mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 15:45:38 +00:00
Squashed 'external/lunasvg/' content from commit 7417baa0a
git-subtree-dir: external/lunasvg git-subtree-split: 7417baa0aff477f361e44e2aa793fdb0c7aae352
This commit is contained in:
commit
32546b5874
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
patreon: sammycage
|
||||
custom: ['https://www.paypal.me/sammycage']
|
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: Build
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: gcc-10
|
||||
CXX: g++-10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: cmake . -DLUNASVG_BUILD_EXAMPLES=ON
|
||||
- run: make -j 2
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: cmake . -DLUNASVG_BUILD_EXAMPLES=ON
|
||||
- run: cmake --build .
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: cmake . -DLUNASVG_BUILD_EXAMPLES=ON
|
||||
- run: cmake --build .
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: svg2png-windows
|
||||
path: example\*\svg2png.exe
|
14
3rdparty/plutovg/CMakeLists.txt
vendored
Normal file
14
3rdparty/plutovg/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
target_sources(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg-paint.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg-geometry.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg-blend.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg-rle.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/plutovg-dash.c"
|
||||
)
|
||||
|
||||
target_include_directories(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
826
3rdparty/plutovg/plutovg-blend.c
vendored
Normal file
826
3rdparty/plutovg/plutovg-blend.c
vendored
Normal file
|
@ -0,0 +1,826 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#define COLOR_TABLE_SIZE 1024
|
||||
typedef struct {
|
||||
plutovg_spread_method_t spread;
|
||||
plutovg_matrix_t matrix;
|
||||
uint32_t colortable[COLOR_TABLE_SIZE];
|
||||
union {
|
||||
struct {
|
||||
double x1, y1;
|
||||
double x2, y2;
|
||||
} linear;
|
||||
struct {
|
||||
double cx, cy, cr;
|
||||
double fx, fy, fr;
|
||||
} radial;
|
||||
};
|
||||
} gradient_data_t;
|
||||
|
||||
typedef struct {
|
||||
plutovg_matrix_t matrix;
|
||||
uint8_t* data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
int const_alpha;
|
||||
} texture_data_t;
|
||||
|
||||
typedef struct {
|
||||
double dx;
|
||||
double dy;
|
||||
double l;
|
||||
double off;
|
||||
} linear_gradient_values_t;
|
||||
|
||||
typedef struct {
|
||||
double dx;
|
||||
double dy;
|
||||
double dr;
|
||||
double sqrfr;
|
||||
double a;
|
||||
double inv2a;
|
||||
int extended;
|
||||
} radial_gradient_values_t;
|
||||
|
||||
static inline uint32_t premultiply_color(const plutovg_color_t* color, double opacity)
|
||||
{
|
||||
uint32_t alpha = (uint8_t)(color->a * opacity * 255);
|
||||
uint32_t pr = (uint8_t)(color->r * alpha);
|
||||
uint32_t pg = (uint8_t)(color->g * alpha);
|
||||
uint32_t pb = (uint8_t)(color->b * alpha);
|
||||
|
||||
return (alpha << 24) | (pr << 16) | (pg << 8) | (pb);
|
||||
}
|
||||
|
||||
static inline uint32_t combine_opacity(const plutovg_color_t* color, double opacity)
|
||||
{
|
||||
uint32_t a = (uint8_t)(color->a * opacity * 255);
|
||||
uint32_t r = (uint8_t)(color->r * 255);
|
||||
uint32_t g = (uint8_t)(color->g * 255);
|
||||
uint32_t b = (uint8_t)(color->b * 255);
|
||||
|
||||
return (a << 24) | (r << 16) | (g << 8) | (b);
|
||||
}
|
||||
|
||||
static inline uint32_t premultiply_pixel(uint32_t color)
|
||||
{
|
||||
uint32_t a = plutovg_alpha(color);
|
||||
uint32_t r = plutovg_red(color);
|
||||
uint32_t g = plutovg_green(color);
|
||||
uint32_t b = plutovg_blue(color);
|
||||
|
||||
uint32_t pr = (r * a) / 255;
|
||||
uint32_t pg = (g * a) / 255;
|
||||
uint32_t pb = (b * a) / 255;
|
||||
return (a << 24) | (pr << 16) | (pg << 8) | (pb);
|
||||
}
|
||||
|
||||
static inline uint32_t interpolate_pixel(uint32_t x, uint32_t a, uint32_t y, uint32_t b)
|
||||
{
|
||||
uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
|
||||
t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
|
||||
t &= 0xff00ff;
|
||||
x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
|
||||
x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
|
||||
x &= 0xff00ff00;
|
||||
x |= t;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint32_t BYTE_MUL(uint32_t x, uint32_t a)
|
||||
{
|
||||
uint32_t t = (x & 0xff00ff) * a;
|
||||
t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
|
||||
t &= 0xff00ff;
|
||||
x = ((x >> 8) & 0xff00ff) * a;
|
||||
x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
|
||||
x &= 0xff00ff00;
|
||||
x |= t;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline void memfill32(uint32_t* dest, uint32_t value, int length)
|
||||
{
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = value;
|
||||
}
|
||||
|
||||
static inline int gradient_clamp(const gradient_data_t* gradient, int ipos)
|
||||
{
|
||||
if(gradient->spread == plutovg_spread_method_repeat)
|
||||
{
|
||||
ipos = ipos % COLOR_TABLE_SIZE;
|
||||
ipos = ipos < 0 ? COLOR_TABLE_SIZE + ipos : ipos;
|
||||
}
|
||||
else if(gradient->spread == plutovg_spread_method_reflect)
|
||||
{
|
||||
const int limit = COLOR_TABLE_SIZE * 2;
|
||||
ipos = ipos % limit;
|
||||
ipos = ipos < 0 ? limit + ipos : ipos;
|
||||
ipos = ipos >= COLOR_TABLE_SIZE ? limit - 1 - ipos : ipos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ipos < 0)
|
||||
ipos = 0;
|
||||
else if(ipos >= COLOR_TABLE_SIZE)
|
||||
ipos = COLOR_TABLE_SIZE - 1;
|
||||
}
|
||||
|
||||
return ipos;
|
||||
}
|
||||
|
||||
#define FIXPT_BITS 8
|
||||
#define FIXPT_SIZE (1 << FIXPT_BITS)
|
||||
static inline uint32_t gradient_pixel_fixed(const gradient_data_t* gradient, int fixed_pos)
|
||||
{
|
||||
int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
|
||||
return gradient->colortable[gradient_clamp(gradient, ipos)];
|
||||
}
|
||||
|
||||
static inline uint32_t gradient_pixel(const gradient_data_t* gradient, double pos)
|
||||
{
|
||||
int ipos = (int)(pos * (COLOR_TABLE_SIZE - 1) + 0.5);
|
||||
return gradient->colortable[gradient_clamp(gradient, ipos)];
|
||||
}
|
||||
|
||||
static void fetch_linear_gradient(uint32_t* buffer, const linear_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length)
|
||||
{
|
||||
double t, inc;
|
||||
double rx = 0, ry = 0;
|
||||
|
||||
if(v->l == 0.0)
|
||||
{
|
||||
t = inc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rx = gradient->matrix.m01 * (y + 0.5) + gradient->matrix.m00 * (x + 0.5) + gradient->matrix.m02;
|
||||
ry = gradient->matrix.m11 * (y + 0.5) + gradient->matrix.m10 * (x + 0.5) + gradient->matrix.m12;
|
||||
t = v->dx * rx + v->dy * ry + v->off;
|
||||
inc = v->dx * gradient->matrix.m00 + v->dy * gradient->matrix.m10;
|
||||
t *= (COLOR_TABLE_SIZE - 1);
|
||||
inc *= (COLOR_TABLE_SIZE - 1);
|
||||
}
|
||||
|
||||
const uint32_t* end = buffer + length;
|
||||
if(inc > -1e-5 && inc < 1e-5)
|
||||
{
|
||||
memfill32(buffer, gradient_pixel_fixed(gradient, (int)(t * FIXPT_SIZE)), length);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(t + inc * length < (double)(INT_MAX >> (FIXPT_BITS + 1)) && t + inc * length > (double)(INT_MIN >> (FIXPT_BITS + 1)))
|
||||
{
|
||||
int t_fixed = (int)(t * FIXPT_SIZE);
|
||||
int inc_fixed = (int)(inc * FIXPT_SIZE);
|
||||
while(buffer < end)
|
||||
{
|
||||
*buffer = gradient_pixel_fixed(gradient, t_fixed);
|
||||
t_fixed += inc_fixed;
|
||||
++buffer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(buffer < end)
|
||||
{
|
||||
*buffer = gradient_pixel(gradient, t / COLOR_TABLE_SIZE);
|
||||
t += inc;
|
||||
++buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fetch_radial_gradient(uint32_t* buffer, const radial_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length)
|
||||
{
|
||||
if(v->a == 0.0)
|
||||
{
|
||||
memfill32(buffer, 0, length);
|
||||
return;
|
||||
}
|
||||
|
||||
double rx = gradient->matrix.m01 * (y + 0.5) + gradient->matrix.m02 + gradient->matrix.m00 * (x + 0.5);
|
||||
double ry = gradient->matrix.m11 * (y + 0.5) + gradient->matrix.m12 + gradient->matrix.m10 * (x + 0.5);
|
||||
|
||||
rx -= gradient->radial.fx;
|
||||
ry -= gradient->radial.fy;
|
||||
|
||||
double inv_a = 1 / (2 * v->a);
|
||||
double delta_rx = gradient->matrix.m00;
|
||||
double delta_ry = gradient->matrix.m10;
|
||||
|
||||
double b = 2 * (v->dr * gradient->radial.fr + rx * v->dx + ry * v->dy);
|
||||
double delta_b = 2 * (delta_rx * v->dx + delta_ry * v->dy);
|
||||
double b_delta_b = 2 * b * delta_b;
|
||||
double delta_b_delta_b = 2 * delta_b * delta_b;
|
||||
|
||||
double bb = b * b;
|
||||
double delta_bb = delta_b * delta_b;
|
||||
|
||||
b *= inv_a;
|
||||
delta_b *= inv_a;
|
||||
|
||||
double rxrxryry = rx * rx + ry * ry;
|
||||
double delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
|
||||
double rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry);
|
||||
double delta_rx_plus_ry = 2 * delta_rxrxryry;
|
||||
|
||||
inv_a *= inv_a;
|
||||
|
||||
double det = (bb - 4 * v->a * (v->sqrfr - rxrxryry)) * inv_a;
|
||||
double delta_det = (b_delta_b + delta_bb + 4 * v->a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
|
||||
double delta_delta_det = (delta_b_delta_b + 4 * v->a * delta_rx_plus_ry) * inv_a;
|
||||
|
||||
const uint32_t* end = buffer + length;
|
||||
if(v->extended)
|
||||
{
|
||||
while(buffer < end)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if(det >= 0)
|
||||
{
|
||||
double w = sqrt(det) - b;
|
||||
if(gradient->radial.fr + v->dr * w >= 0)
|
||||
result = gradient_pixel(gradient, w);
|
||||
}
|
||||
|
||||
*buffer = result;
|
||||
det += delta_det;
|
||||
delta_det += delta_delta_det;
|
||||
b += delta_b;
|
||||
++buffer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(buffer < end)
|
||||
{
|
||||
*buffer++ = gradient_pixel(gradient, sqrt(det) - b);
|
||||
det += delta_det;
|
||||
delta_det += delta_delta_det;
|
||||
b += delta_b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void composition_solid_source(uint32_t* dest, int length, uint32_t color, uint32_t alpha)
|
||||
{
|
||||
if(alpha == 255)
|
||||
{
|
||||
memfill32(dest, color, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ialpha = 255 - alpha;
|
||||
color = BYTE_MUL(color, alpha);
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = color + BYTE_MUL(dest[i], ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
static void composition_solid_source_over(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
|
||||
{
|
||||
if(const_alpha != 255) color = BYTE_MUL(color, const_alpha);
|
||||
uint32_t ialpha = 255 - plutovg_alpha(color);
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = color + BYTE_MUL(dest[i], ialpha);
|
||||
}
|
||||
|
||||
static void composition_solid_destination_in(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
|
||||
{
|
||||
uint32_t a = plutovg_alpha(color);
|
||||
if(const_alpha != 255) a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = BYTE_MUL(dest[i], a);
|
||||
}
|
||||
|
||||
static void composition_solid_destination_out(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha)
|
||||
{
|
||||
uint32_t a = plutovg_alpha(~color);
|
||||
if(const_alpha != 255) a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
|
||||
for(int i = 0; i < length;i++)
|
||||
dest[i] = BYTE_MUL(dest[i], a);
|
||||
}
|
||||
|
||||
static void composition_source(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
|
||||
{
|
||||
if(const_alpha == 255)
|
||||
{
|
||||
memcpy(dest, src, (size_t)(length) * sizeof(uint32_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ialpha = 255 - const_alpha;
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = interpolate_pixel(src[i], const_alpha, dest[i], ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
static void composition_source_over(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
|
||||
{
|
||||
uint32_t s, sia;
|
||||
if(const_alpha == 255)
|
||||
{
|
||||
for(int i = 0;i < length;i++)
|
||||
{
|
||||
s = src[i];
|
||||
if(s >= 0xff000000)
|
||||
dest[i] = s;
|
||||
else if(s != 0)
|
||||
{
|
||||
sia = plutovg_alpha(~s);
|
||||
dest[i] = s + BYTE_MUL(dest[i], sia);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0;i < length;i++)
|
||||
{
|
||||
s = BYTE_MUL(src[i], const_alpha);
|
||||
sia = plutovg_alpha(~s);
|
||||
dest[i] = s + BYTE_MUL(dest[i], sia);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void composition_destination_in(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
|
||||
{
|
||||
if(const_alpha == 255)
|
||||
{
|
||||
for(int i = 0; i < length;i++)
|
||||
dest[i] = BYTE_MUL(dest[i], plutovg_alpha(src[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t cia = 255 - const_alpha;
|
||||
uint32_t a;
|
||||
for(int i = 0;i < length;i++)
|
||||
{
|
||||
a = BYTE_MUL(plutovg_alpha(src[i]), const_alpha) + cia;
|
||||
dest[i] = BYTE_MUL(dest[i], a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void composition_destination_out(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha)
|
||||
{
|
||||
if(const_alpha == 255)
|
||||
{
|
||||
for(int i = 0;i < length;i++)
|
||||
dest[i] = BYTE_MUL(dest[i], plutovg_alpha(~src[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t cia = 255 - const_alpha;
|
||||
uint32_t sia;
|
||||
for(int i = 0;i < length;i++)
|
||||
{
|
||||
sia = BYTE_MUL(plutovg_alpha(~src[i]), const_alpha) + cia;
|
||||
dest[i] = BYTE_MUL(dest[i], sia);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef void(*composition_solid_function_t)(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha);
|
||||
typedef void(*composition_function_t)(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha);
|
||||
|
||||
static const composition_solid_function_t composition_solid_map[] = {
|
||||
composition_solid_source,
|
||||
composition_solid_source_over,
|
||||
composition_solid_destination_in,
|
||||
composition_solid_destination_out
|
||||
};
|
||||
|
||||
static const composition_function_t composition_map[] = {
|
||||
composition_source,
|
||||
composition_source_over,
|
||||
composition_destination_in,
|
||||
composition_destination_out
|
||||
};
|
||||
|
||||
static void blend_solid(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, uint32_t solid)
|
||||
{
|
||||
composition_solid_function_t func = composition_solid_map[op];
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
|
||||
func(target, spans->len, solid, spans->coverage);
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define BUFFER_SIZE 1024
|
||||
static void blend_linear_gradient(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const gradient_data_t* gradient)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
unsigned int buffer[BUFFER_SIZE];
|
||||
|
||||
linear_gradient_values_t v;
|
||||
v.dx = gradient->linear.x2 - gradient->linear.x1;
|
||||
v.dy = gradient->linear.y2 - gradient->linear.y1;
|
||||
v.l = v.dx * v.dx + v.dy * v.dy;
|
||||
v.off = 0.0;
|
||||
if(v.l != 0.0)
|
||||
{
|
||||
v.dx /= v.l;
|
||||
v.dy /= v.l;
|
||||
v.off = -v.dx * gradient->linear.x1 - v.dy * gradient->linear.y1;
|
||||
}
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
int length = spans->len;
|
||||
int x = spans->x;
|
||||
while(length)
|
||||
{
|
||||
int l = MIN(length, BUFFER_SIZE);
|
||||
fetch_linear_gradient(buffer, &v, gradient, spans->y, x, l);
|
||||
uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
|
||||
func(target, l, buffer, spans->coverage);
|
||||
x += l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
static void blend_radial_gradient(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const gradient_data_t* gradient)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
unsigned int buffer[BUFFER_SIZE];
|
||||
|
||||
radial_gradient_values_t v;
|
||||
v.dx = gradient->radial.cx - gradient->radial.fx;
|
||||
v.dy = gradient->radial.cy - gradient->radial.fy;
|
||||
v.dr = gradient->radial.cr - gradient->radial.fr;
|
||||
v.sqrfr = gradient->radial.fr * gradient->radial.fr;
|
||||
v.a = v.dr * v.dr - v.dx * v.dx - v.dy * v.dy;
|
||||
v.inv2a = 1.0 / (2.0 * v.a);
|
||||
v.extended = gradient->radial.fr != 0.0 || v.a <= 0.0;
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
int length = spans->len;
|
||||
int x = spans->x;
|
||||
while(length)
|
||||
{
|
||||
int l = MIN(length, BUFFER_SIZE);
|
||||
fetch_radial_gradient(buffer, &v, gradient, spans->y, x, l);
|
||||
uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
|
||||
func(target, l, buffer, spans->coverage);
|
||||
x += l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
#define CLAMP(v, lo, hi) ((v) < (lo) ? (lo) : (hi) < (v) ? (hi) : (v))
|
||||
#define FIXED_SCALE (1 << 16)
|
||||
static void blend_transformed_argb(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const texture_data_t* texture)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
uint32_t buffer[BUFFER_SIZE];
|
||||
|
||||
int image_width = texture->width;
|
||||
int image_height = texture->height;
|
||||
|
||||
int fdx = (int)(texture->matrix.m00 * FIXED_SCALE);
|
||||
int fdy = (int)(texture->matrix.m10 * FIXED_SCALE);
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
|
||||
|
||||
const double cx = spans->x + 0.5;
|
||||
const double cy = spans->y + 0.5;
|
||||
|
||||
int x = (int)((texture->matrix.m01 * cy + texture->matrix.m00 * cx + texture->matrix.m02) * FIXED_SCALE);
|
||||
int y = (int)((texture->matrix.m11 * cy + texture->matrix.m10 * cx + texture->matrix.m12) * FIXED_SCALE);
|
||||
|
||||
int length = spans->len;
|
||||
const int coverage = (spans->coverage * texture->const_alpha) >> 8;
|
||||
while(length)
|
||||
{
|
||||
int l = MIN(length, BUFFER_SIZE);
|
||||
const uint32_t* end = buffer + l;
|
||||
uint32_t* b = buffer;
|
||||
while(b < end)
|
||||
{
|
||||
int px = CLAMP(x >> 16, 0, image_width - 1);
|
||||
int py = CLAMP(y >> 16, 0, image_height - 1);
|
||||
*b = ((const uint32_t*)(texture->data + py * texture->stride))[px];
|
||||
|
||||
x += fdx;
|
||||
y += fdy;
|
||||
++b;
|
||||
}
|
||||
|
||||
func(target, l, buffer, coverage);
|
||||
target += l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
static void blend_untransformed_argb(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const texture_data_t* texture)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
|
||||
const int image_width = texture->width;
|
||||
const int image_height = texture->height;
|
||||
|
||||
int xoff = (int)(texture->matrix.m02);
|
||||
int yoff = (int)(texture->matrix.m12);
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
int x = spans->x;
|
||||
int length = spans->len;
|
||||
int sx = xoff + x;
|
||||
int sy = yoff + spans->y;
|
||||
if(sy >= 0 && sy < image_height && sx < image_width)
|
||||
{
|
||||
if(sx < 0)
|
||||
{
|
||||
x -= sx;
|
||||
length += sx;
|
||||
sx = 0;
|
||||
}
|
||||
if(sx + length > image_width) length = image_width - sx;
|
||||
if(length > 0)
|
||||
{
|
||||
const int coverage = (spans->coverage * texture->const_alpha) >> 8;
|
||||
const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx;
|
||||
uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
|
||||
func(dest, length, src, coverage);
|
||||
}
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
static void blend_untransformed_tiled_argb(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const texture_data_t* texture)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
|
||||
int image_width = texture->width;
|
||||
int image_height = texture->height;
|
||||
|
||||
int xoff = (int)(texture->matrix.m02) % image_width;
|
||||
int yoff = (int)(texture->matrix.m12) % image_height;
|
||||
|
||||
if(xoff < 0)
|
||||
xoff += image_width;
|
||||
if(yoff < 0)
|
||||
yoff += image_height;
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
int x = spans->x;
|
||||
int length = spans->len;
|
||||
int sx = (xoff + spans->x) % image_width;
|
||||
int sy = (spans->y + yoff) % image_height;
|
||||
if(sx < 0)
|
||||
sx += image_width;
|
||||
if(sy < 0)
|
||||
sy += image_height;
|
||||
|
||||
const int coverage = (spans->coverage * texture->const_alpha) >> 8;
|
||||
while(length)
|
||||
{
|
||||
int l = MIN(image_width - sx, length);
|
||||
if(BUFFER_SIZE < l)
|
||||
l = BUFFER_SIZE;
|
||||
const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx;
|
||||
uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x;
|
||||
func(dest, l, src, coverage);
|
||||
x += l;
|
||||
length -= l;
|
||||
sx = 0;
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
static void blend_transformed_tiled_argb(plutovg_surface_t* surface, plutovg_operator_t op, const plutovg_rle_t* rle, const texture_data_t* texture)
|
||||
{
|
||||
composition_function_t func = composition_map[op];
|
||||
uint32_t buffer[BUFFER_SIZE];
|
||||
|
||||
int image_width = texture->width;
|
||||
int image_height = texture->height;
|
||||
const int scanline_offset = texture->stride / 4;
|
||||
|
||||
int fdx = (int)(texture->matrix.m00 * FIXED_SCALE);
|
||||
int fdy = (int)(texture->matrix.m10 * FIXED_SCALE);
|
||||
|
||||
int count = rle->spans.size;
|
||||
const plutovg_span_t* spans = rle->spans.data;
|
||||
while(count--)
|
||||
{
|
||||
uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x;
|
||||
const uint32_t* image_bits = (const uint32_t*)texture->data;
|
||||
|
||||
const double cx = spans->x + 0.5;
|
||||
const double cy = spans->y + 0.5;
|
||||
|
||||
int x = (int)((texture->matrix.m01 * cy + texture->matrix.m00 * cx + texture->matrix.m02) * FIXED_SCALE);
|
||||
int y = (int)((texture->matrix.m11 * cy + texture->matrix.m10 * cx + texture->matrix.m12) * FIXED_SCALE);
|
||||
|
||||
const int coverage = (spans->coverage * texture->const_alpha) >> 8;
|
||||
int length = spans->len;
|
||||
while(length)
|
||||
{
|
||||
int l = MIN(length, BUFFER_SIZE);
|
||||
const uint32_t* end = buffer + l;
|
||||
uint32_t* b = buffer;
|
||||
int px16 = x % (image_width << 16);
|
||||
int py16 = y % (image_height << 16);
|
||||
int px_delta = fdx % (image_width << 16);
|
||||
int py_delta = fdy % (image_height << 16);
|
||||
while(b < end)
|
||||
{
|
||||
if(px16 < 0) px16 += image_width << 16;
|
||||
if(py16 < 0) py16 += image_height << 16;
|
||||
int px = px16 >> 16;
|
||||
int py = py16 >> 16;
|
||||
int y_offset = py * scanline_offset;
|
||||
|
||||
*b = image_bits[y_offset + px];
|
||||
x += fdx;
|
||||
y += fdy;
|
||||
px16 += px_delta;
|
||||
if(px16 >= image_width << 16)
|
||||
px16 -= image_width << 16;
|
||||
py16 += py_delta;
|
||||
if(py16 >= image_height << 16)
|
||||
py16 -= image_height << 16;
|
||||
++b;
|
||||
}
|
||||
|
||||
func(target, l, buffer, coverage);
|
||||
target += l;
|
||||
length -= l;
|
||||
}
|
||||
|
||||
++spans;
|
||||
}
|
||||
}
|
||||
|
||||
void plutovg_blend(plutovg_t* pluto, const plutovg_rle_t* rle)
|
||||
{
|
||||
plutovg_paint_t* source = pluto->state->source;
|
||||
if(source->type == plutovg_paint_type_color)
|
||||
plutovg_blend_color(pluto, rle, source->color);
|
||||
else if(source->type == plutovg_paint_type_gradient)
|
||||
plutovg_blend_gradient(pluto, rle, source->gradient);
|
||||
else
|
||||
plutovg_blend_texture(pluto, rle, source->texture);
|
||||
}
|
||||
|
||||
void plutovg_blend_color(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_color_t* color)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
uint32_t solid = premultiply_color(color, state->opacity);
|
||||
|
||||
uint32_t alpha = plutovg_alpha(solid);
|
||||
if(alpha == 255 && state->op == plutovg_operator_src_over)
|
||||
blend_solid(pluto->surface, plutovg_operator_src, rle, solid);
|
||||
else
|
||||
blend_solid(pluto->surface, state->op, rle, solid);
|
||||
}
|
||||
|
||||
void plutovg_blend_gradient(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_gradient_t* gradient)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
gradient_data_t data;
|
||||
int i, pos = 0, nstop = gradient->stops.size;
|
||||
const plutovg_gradient_stop_t *curr, *next, *start, *last;
|
||||
uint32_t curr_color, next_color, last_color;
|
||||
uint32_t dist, idist;
|
||||
double delta, t, incr, fpos;
|
||||
double opacity = state->opacity * gradient->opacity;
|
||||
|
||||
start = gradient->stops.data;
|
||||
curr = start;
|
||||
curr_color = combine_opacity(&curr->color, opacity);
|
||||
|
||||
data.colortable[pos] = premultiply_pixel(curr_color);
|
||||
++pos;
|
||||
incr = 1.0 / COLOR_TABLE_SIZE;
|
||||
fpos = 1.5 * incr;
|
||||
|
||||
while(fpos <= curr->offset)
|
||||
{
|
||||
data.colortable[pos] = data.colortable[pos - 1];
|
||||
++pos;
|
||||
fpos += incr;
|
||||
}
|
||||
|
||||
for(i = 0;i < nstop - 1;i++)
|
||||
{
|
||||
curr = (start + i);
|
||||
next = (start + i + 1);
|
||||
delta = 1.0 / (next->offset - curr->offset);
|
||||
next_color = combine_opacity(&next->color, opacity);
|
||||
while(fpos < next->offset && pos < COLOR_TABLE_SIZE)
|
||||
{
|
||||
t = (fpos - curr->offset) * delta;
|
||||
dist = (uint32_t)(255 * t);
|
||||
idist = 255 - dist;
|
||||
data.colortable[pos] = premultiply_pixel(interpolate_pixel(curr_color, idist, next_color, dist));
|
||||
++pos;
|
||||
fpos += incr;
|
||||
}
|
||||
|
||||
curr_color = next_color;
|
||||
}
|
||||
|
||||
last = start + nstop - 1;
|
||||
last_color = premultiply_color(&last->color, opacity);
|
||||
for(;pos < COLOR_TABLE_SIZE;++pos)
|
||||
data.colortable[pos] = last_color;
|
||||
|
||||
data.spread = gradient->spread;
|
||||
data.matrix = gradient->matrix;
|
||||
plutovg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
|
||||
plutovg_matrix_invert(&data.matrix);
|
||||
|
||||
if(gradient->type==plutovg_gradient_type_linear)
|
||||
{
|
||||
data.linear.x1 = gradient->values[0];
|
||||
data.linear.y1 = gradient->values[1];
|
||||
data.linear.x2 = gradient->values[2];
|
||||
data.linear.y2 = gradient->values[3];
|
||||
blend_linear_gradient(pluto->surface, state->op, rle, &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.radial.cx = gradient->values[0];
|
||||
data.radial.cy = gradient->values[1];
|
||||
data.radial.cr = gradient->values[2];
|
||||
data.radial.fx = gradient->values[3];
|
||||
data.radial.fy = gradient->values[4];
|
||||
data.radial.fr = gradient->values[5];
|
||||
blend_radial_gradient(pluto->surface, state->op, rle, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void plutovg_blend_texture(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_texture_t* texture)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
texture_data_t data;
|
||||
data.data = texture->surface->data;
|
||||
data.width = texture->surface->width;
|
||||
data.height = texture->surface->height;
|
||||
data.stride = texture->surface->stride;
|
||||
data.const_alpha = (int)(state->opacity * texture->opacity * 256.0);
|
||||
|
||||
data.matrix = texture->matrix;
|
||||
plutovg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix);
|
||||
plutovg_matrix_invert(&data.matrix);
|
||||
|
||||
const plutovg_matrix_t* matrix = &data.matrix;
|
||||
int translating = (matrix->m00==1.0 && matrix->m10==0.0 && matrix->m01==0.0 && matrix->m11==1.0);
|
||||
if(translating)
|
||||
{
|
||||
if(texture->type==plutovg_texture_type_plain)
|
||||
blend_untransformed_argb(pluto->surface, state->op, rle, &data);
|
||||
else
|
||||
blend_untransformed_tiled_argb(pluto->surface, state->op, rle, &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(texture->type==plutovg_texture_type_plain)
|
||||
blend_transformed_argb(pluto->surface, state->op, rle, &data);
|
||||
else
|
||||
blend_transformed_tiled_argb(pluto->surface, state->op, rle, &data);
|
||||
}
|
||||
}
|
114
3rdparty/plutovg/plutovg-dash.c
vendored
Normal file
114
3rdparty/plutovg/plutovg-dash.c
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
plutovg_dash_t* plutovg_dash_create(double offset, const double* data, int size)
|
||||
{
|
||||
if(data==NULL || size==0)
|
||||
return NULL;
|
||||
|
||||
plutovg_dash_t* dash = malloc(sizeof(plutovg_dash_t));
|
||||
dash->offset = offset;
|
||||
dash->data = malloc((size_t)size * sizeof(double));
|
||||
dash->size = size;
|
||||
memcpy(dash->data, data, (size_t)size * sizeof(double));
|
||||
return dash;
|
||||
}
|
||||
|
||||
plutovg_dash_t* plutovg_dash_clone(const plutovg_dash_t* dash)
|
||||
{
|
||||
if(dash==NULL)
|
||||
return NULL;
|
||||
|
||||
return plutovg_dash_create(dash->offset, dash->data, dash->size);
|
||||
}
|
||||
|
||||
void plutovg_dash_destroy(plutovg_dash_t* dash)
|
||||
{
|
||||
if(dash==NULL)
|
||||
return;
|
||||
|
||||
free(dash->data);
|
||||
free(dash);
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_dash_path(const plutovg_dash_t* dash, const plutovg_path_t* path)
|
||||
{
|
||||
if(dash->data==NULL || dash->size==0)
|
||||
return plutovg_path_clone(path);
|
||||
|
||||
int toggle = 1;
|
||||
int offset = 0;
|
||||
double phase = dash->offset;
|
||||
while(phase >= dash->data[offset])
|
||||
{
|
||||
toggle = !toggle;
|
||||
phase -= dash->data[offset];
|
||||
offset += 1;
|
||||
if(offset == dash->size) offset = 0;
|
||||
}
|
||||
|
||||
plutovg_path_t* flat = plutovg_path_clone_flat(path);
|
||||
plutovg_path_t* result = plutovg_path_create();
|
||||
plutovg_array_ensure(result->elements, flat->elements.size);
|
||||
plutovg_array_ensure(result->points, flat->points.size);
|
||||
|
||||
plutovg_path_element_t* elements = flat->elements.data;
|
||||
plutovg_path_element_t* end = elements + flat->elements.size;
|
||||
plutovg_point_t* points = flat->points.data;
|
||||
while(elements < end)
|
||||
{
|
||||
int itoggle = toggle;
|
||||
int ioffset = offset;
|
||||
double iphase = phase;
|
||||
|
||||
double x0 = points->x;
|
||||
double y0 = points->y;
|
||||
|
||||
if(itoggle)
|
||||
plutovg_path_move_to(result, x0, y0);
|
||||
|
||||
++elements;
|
||||
++points;
|
||||
|
||||
while(elements < end && *elements==plutovg_path_element_line_to)
|
||||
{
|
||||
double dx = points->x - x0;
|
||||
double dy = points->y - y0;
|
||||
double dist0 = sqrt(dx*dx + dy*dy);
|
||||
double dist1 = 0;
|
||||
|
||||
while(dist0 - dist1 > dash->data[ioffset] - iphase)
|
||||
{
|
||||
dist1 += dash->data[ioffset] - iphase;
|
||||
double a = dist1 / dist0;
|
||||
double x = x0 + a * dx;
|
||||
double y = y0 + a * dy;
|
||||
|
||||
if(itoggle)
|
||||
plutovg_path_line_to(result, x, y);
|
||||
else
|
||||
plutovg_path_move_to(result, x, y);
|
||||
|
||||
itoggle = !itoggle;
|
||||
iphase = 0;
|
||||
ioffset += 1;
|
||||
if(ioffset == dash->size) ioffset = 0;
|
||||
}
|
||||
|
||||
iphase += dist0 - dist1;
|
||||
|
||||
x0 = points->x;
|
||||
y0 = points->y;
|
||||
|
||||
if(itoggle)
|
||||
plutovg_path_line_to(result, x0, y0);
|
||||
|
||||
++elements;
|
||||
++points;
|
||||
}
|
||||
}
|
||||
|
||||
plutovg_path_destroy(flat);
|
||||
return result;
|
||||
}
|
582
3rdparty/plutovg/plutovg-geometry.c
vendored
Normal file
582
3rdparty/plutovg/plutovg-geometry.c
vendored
Normal file
|
@ -0,0 +1,582 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
void plutovg_rect_init(plutovg_rect_t* rect, double x, double y, double w, double h)
|
||||
{
|
||||
rect->x = x;
|
||||
rect->y = y;
|
||||
rect->w = w;
|
||||
rect->h = h;
|
||||
}
|
||||
|
||||
void plutovg_rect_init_zero(plutovg_rect_t* rect)
|
||||
{
|
||||
rect->x = 0.0;
|
||||
rect->y = 0.0;
|
||||
rect->w = 0.0;
|
||||
rect->h = 0.0;
|
||||
}
|
||||
|
||||
void plutovg_matrix_init(plutovg_matrix_t* matrix, double m00, double m10, double m01, double m11, double m02, double m12)
|
||||
{
|
||||
matrix->m00 = m00; matrix->m10 = m10;
|
||||
matrix->m01 = m01; matrix->m11 = m11;
|
||||
matrix->m02 = m02; matrix->m12 = m12;
|
||||
}
|
||||
|
||||
void plutovg_matrix_init_identity(plutovg_matrix_t* matrix)
|
||||
{
|
||||
matrix->m00 = 1.0; matrix->m10 = 0.0;
|
||||
matrix->m01 = 0.0; matrix->m11 = 1.0;
|
||||
matrix->m02 = 0.0; matrix->m12 = 0.0;
|
||||
}
|
||||
|
||||
void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_init(matrix, 1.0, 0.0, 0.0, 1.0, x, y);
|
||||
}
|
||||
|
||||
void plutovg_matrix_init_scale(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_init(matrix, x, 0.0, 0.0, y, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void plutovg_matrix_init_shear(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_init(matrix, 1.0, tan(y), tan(x), 1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void plutovg_matrix_init_rotate(plutovg_matrix_t* matrix, double radians, double x, double y)
|
||||
{
|
||||
double c = cos(radians);
|
||||
double s = sin(radians);
|
||||
|
||||
double cx = x * (1 - c) + y * s;
|
||||
double cy = y * (1 - c) - x * s;
|
||||
|
||||
plutovg_matrix_init(matrix, c, s, -s, c, cx, cy);
|
||||
}
|
||||
|
||||
void plutovg_matrix_translate(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_t m;
|
||||
plutovg_matrix_init_translate(&m, x, y);
|
||||
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||
}
|
||||
|
||||
void plutovg_matrix_scale(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_t m;
|
||||
plutovg_matrix_init_scale(&m, x, y);
|
||||
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||
}
|
||||
|
||||
void plutovg_matrix_shear(plutovg_matrix_t* matrix, double x, double y)
|
||||
{
|
||||
plutovg_matrix_t m;
|
||||
plutovg_matrix_init_shear(&m, x, y);
|
||||
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||
}
|
||||
|
||||
void plutovg_matrix_rotate(plutovg_matrix_t* matrix, double radians, double x, double y)
|
||||
{
|
||||
plutovg_matrix_t m;
|
||||
plutovg_matrix_init_rotate(&m, radians, x, y);
|
||||
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||
}
|
||||
|
||||
void plutovg_matrix_multiply(plutovg_matrix_t* matrix, const plutovg_matrix_t* a, const plutovg_matrix_t* b)
|
||||
{
|
||||
double m00 = a->m00 * b->m00 + a->m10 * b->m01;
|
||||
double m10 = a->m00 * b->m10 + a->m10 * b->m11;
|
||||
double m01 = a->m01 * b->m00 + a->m11 * b->m01;
|
||||
double m11 = a->m01 * b->m10 + a->m11 * b->m11;
|
||||
double m02 = a->m02 * b->m00 + a->m12 * b->m01 + b->m02;
|
||||
double m12 = a->m02 * b->m10 + a->m12 * b->m11 + b->m12;
|
||||
|
||||
plutovg_matrix_init(matrix, m00, m10, m01, m11, m02, m12);
|
||||
}
|
||||
|
||||
int plutovg_matrix_invert(plutovg_matrix_t* matrix)
|
||||
{
|
||||
double det = (matrix->m00 * matrix->m11 - matrix->m10 * matrix->m01);
|
||||
if(det == 0.0)
|
||||
return 0;
|
||||
|
||||
double inv_det = 1.0 / det;
|
||||
double m00 = matrix->m00 * inv_det;
|
||||
double m10 = matrix->m10 * inv_det;
|
||||
double m01 = matrix->m01 * inv_det;
|
||||
double m11 = matrix->m11 * inv_det;
|
||||
double m02 = (matrix->m01 * matrix->m12 - matrix->m11 * matrix->m02) * inv_det;
|
||||
double m12 = (matrix->m10 * matrix->m02 - matrix->m00 * matrix->m12) * inv_det;
|
||||
|
||||
plutovg_matrix_init(matrix, m11, -m10, -m01, m00, m02, m12);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void plutovg_matrix_map(const plutovg_matrix_t* matrix, double x, double y, double* _x, double* _y)
|
||||
{
|
||||
*_x = x * matrix->m00 + y * matrix->m01 + matrix->m02;
|
||||
*_y = x * matrix->m10 + y * matrix->m11 + matrix->m12;
|
||||
}
|
||||
|
||||
void plutovg_matrix_map_point(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst)
|
||||
{
|
||||
plutovg_matrix_map(matrix, src->x, src->y, &dst->x, &dst->y);
|
||||
}
|
||||
|
||||
void plutovg_matrix_map_rect(const plutovg_matrix_t* matrix, const plutovg_rect_t* src, plutovg_rect_t* dst)
|
||||
{
|
||||
plutovg_point_t p[4];
|
||||
p[0].x = src->x;
|
||||
p[0].y = src->y;
|
||||
p[1].x = src->x + src->w;
|
||||
p[1].y = src->y;
|
||||
p[2].x = src->x + src->w;
|
||||
p[2].y = src->y + src->h;
|
||||
p[3].x = src->x;
|
||||
p[3].y = src->y + src->h;
|
||||
|
||||
plutovg_matrix_map_point(matrix, &p[0], &p[0]);
|
||||
plutovg_matrix_map_point(matrix, &p[1], &p[1]);
|
||||
plutovg_matrix_map_point(matrix, &p[2], &p[2]);
|
||||
plutovg_matrix_map_point(matrix, &p[3], &p[3]);
|
||||
|
||||
double l = p[0].x;
|
||||
double t = p[0].y;
|
||||
double r = p[0].x;
|
||||
double b = p[0].y;
|
||||
|
||||
for(int i = 0;i < 4;i++)
|
||||
{
|
||||
if(p[i].x < l) l = p[i].x;
|
||||
if(p[i].x > r) r = p[i].x;
|
||||
if(p[i].y < t) t = p[i].y;
|
||||
if(p[i].y > b) b = p[i].y;
|
||||
}
|
||||
|
||||
dst->x = l;
|
||||
dst->y = t;
|
||||
dst->w = r - l;
|
||||
dst->h = b - t;
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_path_create(void)
|
||||
{
|
||||
plutovg_path_t* path = malloc(sizeof(plutovg_path_t));
|
||||
path->ref = 1;
|
||||
path->contours = 0;
|
||||
path->start.x = 0.0;
|
||||
path->start.y = 0.0;
|
||||
plutovg_array_init(path->elements);
|
||||
plutovg_array_init(path->points);
|
||||
return path;
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_path_reference(plutovg_path_t* path)
|
||||
{
|
||||
++path->ref;
|
||||
return path;
|
||||
}
|
||||
|
||||
void plutovg_path_destroy(plutovg_path_t* path)
|
||||
{
|
||||
if(path==NULL)
|
||||
return;
|
||||
|
||||
if(--path->ref==0)
|
||||
{
|
||||
free(path->elements.data);
|
||||
free(path->points.data);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_path_get_reference_count(const plutovg_path_t* path)
|
||||
{
|
||||
return path->ref;
|
||||
}
|
||||
|
||||
void plutovg_path_move_to(plutovg_path_t* path, double x, double y)
|
||||
{
|
||||
plutovg_array_ensure(path->elements, 1);
|
||||
plutovg_array_ensure(path->points, 1);
|
||||
|
||||
path->elements.data[path->elements.size] = plutovg_path_element_move_to;
|
||||
plutovg_point_t* points = path->points.data + path->points.size;
|
||||
points[0].x = x;
|
||||
points[0].y = y;
|
||||
|
||||
path->elements.size += 1;
|
||||
path->points.size += 1;
|
||||
path->contours += 1;
|
||||
|
||||
path->start.x = x;
|
||||
path->start.y = y;
|
||||
}
|
||||
|
||||
void plutovg_path_line_to(plutovg_path_t* path, double x, double y)
|
||||
{
|
||||
plutovg_array_ensure(path->elements, 1);
|
||||
plutovg_array_ensure(path->points, 1);
|
||||
|
||||
path->elements.data[path->elements.size] = plutovg_path_element_line_to;
|
||||
plutovg_point_t* points = path->points.data + path->points.size;
|
||||
points[0].x = x;
|
||||
points[0].y = y;
|
||||
|
||||
path->elements.size += 1;
|
||||
path->points.size += 1;
|
||||
}
|
||||
|
||||
void plutovg_path_quad_to(plutovg_path_t* path, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
double x, y;
|
||||
plutovg_path_get_current_point(path, &x, &y);
|
||||
|
||||
double cx = 2.0 / 3.0 * x1 + 1.0 / 3.0 * x;
|
||||
double cy = 2.0 / 3.0 * y1 + 1.0 / 3.0 * y;
|
||||
double cx1 = 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2;
|
||||
double cy1 = 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2;
|
||||
plutovg_path_cubic_to(path, cx, cy, cx1, cy1, x2, y2);
|
||||
}
|
||||
|
||||
void plutovg_path_cubic_to(plutovg_path_t* path, double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
plutovg_array_ensure(path->elements, 1);
|
||||
plutovg_array_ensure(path->points, 3);
|
||||
|
||||
path->elements.data[path->elements.size] = plutovg_path_element_cubic_to;
|
||||
plutovg_point_t* points = path->points.data + path->points.size;
|
||||
points[0].x = x1;
|
||||
points[0].y = y1;
|
||||
points[1].x = x2;
|
||||
points[1].y = y2;
|
||||
points[2].x = x3;
|
||||
points[2].y = y3;
|
||||
|
||||
path->elements.size += 1;
|
||||
path->points.size += 3;
|
||||
}
|
||||
|
||||
void plutovg_path_close(plutovg_path_t* path)
|
||||
{
|
||||
if(path->elements.size == 0)
|
||||
return;
|
||||
|
||||
if(path->elements.data[path->elements.size - 1] == plutovg_path_element_close)
|
||||
return;
|
||||
|
||||
plutovg_array_ensure(path->elements, 1);
|
||||
plutovg_array_ensure(path->points, 1);
|
||||
|
||||
path->elements.data[path->elements.size] = plutovg_path_element_close;
|
||||
plutovg_point_t* points = path->points.data + path->points.size;
|
||||
points[0].x = path->start.x;
|
||||
points[0].y = path->start.y;
|
||||
|
||||
path->elements.size += 1;
|
||||
path->points.size += 1;
|
||||
}
|
||||
|
||||
static inline void rel_to_abs(const plutovg_path_t* path, double* x, double* y)
|
||||
{
|
||||
double _x, _y;
|
||||
plutovg_path_get_current_point(path, &_x, &_y);
|
||||
|
||||
*x += _x;
|
||||
*y += _y;
|
||||
}
|
||||
|
||||
void plutovg_path_rel_move_to(plutovg_path_t* path, double x, double y)
|
||||
{
|
||||
rel_to_abs(path, &x, &y);
|
||||
plutovg_path_move_to(path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_path_rel_line_to(plutovg_path_t* path, double x, double y)
|
||||
{
|
||||
rel_to_abs(path, &x, &y);
|
||||
plutovg_path_line_to(path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_path_rel_quad_to(plutovg_path_t* path, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
rel_to_abs(path, &x1, &y1);
|
||||
rel_to_abs(path, &x2, &y2);
|
||||
plutovg_path_quad_to(path, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void plutovg_path_rel_cubic_to(plutovg_path_t* path, double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
rel_to_abs(path, &x1, &y1);
|
||||
rel_to_abs(path, &x2, &y2);
|
||||
rel_to_abs(path, &x3, &y3);
|
||||
plutovg_path_cubic_to(path, x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
|
||||
void plutovg_path_add_rect(plutovg_path_t* path, double x, double y, double w, double h)
|
||||
{
|
||||
plutovg_path_move_to(path, x, y);
|
||||
plutovg_path_line_to(path, x + w, y);
|
||||
plutovg_path_line_to(path, x + w, y + h);
|
||||
plutovg_path_line_to(path, x, y + h);
|
||||
plutovg_path_line_to(path, x, y);
|
||||
plutovg_path_close(path);
|
||||
}
|
||||
|
||||
#define KAPPA 0.5522847498
|
||||
void plutovg_path_add_round_rect(plutovg_path_t* path, double x, double y, double w, double h, double rx, double ry)
|
||||
{
|
||||
double right = x + w;
|
||||
double bottom = y + h;
|
||||
|
||||
double cpx = rx * KAPPA;
|
||||
double cpy = ry * KAPPA;
|
||||
|
||||
plutovg_path_move_to(path, x, y+ry);
|
||||
plutovg_path_cubic_to(path, x, y+ry-cpy, x+rx-cpx, y, x+rx, y);
|
||||
plutovg_path_line_to(path, right-rx, y);
|
||||
plutovg_path_cubic_to(path, right-rx+cpx, y, right, y+ry-cpy, right, y+ry);
|
||||
plutovg_path_line_to(path, right, bottom-ry);
|
||||
plutovg_path_cubic_to(path, right, bottom-ry+cpy, right-rx+cpx, bottom, right-rx, bottom);
|
||||
plutovg_path_line_to(path, x+rx, bottom);
|
||||
plutovg_path_cubic_to(path, x+rx-cpx, bottom, x, bottom-ry+cpy, x, bottom-ry);
|
||||
plutovg_path_line_to(path, x, y+ry);
|
||||
plutovg_path_close(path);
|
||||
}
|
||||
|
||||
void plutovg_path_add_ellipse(plutovg_path_t* path, double cx, double cy, double rx, double ry)
|
||||
{
|
||||
double left = cx - rx;
|
||||
double top = cy - ry;
|
||||
double right = cx + rx;
|
||||
double bottom = cy + ry;
|
||||
|
||||
double cpx = rx * KAPPA;
|
||||
double cpy = ry * KAPPA;
|
||||
|
||||
plutovg_path_move_to(path, cx, top);
|
||||
plutovg_path_cubic_to(path, cx+cpx, top, right, cy-cpy, right, cy);
|
||||
plutovg_path_cubic_to(path, right, cy+cpy, cx+cpx, bottom, cx, bottom);
|
||||
plutovg_path_cubic_to(path, cx-cpx, bottom, left, cy+cpy, left, cy);
|
||||
plutovg_path_cubic_to(path, left, cy-cpy, cx-cpx, top, cx, top);
|
||||
plutovg_path_close(path);
|
||||
}
|
||||
|
||||
void plutovg_path_add_circle(plutovg_path_t* path, double cx, double cy, double r)
|
||||
{
|
||||
plutovg_path_add_ellipse(path, cx, cy, r, r);
|
||||
}
|
||||
|
||||
void plutovg_path_add_path(plutovg_path_t* path, const plutovg_path_t* source, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
plutovg_array_ensure(path->elements, source->elements.size);
|
||||
plutovg_array_ensure(path->points, source->points.size);
|
||||
|
||||
plutovg_point_t* points = path->points.data + path->points.size;
|
||||
const plutovg_point_t* data = source->points.data;
|
||||
const plutovg_point_t* end = data + source->points.size;
|
||||
while(data < end)
|
||||
{
|
||||
if(matrix)
|
||||
plutovg_matrix_map_point(matrix, data, points);
|
||||
else
|
||||
memcpy(points, data, sizeof(plutovg_point_t));
|
||||
|
||||
points += 1;
|
||||
data += 1;
|
||||
}
|
||||
|
||||
plutovg_path_element_t* elements = path->elements.data + path->elements.size;
|
||||
memcpy(elements, source->elements.data, (size_t)source->elements.size * sizeof(plutovg_path_element_t));
|
||||
|
||||
path->elements.size += source->elements.size;
|
||||
path->points.size += source->points.size;
|
||||
path->contours += source->contours;
|
||||
path->start = source->start;
|
||||
}
|
||||
|
||||
void plutovg_path_transform(plutovg_path_t* path, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
plutovg_point_t* points = path->points.data;
|
||||
plutovg_point_t* end = points + path->points.size;
|
||||
while(points < end)
|
||||
{
|
||||
plutovg_matrix_map_point(matrix, points, points);
|
||||
points += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void plutovg_path_get_current_point(const plutovg_path_t* path, double* x, double* y)
|
||||
{
|
||||
*x = 0.0;
|
||||
*y = 0.0;
|
||||
|
||||
if(path->points.size == 0)
|
||||
return;
|
||||
|
||||
*x = path->points.data[path->points.size - 1].x;
|
||||
*y = path->points.data[path->points.size - 1].y;
|
||||
}
|
||||
|
||||
int plutovg_path_get_element_count(const plutovg_path_t* path)
|
||||
{
|
||||
return path->elements.size;
|
||||
}
|
||||
|
||||
plutovg_path_element_t* plutovg_path_get_elements(const plutovg_path_t* path)
|
||||
{
|
||||
return path->elements.data;
|
||||
}
|
||||
|
||||
int plutovg_path_get_point_count(const plutovg_path_t* path)
|
||||
{
|
||||
return path->points.size;
|
||||
}
|
||||
|
||||
plutovg_point_t* plutovg_path_get_points(const plutovg_path_t* path)
|
||||
{
|
||||
return path->points.data;
|
||||
}
|
||||
|
||||
void plutovg_path_clear(plutovg_path_t* path)
|
||||
{
|
||||
path->elements.size = 0;
|
||||
path->points.size = 0;
|
||||
path->contours = 0;
|
||||
path->start.x = 0.0;
|
||||
path->start.y = 0.0;
|
||||
}
|
||||
|
||||
int plutovg_path_empty(const plutovg_path_t* path)
|
||||
{
|
||||
return path->elements.size == 0;
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_path_clone(const plutovg_path_t* path)
|
||||
{
|
||||
plutovg_path_t* result = plutovg_path_create();
|
||||
plutovg_array_ensure(result->elements, path->elements.size);
|
||||
plutovg_array_ensure(result->points, path->points.size);
|
||||
|
||||
memcpy(result->elements.data, path->elements.data, (size_t)path->elements.size * sizeof(plutovg_path_element_t));
|
||||
memcpy(result->points.data, path->points.data, (size_t)path->points.size * sizeof(plutovg_point_t));
|
||||
|
||||
result->elements.size = path->elements.size;
|
||||
result->points.size = path->points.size;
|
||||
result->contours = path->contours;
|
||||
result->start = path->start;
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
double x1; double y1;
|
||||
double x2; double y2;
|
||||
double x3; double y3;
|
||||
double x4; double y4;
|
||||
} bezier_t;
|
||||
|
||||
static inline void split(const bezier_t* b, bezier_t* first, bezier_t* second)
|
||||
{
|
||||
double c = (b->x2 + b->x3) * 0.5;
|
||||
first->x2 = (b->x1 + b->x2) * 0.5;
|
||||
second->x3 = (b->x3 + b->x4) * 0.5;
|
||||
first->x1 = b->x1;
|
||||
second->x4 = b->x4;
|
||||
first->x3 = (first->x2 + c) * 0.5;
|
||||
second->x2 = (second->x3 + c) * 0.5;
|
||||
first->x4 = second->x1 = (first->x3 + second->x2) * 0.5;
|
||||
|
||||
c = (b->y2 + b->y3) * 0.5;
|
||||
first->y2 = (b->y1 + b->y2) * 0.5;
|
||||
second->y3 = (b->y3 + b->y4) * 0.5;
|
||||
first->y1 = b->y1;
|
||||
second->y4 = b->y4;
|
||||
first->y3 = (first->y2 + c) * 0.5;
|
||||
second->y2 = (second->y3 + c) * 0.5;
|
||||
first->y4 = second->y1 = (first->y3 + second->y2) * 0.5;
|
||||
}
|
||||
|
||||
static void flatten(plutovg_path_t* path, const plutovg_point_t* p0, const plutovg_point_t* p1, const plutovg_point_t* p2, const plutovg_point_t* p3)
|
||||
{
|
||||
bezier_t beziers[32];
|
||||
beziers[0].x1 = p0->x;
|
||||
beziers[0].y1 = p0->y;
|
||||
beziers[0].x2 = p1->x;
|
||||
beziers[0].y2 = p1->y;
|
||||
beziers[0].x3 = p2->x;
|
||||
beziers[0].y3 = p2->y;
|
||||
beziers[0].x4 = p3->x;
|
||||
beziers[0].y4 = p3->y;
|
||||
|
||||
const double threshold = 0.25;
|
||||
|
||||
bezier_t* b = beziers;
|
||||
while(b >= beziers)
|
||||
{
|
||||
double y4y1 = b->y4 - b->y1;
|
||||
double x4x1 = b->x4 - b->x1;
|
||||
double l = fabs(x4x1) + fabs(y4y1);
|
||||
double d;
|
||||
if(l > 1.0)
|
||||
{
|
||||
d = fabs((x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2)) + fabs((x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3));
|
||||
}
|
||||
else
|
||||
{
|
||||
d = fabs(b->x1 - b->x2) + fabs(b->y1 - b->y2) + fabs(b->x1 - b->x3) + fabs(b->y1 - b->y3);
|
||||
l = 1.0;
|
||||
}
|
||||
|
||||
if(d < threshold*l || b == beziers + 31)
|
||||
{
|
||||
plutovg_path_line_to(path, b->x4, b->y4);
|
||||
--b;
|
||||
}
|
||||
else
|
||||
{
|
||||
split(b, b+1, b);
|
||||
++b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_path_clone_flat(const plutovg_path_t* path)
|
||||
{
|
||||
plutovg_path_t* result = plutovg_path_create();
|
||||
plutovg_array_ensure(result->elements, path->elements.size);
|
||||
plutovg_array_ensure(result->points, path->points.size);
|
||||
plutovg_point_t* points = path->points.data;
|
||||
|
||||
for(int i = 0;i < path->elements.size;i++)
|
||||
{
|
||||
switch(path->elements.data[i])
|
||||
{
|
||||
case plutovg_path_element_move_to:
|
||||
plutovg_path_move_to(result, points[0].x, points[0].y);
|
||||
points += 1;
|
||||
break;
|
||||
case plutovg_path_element_line_to:
|
||||
plutovg_path_line_to(result, points[0].x, points[0].y);
|
||||
points += 1;
|
||||
break;
|
||||
case plutovg_path_element_close:
|
||||
plutovg_path_line_to(result, points[0].x, points[0].y);
|
||||
points += 1;
|
||||
break;
|
||||
case plutovg_path_element_cubic_to:
|
||||
{
|
||||
plutovg_point_t p0;
|
||||
plutovg_path_get_current_point(result, &p0.x, &p0.y);
|
||||
flatten(result, &p0, points, points + 1, points + 2);
|
||||
points += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
374
3rdparty/plutovg/plutovg-paint.c
vendored
Normal file
374
3rdparty/plutovg/plutovg-paint.c
vendored
Normal file
|
@ -0,0 +1,374 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
void plutovg_color_init_rgb(plutovg_color_t* color, double r, double g, double b)
|
||||
{
|
||||
plutovg_color_init_rgba(color, r, g, b, 1.0);
|
||||
}
|
||||
|
||||
void plutovg_color_init_rgba(plutovg_color_t* color, double r, double g, double b, double a)
|
||||
{
|
||||
color->r = r;
|
||||
color->g = g;
|
||||
color->b = b;
|
||||
color->a = a;
|
||||
}
|
||||
|
||||
plutovg_gradient_t* plutovg_gradient_create_linear(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
plutovg_gradient_t* gradient = malloc(sizeof(plutovg_gradient_t));
|
||||
gradient->ref = 1;
|
||||
gradient->type = plutovg_gradient_type_linear;
|
||||
gradient->spread = plutovg_spread_method_pad;
|
||||
gradient->opacity = 1.0;
|
||||
plutovg_array_init(gradient->stops);
|
||||
plutovg_matrix_init_identity(&gradient->matrix);
|
||||
plutovg_gradient_set_values_linear(gradient, x1, y1, x2, y2);
|
||||
return gradient;
|
||||
}
|
||||
|
||||
plutovg_gradient_t* plutovg_gradient_create_radial(double cx, double cy, double cr, double fx, double fy, double fr)
|
||||
{
|
||||
plutovg_gradient_t* gradient = malloc(sizeof(plutovg_gradient_t));
|
||||
gradient->ref = 1;
|
||||
gradient->type = plutovg_gradient_type_radial;
|
||||
gradient->spread = plutovg_spread_method_pad;
|
||||
gradient->opacity = 1.0;
|
||||
plutovg_array_init(gradient->stops);
|
||||
plutovg_matrix_init_identity(&gradient->matrix);
|
||||
plutovg_gradient_set_values_radial(gradient, cx, cy, cr, fx, fy, fr);
|
||||
return gradient;
|
||||
}
|
||||
|
||||
plutovg_gradient_t* plutovg_gradient_reference(plutovg_gradient_t* gradient)
|
||||
{
|
||||
++gradient->ref;
|
||||
return gradient;
|
||||
}
|
||||
|
||||
void plutovg_gradient_destroy(plutovg_gradient_t* gradient)
|
||||
{
|
||||
if(gradient==NULL)
|
||||
return;
|
||||
|
||||
if(--gradient->ref==0)
|
||||
{
|
||||
free(gradient->stops.data);
|
||||
free(gradient);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_gradient_get_reference_count(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->ref;
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_type(plutovg_gradient_t* gradient, plutovg_gradient_type_t type)
|
||||
{
|
||||
gradient->type = type;
|
||||
}
|
||||
|
||||
plutovg_gradient_type_t plutovg_gradient_get_type(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->type;
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_spread(plutovg_gradient_t* gradient, plutovg_spread_method_t spread)
|
||||
{
|
||||
gradient->spread = spread;
|
||||
}
|
||||
|
||||
plutovg_spread_method_t plutovg_gradient_get_spread(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->spread;
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_matrix(plutovg_gradient_t* gradient, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
gradient->matrix = *matrix;
|
||||
}
|
||||
|
||||
void plutovg_gradient_get_matrix(const plutovg_gradient_t* gradient, plutovg_matrix_t* matrix)
|
||||
{
|
||||
*matrix = gradient->matrix;
|
||||
}
|
||||
|
||||
void plutovg_gradient_add_stop_rgb(plutovg_gradient_t* gradient, double offset, double r, double g, double b)
|
||||
{
|
||||
plutovg_gradient_add_stop_rgba(gradient, offset, r, g, b, 1.0);
|
||||
}
|
||||
|
||||
void plutovg_gradient_add_stop_rgba(plutovg_gradient_t* gradient, double offset, double r, double g, double b, double a)
|
||||
{
|
||||
plutovg_array_ensure(gradient->stops, 1);
|
||||
plutovg_gradient_stop_t* stops = gradient->stops.data;
|
||||
int nstops = gradient->stops.size;
|
||||
int i;
|
||||
for(i = 0;i < nstops;i++)
|
||||
{
|
||||
if(offset < stops[i].offset)
|
||||
{
|
||||
memmove(&stops[i+1], &stops[i], (size_t)(nstops - i) * sizeof(plutovg_gradient_stop_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stops[i].offset = offset;
|
||||
stops[i].color.r = r;
|
||||
stops[i].color.g = g;
|
||||
stops[i].color.b = b;
|
||||
stops[i].color.a = a;
|
||||
|
||||
gradient->stops.size++;
|
||||
}
|
||||
|
||||
void plutovg_gradient_add_stop(plutovg_gradient_t* gradient, const plutovg_gradient_stop_t* stop)
|
||||
{
|
||||
plutovg_gradient_add_stop_rgba(gradient, stop->offset, stop->color.r, stop->color.g, stop->color.b, stop->color.a);
|
||||
}
|
||||
|
||||
void plutovg_gradient_clear_stops(plutovg_gradient_t* gradient)
|
||||
{
|
||||
gradient->stops.size = 0;
|
||||
}
|
||||
|
||||
int plutovg_gradient_get_stop_count(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->stops.size;
|
||||
}
|
||||
|
||||
plutovg_gradient_stop_t* plutovg_gradient_get_stops(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->stops.data;
|
||||
}
|
||||
|
||||
void plutovg_gradient_get_values_linear(const plutovg_gradient_t* gradient, double* x1, double* y1, double* x2, double* y2)
|
||||
{
|
||||
*x1 = gradient->values[0];
|
||||
*y1 = gradient->values[1];
|
||||
*x2 = gradient->values[2];
|
||||
*y2 = gradient->values[3];
|
||||
}
|
||||
|
||||
void plutovg_gradient_get_values_radial(const plutovg_gradient_t* gradient, double* cx, double* cy, double* cr, double* fx, double* fy, double* fr)
|
||||
{
|
||||
*cx = gradient->values[0];
|
||||
*cy = gradient->values[1];
|
||||
*cr = gradient->values[2];
|
||||
*fx = gradient->values[3];
|
||||
*fy = gradient->values[4];
|
||||
*fr = gradient->values[5];
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_values_linear(plutovg_gradient_t* gradient, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
gradient->values[0] = x1;
|
||||
gradient->values[1] = y1;
|
||||
gradient->values[2] = x2;
|
||||
gradient->values[3] = y2;
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_values_radial(plutovg_gradient_t* gradient, double cx, double cy, double cr, double fx, double fy, double fr)
|
||||
{
|
||||
gradient->values[0] = cx;
|
||||
gradient->values[1] = cy;
|
||||
gradient->values[2] = cr;
|
||||
gradient->values[3] = fx;
|
||||
gradient->values[4] = fy;
|
||||
gradient->values[5] = fr;
|
||||
}
|
||||
|
||||
void plutovg_gradient_set_opacity(plutovg_gradient_t* gradient, double opacity)
|
||||
{
|
||||
gradient->opacity = opacity;
|
||||
}
|
||||
|
||||
double plutovg_gradient_get_opacity(const plutovg_gradient_t* gradient)
|
||||
{
|
||||
return gradient->opacity;
|
||||
}
|
||||
|
||||
plutovg_texture_t* plutovg_texture_create(plutovg_surface_t* surface)
|
||||
{
|
||||
plutovg_texture_t* texture = malloc(sizeof(plutovg_texture_t));
|
||||
texture->ref = 1;
|
||||
texture->type = plutovg_texture_type_plain;
|
||||
texture->surface = plutovg_surface_reference(surface);
|
||||
texture->opacity = 1.0;
|
||||
plutovg_matrix_init_identity(&texture->matrix);
|
||||
return texture;
|
||||
}
|
||||
|
||||
plutovg_texture_t* plutovg_texture_reference(plutovg_texture_t* texture)
|
||||
{
|
||||
++texture->ref;
|
||||
return texture;
|
||||
}
|
||||
|
||||
void plutovg_texture_destroy(plutovg_texture_t* texture)
|
||||
{
|
||||
if(texture==NULL)
|
||||
return;
|
||||
|
||||
if(--texture->ref==0)
|
||||
{
|
||||
plutovg_surface_destroy(texture->surface);
|
||||
free(texture);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_texture_get_reference_count(const plutovg_texture_t* texture)
|
||||
{
|
||||
return texture->ref;
|
||||
}
|
||||
|
||||
void plutovg_texture_set_type(plutovg_texture_t* texture, plutovg_texture_type_t type)
|
||||
{
|
||||
texture->type = type;
|
||||
}
|
||||
|
||||
plutovg_texture_type_t plutovg_texture_get_type(const plutovg_texture_t* texture)
|
||||
{
|
||||
return texture->type;
|
||||
}
|
||||
|
||||
void plutovg_texture_set_matrix(plutovg_texture_t* texture, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
memcpy(&texture->matrix, matrix, sizeof(plutovg_matrix_t));
|
||||
}
|
||||
|
||||
void plutovg_texture_get_matrix(const plutovg_texture_t* texture, plutovg_matrix_t* matrix)
|
||||
{
|
||||
memcpy(matrix, &texture->matrix, sizeof(plutovg_matrix_t));
|
||||
}
|
||||
|
||||
void plutovg_texture_set_surface(plutovg_texture_t* texture, plutovg_surface_t* surface)
|
||||
{
|
||||
surface = plutovg_surface_reference(surface);
|
||||
plutovg_surface_destroy(texture->surface);
|
||||
texture->surface = surface;
|
||||
}
|
||||
|
||||
plutovg_surface_t* plutovg_texture_get_surface(const plutovg_texture_t* texture)
|
||||
{
|
||||
return texture->surface;
|
||||
}
|
||||
|
||||
void plutovg_texture_set_opacity(plutovg_texture_t* texture, double opacity)
|
||||
{
|
||||
texture->opacity = opacity;
|
||||
}
|
||||
|
||||
double plutovg_texture_get_opacity(const plutovg_texture_t* texture)
|
||||
{
|
||||
return texture->opacity;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_rgb(double r, double g, double b)
|
||||
{
|
||||
return plutovg_paint_create_rgba(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_rgba(double r, double g, double b, double a)
|
||||
{
|
||||
plutovg_paint_t* paint = malloc(sizeof(plutovg_paint_t));
|
||||
paint->ref = 1;
|
||||
paint->type = plutovg_paint_type_color;
|
||||
paint->color = malloc(sizeof(plutovg_color_t));
|
||||
plutovg_color_init_rgba(paint->color, r, g, b, a);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_linear(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
plutovg_gradient_t* gradient = plutovg_gradient_create_linear(x1, y1, x2, y2);
|
||||
plutovg_paint_t* paint = plutovg_paint_create_gradient(gradient);
|
||||
plutovg_gradient_destroy(gradient);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_radial(double cx, double cy, double cr, double fx, double fy, double fr)
|
||||
{
|
||||
plutovg_gradient_t* gradient = plutovg_gradient_create_radial(cx, cy, cr, fx, fy, fr);
|
||||
plutovg_paint_t* paint = plutovg_paint_create_gradient(gradient);
|
||||
plutovg_gradient_destroy(gradient);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_for_surface(plutovg_surface_t* surface)
|
||||
{
|
||||
plutovg_texture_t* texture = plutovg_texture_create(surface);
|
||||
plutovg_paint_t* paint = plutovg_paint_create_texture(texture);
|
||||
plutovg_texture_destroy(texture);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_color(const plutovg_color_t* color)
|
||||
{
|
||||
return plutovg_paint_create_rgba(color->r, color->g, color->b, color->a);
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_gradient(plutovg_gradient_t* gradient)
|
||||
{
|
||||
plutovg_paint_t* paint = malloc(sizeof(plutovg_paint_t));
|
||||
paint->ref = 1;
|
||||
paint->type = plutovg_paint_type_gradient;
|
||||
paint->gradient = plutovg_gradient_reference(gradient);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_texture(plutovg_texture_t* texture)
|
||||
{
|
||||
plutovg_paint_t* paint = malloc(sizeof(plutovg_paint_t));
|
||||
paint->ref = 1;
|
||||
paint->type = plutovg_paint_type_texture;
|
||||
paint->texture = plutovg_texture_reference(texture);
|
||||
return paint;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_paint_reference(plutovg_paint_t* paint)
|
||||
{
|
||||
++paint->ref;
|
||||
return paint;
|
||||
}
|
||||
|
||||
void plutovg_paint_destroy(plutovg_paint_t* paint)
|
||||
{
|
||||
if(paint==NULL)
|
||||
return;
|
||||
|
||||
if(--paint->ref==0)
|
||||
{
|
||||
if(paint->type==plutovg_paint_type_color)
|
||||
free(paint->color);
|
||||
if(paint->type==plutovg_paint_type_gradient)
|
||||
plutovg_gradient_destroy(paint->gradient);
|
||||
if(paint->type==plutovg_paint_type_texture)
|
||||
plutovg_texture_destroy(paint->texture);
|
||||
free(paint);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_paint_get_reference_count(const plutovg_paint_t* paint)
|
||||
{
|
||||
return paint->ref;
|
||||
}
|
||||
|
||||
plutovg_paint_type_t plutovg_paint_get_type(const plutovg_paint_t* paint)
|
||||
{
|
||||
return paint->type;
|
||||
}
|
||||
|
||||
plutovg_color_t* plutovg_paint_get_color(const plutovg_paint_t* paint)
|
||||
{
|
||||
return paint->type==plutovg_paint_type_color?paint->color:NULL;
|
||||
}
|
||||
|
||||
plutovg_gradient_t* plutovg_paint_get_gradient(const plutovg_paint_t* paint)
|
||||
{
|
||||
return paint->type==plutovg_paint_type_gradient?paint->gradient:NULL;
|
||||
}
|
||||
|
||||
plutovg_texture_t* plutovg_paint_get_texture(const plutovg_paint_t* paint)
|
||||
{
|
||||
return paint->type==plutovg_paint_type_texture?paint->texture:NULL;
|
||||
}
|
168
3rdparty/plutovg/plutovg-private.h
vendored
Normal file
168
3rdparty/plutovg/plutovg-private.h
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
#ifndef PLUTOVG_PRIVATE_H
|
||||
#define PLUTOVG_PRIVATE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "plutovg.h"
|
||||
|
||||
struct plutovg_surface {
|
||||
int ref;
|
||||
unsigned char* data;
|
||||
int owndata;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
};
|
||||
|
||||
struct plutovg_path {
|
||||
int ref;
|
||||
int contours;
|
||||
plutovg_point_t start;
|
||||
struct {
|
||||
plutovg_path_element_t* data;
|
||||
int size;
|
||||
int capacity;
|
||||
} elements;
|
||||
struct {
|
||||
plutovg_point_t* data;
|
||||
int size;
|
||||
int capacity;
|
||||
} points;
|
||||
};
|
||||
|
||||
struct plutovg_gradient {
|
||||
int ref;
|
||||
plutovg_gradient_type_t type;
|
||||
plutovg_spread_method_t spread;
|
||||
plutovg_matrix_t matrix;
|
||||
double values[6];
|
||||
double opacity;
|
||||
struct {
|
||||
plutovg_gradient_stop_t* data;
|
||||
int size;
|
||||
int capacity;
|
||||
} stops;
|
||||
};
|
||||
|
||||
struct plutovg_texture {
|
||||
int ref;
|
||||
plutovg_texture_type_t type;
|
||||
plutovg_surface_t* surface;
|
||||
plutovg_matrix_t matrix;
|
||||
double opacity;
|
||||
};
|
||||
|
||||
struct plutovg_paint {
|
||||
int ref;
|
||||
plutovg_paint_type_t type;
|
||||
union {
|
||||
plutovg_color_t* color;
|
||||
plutovg_gradient_t* gradient;
|
||||
plutovg_texture_t* texture;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
short x;
|
||||
short y;
|
||||
unsigned short len;
|
||||
unsigned char coverage;
|
||||
} plutovg_span_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
plutovg_span_t* data;
|
||||
int size;
|
||||
int capacity;
|
||||
} spans;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
} plutovg_rle_t;
|
||||
|
||||
typedef struct {
|
||||
double offset;
|
||||
double* data;
|
||||
int size;
|
||||
} plutovg_dash_t;
|
||||
|
||||
typedef struct {
|
||||
double width;
|
||||
double miterlimit;
|
||||
plutovg_line_cap_t cap;
|
||||
plutovg_line_join_t join;
|
||||
plutovg_dash_t* dash;
|
||||
} plutovg_stroke_data_t;
|
||||
|
||||
typedef struct plutovg_state {
|
||||
plutovg_rle_t* clippath;
|
||||
plutovg_paint_t* source;
|
||||
plutovg_matrix_t matrix;
|
||||
plutovg_fill_rule_t winding;
|
||||
plutovg_stroke_data_t stroke;
|
||||
plutovg_operator_t op;
|
||||
double opacity;
|
||||
struct plutovg_state* next;
|
||||
} plutovg_state_t;
|
||||
|
||||
struct plutovg {
|
||||
int ref;
|
||||
plutovg_surface_t* surface;
|
||||
plutovg_state_t* state;
|
||||
plutovg_path_t* path;
|
||||
plutovg_rle_t* rle;
|
||||
plutovg_rle_t* clippath;
|
||||
plutovg_rect_t clip;
|
||||
};
|
||||
|
||||
plutovg_rle_t* plutovg_rle_create(void);
|
||||
void plutovg_rle_destroy(plutovg_rle_t* rle);
|
||||
void plutovg_rle_rasterize(plutovg_rle_t* rle, const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_rect_t* clip, const plutovg_stroke_data_t* stroke, plutovg_fill_rule_t winding);
|
||||
plutovg_rle_t* plutovg_rle_intersection(const plutovg_rle_t* a, const plutovg_rle_t* b);
|
||||
void plutovg_rle_clip_path(plutovg_rle_t* rle, const plutovg_rle_t* clip);
|
||||
plutovg_rle_t* plutovg_rle_clone(const plutovg_rle_t* rle);
|
||||
void plutovg_rle_clear(plutovg_rle_t* rle);
|
||||
|
||||
plutovg_dash_t* plutovg_dash_create(double offset, const double* data, int size);
|
||||
plutovg_dash_t* plutovg_dash_clone(const plutovg_dash_t* dash);
|
||||
void plutovg_dash_destroy(plutovg_dash_t* dash);
|
||||
plutovg_path_t* plutovg_dash_path(const plutovg_dash_t* dash, const plutovg_path_t* path);
|
||||
|
||||
plutovg_state_t* plutovg_state_create(void);
|
||||
plutovg_state_t* plutovg_state_clone(const plutovg_state_t* state);
|
||||
void plutovg_state_destroy(plutovg_state_t* state);
|
||||
|
||||
void plutovg_blend(plutovg_t* pluto, const plutovg_rle_t* rle);
|
||||
void plutovg_blend_color(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_color_t* color);
|
||||
void plutovg_blend_gradient(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_gradient_t* gradient);
|
||||
void plutovg_blend_texture(plutovg_t* pluto, const plutovg_rle_t* rle, const plutovg_texture_t* texture);
|
||||
|
||||
#define plutovg_alpha(c) ((c) >> 24)
|
||||
#define plutovg_red(c) (((c) >> 16) & 0xff)
|
||||
#define plutovg_green(c) (((c) >> 8) & 0xff)
|
||||
#define plutovg_blue(c) (((c) >> 0) & 0xff)
|
||||
|
||||
#define plutovg_array_init(array) \
|
||||
do { \
|
||||
array.data = NULL; \
|
||||
array.size = 0; \
|
||||
array.capacity = 0; \
|
||||
} while(0)
|
||||
|
||||
#define plutovg_array_ensure(array, count) \
|
||||
do { \
|
||||
if(array.size + count > array.capacity) { \
|
||||
int capacity = array.size + count; \
|
||||
int newcapacity = array.capacity == 0 ? 8 : array.capacity; \
|
||||
while(newcapacity < capacity) { newcapacity *= 2; } \
|
||||
array.data = realloc(array.data, (size_t)newcapacity * sizeof(array.data[0])); \
|
||||
array.capacity = newcapacity; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#endif // PLUTOVG_PRIVATE_H
|
413
3rdparty/plutovg/plutovg-rle.c
vendored
Normal file
413
3rdparty/plutovg/plutovg-rle.c
vendored
Normal file
|
@ -0,0 +1,413 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
#include "sw_ft_raster.h"
|
||||
#include "sw_ft_stroker.h"
|
||||
#include "sw_ft_types.h"
|
||||
#include "sw_ft_math.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
static SW_FT_Outline* sw_ft_outline_create(int points, int contours)
|
||||
{
|
||||
SW_FT_Outline* ft = malloc(sizeof(SW_FT_Outline));
|
||||
ft->points = malloc((size_t)(points + contours) * sizeof(SW_FT_Vector));
|
||||
ft->tags = malloc((size_t)(points + contours) * sizeof(char));
|
||||
ft->contours = malloc((size_t)contours * sizeof(short));
|
||||
ft->contours_flag = malloc((size_t)contours * sizeof(char));
|
||||
ft->n_points = ft->n_contours = 0;
|
||||
ft->flags = 0x0;
|
||||
return ft;
|
||||
}
|
||||
|
||||
static void sw_ft_outline_destroy(SW_FT_Outline* ft)
|
||||
{
|
||||
free(ft->points);
|
||||
free(ft->tags);
|
||||
free(ft->contours);
|
||||
free(ft->contours_flag);
|
||||
free(ft);
|
||||
}
|
||||
|
||||
#define FT_COORD(x) (SW_FT_Pos)((x) * 64)
|
||||
static void sw_ft_outline_move_to(SW_FT_Outline* ft, double x, double y)
|
||||
{
|
||||
ft->points[ft->n_points].x = FT_COORD(x);
|
||||
ft->points[ft->n_points].y = FT_COORD(y);
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_ON;
|
||||
if(ft->n_points)
|
||||
{
|
||||
ft->contours[ft->n_contours] = ft->n_points - 1;
|
||||
ft->n_contours++;
|
||||
}
|
||||
|
||||
ft->contours_flag[ft->n_contours] = 1;
|
||||
ft->n_points++;
|
||||
}
|
||||
|
||||
static void sw_ft_outline_line_to(SW_FT_Outline* ft, double x, double y)
|
||||
{
|
||||
ft->points[ft->n_points].x = FT_COORD(x);
|
||||
ft->points[ft->n_points].y = FT_COORD(y);
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_ON;
|
||||
ft->n_points++;
|
||||
}
|
||||
|
||||
static void sw_ft_outline_cubic_to(SW_FT_Outline* ft, double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
ft->points[ft->n_points].x = FT_COORD(x1);
|
||||
ft->points[ft->n_points].y = FT_COORD(y1);
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_CUBIC;
|
||||
ft->n_points++;
|
||||
|
||||
ft->points[ft->n_points].x = FT_COORD(x2);
|
||||
ft->points[ft->n_points].y = FT_COORD(y2);
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_CUBIC;
|
||||
ft->n_points++;
|
||||
|
||||
ft->points[ft->n_points].x = FT_COORD(x3);
|
||||
ft->points[ft->n_points].y = FT_COORD(y3);
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_ON;
|
||||
ft->n_points++;
|
||||
}
|
||||
|
||||
static void sw_ft_outline_close(SW_FT_Outline* ft)
|
||||
{
|
||||
ft->contours_flag[ft->n_contours] = 0;
|
||||
int index = ft->n_contours ? ft->contours[ft->n_contours - 1] + 1 : 0;
|
||||
if(index == ft->n_points)
|
||||
return;
|
||||
|
||||
ft->points[ft->n_points].x = ft->points[index].x;
|
||||
ft->points[ft->n_points].y = ft->points[index].y;
|
||||
ft->tags[ft->n_points] = SW_FT_CURVE_TAG_ON;
|
||||
ft->n_points++;
|
||||
}
|
||||
|
||||
static void sw_ft_outline_end(SW_FT_Outline* ft)
|
||||
{
|
||||
if(ft->n_points)
|
||||
{
|
||||
ft->contours[ft->n_contours] = ft->n_points - 1;
|
||||
ft->n_contours++;
|
||||
}
|
||||
}
|
||||
|
||||
static SW_FT_Outline* sw_ft_outline_convert(const plutovg_path_t* path, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
SW_FT_Outline* outline = sw_ft_outline_create(path->points.size, path->contours);
|
||||
plutovg_path_element_t* elements = path->elements.data;
|
||||
plutovg_point_t* points = path->points.data;
|
||||
plutovg_point_t p[3];
|
||||
for(int i = 0;i < path->elements.size;i++)
|
||||
{
|
||||
switch(elements[i])
|
||||
{
|
||||
case plutovg_path_element_move_to:
|
||||
plutovg_matrix_map_point(matrix, &points[0], &p[0]);
|
||||
sw_ft_outline_move_to(outline, p[0].x, p[0].y);
|
||||
points += 1;
|
||||
break;
|
||||
case plutovg_path_element_line_to:
|
||||
plutovg_matrix_map_point(matrix, &points[0], &p[0]);
|
||||
sw_ft_outline_line_to(outline, p[0].x, p[0].y);
|
||||
points += 1;
|
||||
break;
|
||||
case plutovg_path_element_cubic_to:
|
||||
plutovg_matrix_map_point(matrix, &points[0], &p[0]);
|
||||
plutovg_matrix_map_point(matrix, &points[1], &p[1]);
|
||||
plutovg_matrix_map_point(matrix, &points[2], &p[2]);
|
||||
sw_ft_outline_cubic_to(outline, p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y);
|
||||
points += 3;
|
||||
break;
|
||||
case plutovg_path_element_close:
|
||||
sw_ft_outline_close(outline);
|
||||
points += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sw_ft_outline_end(outline);
|
||||
return outline;
|
||||
}
|
||||
|
||||
static SW_FT_Outline* sw_ft_outline_convert_dash(const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_dash_t* dash)
|
||||
{
|
||||
plutovg_path_t* dashed = plutovg_dash_path(dash, path);
|
||||
SW_FT_Outline* outline = sw_ft_outline_convert(dashed, matrix);
|
||||
plutovg_path_destroy(dashed);
|
||||
return outline;
|
||||
}
|
||||
|
||||
static void generation_callback(int count, const SW_FT_Span* spans, void* user)
|
||||
{
|
||||
plutovg_rle_t* rle = user;
|
||||
plutovg_array_ensure(rle->spans, count);
|
||||
plutovg_span_t* data = rle->spans.data + rle->spans.size;
|
||||
memcpy(data, spans, (size_t)count * sizeof(plutovg_span_t));
|
||||
rle->spans.size += count;
|
||||
}
|
||||
|
||||
static void bbox_callback(int x, int y, int w, int h, void* user)
|
||||
{
|
||||
plutovg_rle_t* rle = user;
|
||||
rle->x = x;
|
||||
rle->y = y;
|
||||
rle->w = w;
|
||||
rle->h = h;
|
||||
}
|
||||
|
||||
plutovg_rle_t* plutovg_rle_create(void)
|
||||
{
|
||||
plutovg_rle_t* rle = malloc(sizeof(plutovg_rle_t));
|
||||
plutovg_array_init(rle->spans);
|
||||
rle->x = 0;
|
||||
rle->y = 0;
|
||||
rle->w = 0;
|
||||
rle->h = 0;
|
||||
return rle;
|
||||
}
|
||||
|
||||
void plutovg_rle_destroy(plutovg_rle_t* rle)
|
||||
{
|
||||
if(rle==NULL)
|
||||
return;
|
||||
|
||||
free(rle->spans.data);
|
||||
free(rle);
|
||||
}
|
||||
|
||||
#define SQRT2 1.41421356237309504880
|
||||
void plutovg_rle_rasterize(plutovg_rle_t* rle, const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_rect_t* clip, const plutovg_stroke_data_t* stroke, plutovg_fill_rule_t winding)
|
||||
{
|
||||
SW_FT_Raster_Params params;
|
||||
params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
|
||||
params.gray_spans = generation_callback;
|
||||
params.bbox_cb = bbox_callback;
|
||||
params.user = rle;
|
||||
|
||||
if(clip)
|
||||
{
|
||||
params.flags |= SW_FT_RASTER_FLAG_CLIP;
|
||||
params.clip_box.xMin = (SW_FT_Pos)clip->x;
|
||||
params.clip_box.yMin = (SW_FT_Pos)clip->y;
|
||||
params.clip_box.xMax = (SW_FT_Pos)(clip->x + clip->w);
|
||||
params.clip_box.yMax = (SW_FT_Pos)(clip->y + clip->h);
|
||||
}
|
||||
|
||||
if(stroke)
|
||||
{
|
||||
SW_FT_Outline* outline = stroke->dash ? sw_ft_outline_convert_dash(path, matrix, stroke->dash) : sw_ft_outline_convert(path, matrix);
|
||||
SW_FT_Stroker_LineCap ftCap;
|
||||
SW_FT_Stroker_LineJoin ftJoin;
|
||||
SW_FT_Fixed ftWidth;
|
||||
SW_FT_Fixed ftMiterLimit;
|
||||
|
||||
plutovg_point_t p1 = {0, 0};
|
||||
plutovg_point_t p2 = {SQRT2, SQRT2};
|
||||
plutovg_point_t p3;
|
||||
|
||||
plutovg_matrix_map_point(matrix, &p1, &p1);
|
||||
plutovg_matrix_map_point(matrix, &p2, &p2);
|
||||
|
||||
p3.x = p2.x - p1.x;
|
||||
p3.y = p2.y - p1.y;
|
||||
|
||||
double scale = sqrt(p3.x*p3.x + p3.y*p3.y) / 2.0;
|
||||
|
||||
ftWidth = (SW_FT_Fixed)(stroke->width * scale * 0.5 * (1 << 6));
|
||||
ftMiterLimit = (SW_FT_Fixed)(stroke->miterlimit * (1 << 16));
|
||||
|
||||
switch(stroke->cap)
|
||||
{
|
||||
case plutovg_line_cap_square:
|
||||
ftCap = SW_FT_STROKER_LINECAP_SQUARE;
|
||||
break;
|
||||
case plutovg_line_cap_round:
|
||||
ftCap = SW_FT_STROKER_LINECAP_ROUND;
|
||||
break;
|
||||
default:
|
||||
ftCap = SW_FT_STROKER_LINECAP_BUTT;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(stroke->join)
|
||||
{
|
||||
case plutovg_line_join_bevel:
|
||||
ftJoin = SW_FT_STROKER_LINEJOIN_BEVEL;
|
||||
break;
|
||||
case plutovg_line_join_round:
|
||||
ftJoin = SW_FT_STROKER_LINEJOIN_ROUND;
|
||||
break;
|
||||
default:
|
||||
ftJoin = SW_FT_STROKER_LINEJOIN_MITER_FIXED;
|
||||
break;
|
||||
}
|
||||
|
||||
SW_FT_Stroker stroker;
|
||||
SW_FT_Stroker_New(&stroker);
|
||||
SW_FT_Stroker_Set(stroker, ftWidth, ftCap, ftJoin, ftMiterLimit);
|
||||
SW_FT_Stroker_ParseOutline(stroker, outline);
|
||||
|
||||
SW_FT_UInt points;
|
||||
SW_FT_UInt contours;
|
||||
SW_FT_Stroker_GetCounts(stroker, &points, &contours);
|
||||
|
||||
SW_FT_Outline* strokeOutline = sw_ft_outline_create((int)points, (int)contours);
|
||||
SW_FT_Stroker_Export(stroker, strokeOutline);
|
||||
SW_FT_Stroker_Done(stroker);
|
||||
|
||||
strokeOutline->flags = SW_FT_OUTLINE_NONE;
|
||||
params.source = strokeOutline;
|
||||
sw_ft_grays_raster.raster_render(NULL, ¶ms);
|
||||
sw_ft_outline_destroy(outline);
|
||||
sw_ft_outline_destroy(strokeOutline);
|
||||
}
|
||||
else
|
||||
{
|
||||
SW_FT_Outline* outline = sw_ft_outline_convert(path, matrix);
|
||||
outline->flags = winding == plutovg_fill_rule_even_odd ? SW_FT_OUTLINE_EVEN_ODD_FILL : SW_FT_OUTLINE_NONE;
|
||||
params.source = outline;
|
||||
sw_ft_grays_raster.raster_render(NULL, ¶ms);
|
||||
sw_ft_outline_destroy(outline);
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define DIV255(x) (((x) + ((x) >> 8) + 0x80) >> 8)
|
||||
plutovg_rle_t* plutovg_rle_intersection(const plutovg_rle_t* a, const plutovg_rle_t* b)
|
||||
{
|
||||
int count = MAX(a->spans.size, b->spans.size);
|
||||
plutovg_rle_t* result = malloc(sizeof(plutovg_rle_t));
|
||||
plutovg_array_init(result->spans);
|
||||
plutovg_array_ensure(result->spans, count);
|
||||
|
||||
plutovg_span_t* a_spans = a->spans.data;
|
||||
plutovg_span_t* a_end = a_spans + a->spans.size;
|
||||
|
||||
plutovg_span_t* b_spans = b->spans.data;
|
||||
plutovg_span_t* b_end = b_spans + b->spans.size;
|
||||
|
||||
while(count && a_spans < a_end && b_spans < b_end)
|
||||
{
|
||||
if(b_spans->y > a_spans->y)
|
||||
{
|
||||
++a_spans;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(a_spans->y != b_spans->y)
|
||||
{
|
||||
++b_spans;
|
||||
continue;
|
||||
}
|
||||
|
||||
int ax1 = a_spans->x;
|
||||
int ax2 = ax1 + a_spans->len;
|
||||
int bx1 = b_spans->x;
|
||||
int bx2 = bx1 + b_spans->len;
|
||||
|
||||
if(bx1 < ax1 && bx2 < ax1)
|
||||
{
|
||||
++b_spans;
|
||||
continue;
|
||||
}
|
||||
else if(ax1 < bx1 && ax2 < bx1)
|
||||
{
|
||||
++a_spans;
|
||||
continue;
|
||||
}
|
||||
|
||||
int x = MAX(ax1, bx1);
|
||||
int len = MIN(ax2, bx2) - x;
|
||||
if(len)
|
||||
{
|
||||
plutovg_span_t* span = result->spans.data + result->spans.size;
|
||||
span->x = (short)x;
|
||||
span->len = (unsigned short)len;
|
||||
span->y = a_spans->y;
|
||||
span->coverage = DIV255(a_spans->coverage * b_spans->coverage);
|
||||
++result->spans.size;
|
||||
--count;
|
||||
}
|
||||
|
||||
if(ax2 < bx2)
|
||||
{
|
||||
++a_spans;
|
||||
}
|
||||
else
|
||||
{
|
||||
++b_spans;
|
||||
}
|
||||
}
|
||||
|
||||
if(result->spans.size==0)
|
||||
{
|
||||
result->x = 0;
|
||||
result->y = 0;
|
||||
result->w = 0;
|
||||
result->h = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
plutovg_span_t* spans = result->spans.data;
|
||||
int x1 = INT_MAX;
|
||||
int y1 = spans[0].y;
|
||||
int x2 = 0;
|
||||
int y2 = spans[result->spans.size - 1].y;
|
||||
for(int i = 0;i < result->spans.size;i++)
|
||||
{
|
||||
if(spans[i].x < x1) x1 = spans[i].x;
|
||||
if(spans[i].x + spans[i].len > x2) x2 = spans[i].x + spans[i].len;
|
||||
}
|
||||
|
||||
result->x = x1;
|
||||
result->y = y1;
|
||||
result->w = x2 - x1;
|
||||
result->h = y2 - y1 + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
void plutovg_rle_clip_path(plutovg_rle_t* rle, const plutovg_rle_t* clip)
|
||||
{
|
||||
if(rle==NULL || clip==NULL)
|
||||
return;
|
||||
|
||||
plutovg_rle_t* result = plutovg_rle_intersection(rle, clip);
|
||||
plutovg_array_ensure(rle->spans, result->spans.size);
|
||||
memcpy(rle->spans.data, result->spans.data, (size_t)result->spans.size * sizeof(plutovg_span_t));
|
||||
rle->spans.size = result->spans.size;
|
||||
rle->x = result->x;
|
||||
rle->y = result->y;
|
||||
rle->w = result->w;
|
||||
rle->h = result->h;
|
||||
plutovg_rle_destroy(result);
|
||||
}
|
||||
|
||||
plutovg_rle_t* plutovg_rle_clone(const plutovg_rle_t* rle)
|
||||
{
|
||||
if(rle==NULL)
|
||||
return NULL;
|
||||
|
||||
plutovg_rle_t* result = malloc(sizeof(plutovg_rle_t));
|
||||
plutovg_array_init(result->spans);
|
||||
plutovg_array_ensure(result->spans, rle->spans.size);
|
||||
|
||||
memcpy(result->spans.data, rle->spans.data, (size_t)rle->spans.size * sizeof(plutovg_span_t));
|
||||
result->spans.size = rle->spans.size;
|
||||
result->x = rle->x;
|
||||
result->y = rle->y;
|
||||
result->w = rle->w;
|
||||
result->h = rle->h;
|
||||
return result;
|
||||
}
|
||||
|
||||
void plutovg_rle_clear(plutovg_rle_t* rle)
|
||||
{
|
||||
rle->spans.size = 0;
|
||||
rle->x = 0;
|
||||
rle->y = 0;
|
||||
rle->w = 0;
|
||||
rle->h = 0;
|
||||
}
|
497
3rdparty/plutovg/plutovg.c
vendored
Normal file
497
3rdparty/plutovg/plutovg.c
vendored
Normal file
|
@ -0,0 +1,497 @@
|
|||
#include "plutovg-private.h"
|
||||
|
||||
plutovg_surface_t* plutovg_surface_create(int width, int height)
|
||||
{
|
||||
plutovg_surface_t* surface = malloc(sizeof(plutovg_surface_t));
|
||||
surface->ref = 1;
|
||||
surface->owndata = 1;
|
||||
surface->data = calloc(1, (size_t)(width * height * 4));
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->stride = width * 4;
|
||||
return surface;
|
||||
}
|
||||
|
||||
plutovg_surface_t* plutovg_surface_create_for_data(unsigned char* data, int width, int height, int stride)
|
||||
{
|
||||
plutovg_surface_t* surface = malloc(sizeof(plutovg_surface_t));
|
||||
surface->ref = 1;
|
||||
surface->owndata = 0;
|
||||
surface->data = data;
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->stride = stride;
|
||||
return surface;
|
||||
}
|
||||
|
||||
plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface)
|
||||
{
|
||||
++surface->ref;
|
||||
return surface;
|
||||
}
|
||||
|
||||
void plutovg_surface_destroy(plutovg_surface_t* surface)
|
||||
{
|
||||
if(surface==NULL)
|
||||
return;
|
||||
|
||||
if(--surface->ref==0)
|
||||
{
|
||||
if(surface->owndata)
|
||||
free(surface->data);
|
||||
free(surface);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_surface_get_reference_count(const plutovg_surface_t* surface)
|
||||
{
|
||||
return surface->ref;
|
||||
}
|
||||
|
||||
unsigned char* plutovg_surface_get_data(const plutovg_surface_t* surface)
|
||||
{
|
||||
return surface->data;
|
||||
}
|
||||
|
||||
int plutovg_surface_get_width(const plutovg_surface_t* surface)
|
||||
{
|
||||
return surface->width;
|
||||
}
|
||||
|
||||
int plutovg_surface_get_height(const plutovg_surface_t* surface)
|
||||
{
|
||||
return surface->height;
|
||||
}
|
||||
|
||||
int plutovg_surface_get_stride(const plutovg_surface_t* surface)
|
||||
{
|
||||
return surface->stride;
|
||||
}
|
||||
|
||||
plutovg_state_t* plutovg_state_create(void)
|
||||
{
|
||||
plutovg_state_t* state = malloc(sizeof(plutovg_state_t));
|
||||
state->clippath = NULL;
|
||||
state->source = plutovg_paint_create_rgb(0, 0, 0);
|
||||
plutovg_matrix_init_identity(&state->matrix);
|
||||
state->winding = plutovg_fill_rule_non_zero;
|
||||
state->stroke.width = 1.0;
|
||||
state->stroke.miterlimit = 4.0;
|
||||
state->stroke.cap = plutovg_line_cap_butt;
|
||||
state->stroke.join = plutovg_line_join_miter;
|
||||
state->stroke.dash = NULL;
|
||||
state->op = plutovg_operator_src_over;
|
||||
state->opacity = 1.0;
|
||||
state->next = NULL;
|
||||
return state;
|
||||
}
|
||||
|
||||
plutovg_state_t* plutovg_state_clone(const plutovg_state_t* state)
|
||||
{
|
||||
plutovg_state_t* newstate = malloc(sizeof(plutovg_state_t));
|
||||
newstate->clippath = plutovg_rle_clone(state->clippath);
|
||||
newstate->source = plutovg_paint_reference(state->source); /** FIXME: **/
|
||||
newstate->matrix = state->matrix;
|
||||
newstate->winding = state->winding;
|
||||
newstate->stroke.width = state->stroke.width;
|
||||
newstate->stroke.miterlimit = state->stroke.miterlimit;
|
||||
newstate->stroke.cap = state->stroke.cap;
|
||||
newstate->stroke.join = state->stroke.join;
|
||||
newstate->stroke.dash = plutovg_dash_clone(state->stroke.dash);
|
||||
newstate->op = state->op;
|
||||
newstate->opacity = state->opacity;
|
||||
newstate->next = NULL;
|
||||
return newstate;
|
||||
}
|
||||
|
||||
void plutovg_state_destroy(plutovg_state_t* state)
|
||||
{
|
||||
plutovg_rle_destroy(state->clippath);
|
||||
plutovg_paint_destroy(state->source);
|
||||
plutovg_dash_destroy(state->stroke.dash);
|
||||
free(state);
|
||||
}
|
||||
|
||||
plutovg_t* plutovg_create(plutovg_surface_t* surface)
|
||||
{
|
||||
plutovg_t* pluto = malloc(sizeof(plutovg_t));
|
||||
pluto->ref = 1;
|
||||
pluto->surface = plutovg_surface_reference(surface);
|
||||
pluto->state = plutovg_state_create();
|
||||
pluto->path = plutovg_path_create();
|
||||
pluto->rle = plutovg_rle_create();
|
||||
pluto->clippath = NULL;
|
||||
pluto->clip.x = 0.0;
|
||||
pluto->clip.y = 0.0;
|
||||
pluto->clip.w = surface->width;
|
||||
pluto->clip.h = surface->height;
|
||||
return pluto;
|
||||
}
|
||||
|
||||
plutovg_t* plutovg_reference(plutovg_t* pluto)
|
||||
{
|
||||
++pluto->ref;
|
||||
return pluto;
|
||||
}
|
||||
|
||||
void plutovg_destroy(plutovg_t* pluto)
|
||||
{
|
||||
if(pluto==NULL)
|
||||
return;
|
||||
|
||||
if(--pluto->ref==0)
|
||||
{
|
||||
while(pluto->state)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
pluto->state = state->next;
|
||||
plutovg_state_destroy(state);
|
||||
}
|
||||
|
||||
plutovg_surface_destroy(pluto->surface);
|
||||
plutovg_path_destroy(pluto->path);
|
||||
plutovg_rle_destroy(pluto->rle);
|
||||
plutovg_rle_destroy(pluto->clippath);
|
||||
free(pluto);
|
||||
}
|
||||
}
|
||||
|
||||
int plutovg_get_reference_count(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->ref;
|
||||
}
|
||||
|
||||
void plutovg_save(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* newstate = plutovg_state_clone(pluto->state);
|
||||
newstate->next = pluto->state;
|
||||
pluto->state = newstate;
|
||||
}
|
||||
|
||||
void plutovg_restore(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* oldstate = pluto->state;
|
||||
pluto->state = oldstate->next;
|
||||
plutovg_state_destroy(oldstate);
|
||||
}
|
||||
|
||||
void plutovg_set_source_rgb(plutovg_t* pluto, double r, double g, double b)
|
||||
{
|
||||
plutovg_set_source_rgba(pluto, r, g, b, 1.0);
|
||||
}
|
||||
|
||||
void plutovg_set_source_rgba(plutovg_t* pluto, double r, double g, double b, double a)
|
||||
{
|
||||
plutovg_paint_t* source = plutovg_paint_create_rgba(r, g, b, a);
|
||||
plutovg_set_source(pluto, source);
|
||||
plutovg_paint_destroy(source);
|
||||
}
|
||||
|
||||
void plutovg_set_source_surface(plutovg_t* pluto, plutovg_surface_t* surface, double x, double y)
|
||||
{
|
||||
plutovg_paint_t* source = plutovg_paint_create_for_surface(surface);
|
||||
plutovg_texture_t* texture = plutovg_paint_get_texture(source);
|
||||
plutovg_matrix_t matrix;
|
||||
plutovg_matrix_init_translate(&matrix, x, y);
|
||||
plutovg_texture_set_matrix(texture, &matrix);
|
||||
plutovg_set_source(pluto, source);
|
||||
plutovg_paint_destroy(source);
|
||||
}
|
||||
|
||||
void plutovg_set_source_color(plutovg_t* pluto, const plutovg_color_t* color)
|
||||
{
|
||||
plutovg_set_source_rgba(pluto, color->r, color->g, color->b, color->a);
|
||||
}
|
||||
|
||||
void plutovg_set_source_gradient(plutovg_t* pluto, plutovg_gradient_t* gradient)
|
||||
{
|
||||
plutovg_paint_t* source = plutovg_paint_create_gradient(gradient);
|
||||
plutovg_set_source(pluto, source);
|
||||
plutovg_paint_destroy(source);
|
||||
}
|
||||
|
||||
void plutovg_set_source_texture(plutovg_t* pluto, plutovg_texture_t* texture)
|
||||
{
|
||||
plutovg_paint_t* source = plutovg_paint_create_texture(texture);
|
||||
plutovg_set_source(pluto, source);
|
||||
plutovg_paint_destroy(source);
|
||||
}
|
||||
|
||||
void plutovg_set_source(plutovg_t* pluto, plutovg_paint_t* source)
|
||||
{
|
||||
source = plutovg_paint_reference(source);
|
||||
plutovg_paint_destroy(pluto->state->source);
|
||||
pluto->state->source = source;
|
||||
}
|
||||
|
||||
plutovg_paint_t* plutovg_get_source(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->source;
|
||||
}
|
||||
|
||||
void plutovg_set_operator(plutovg_t* pluto, plutovg_operator_t op)
|
||||
{
|
||||
pluto->state->op = op;
|
||||
}
|
||||
|
||||
void plutovg_set_opacity(plutovg_t* pluto, double opacity)
|
||||
{
|
||||
pluto->state->opacity = opacity;
|
||||
}
|
||||
|
||||
void plutovg_set_fill_rule(plutovg_t* pluto, plutovg_fill_rule_t fill_rule)
|
||||
{
|
||||
pluto->state->winding = fill_rule;
|
||||
}
|
||||
|
||||
plutovg_operator_t plutovg_get_operator(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->op;
|
||||
}
|
||||
|
||||
double plutovg_get_opacity(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->opacity;
|
||||
}
|
||||
|
||||
plutovg_fill_rule_t plutovg_get_fill_rule(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->winding;
|
||||
}
|
||||
|
||||
void plutovg_set_line_width(plutovg_t* pluto, double width)
|
||||
{
|
||||
pluto->state->stroke.width = width;
|
||||
}
|
||||
|
||||
void plutovg_set_line_cap(plutovg_t* pluto, plutovg_line_cap_t cap)
|
||||
{
|
||||
pluto->state->stroke.cap = cap;
|
||||
}
|
||||
|
||||
void plutovg_set_line_join(plutovg_t* pluto, plutovg_line_join_t join)
|
||||
{
|
||||
pluto->state->stroke.join = join;
|
||||
}
|
||||
|
||||
void plutovg_set_miter_limit(plutovg_t* pluto, double limit)
|
||||
{
|
||||
pluto->state->stroke.miterlimit = limit;
|
||||
}
|
||||
|
||||
void plutovg_set_dash(plutovg_t* pluto, double offset, const double* data, int size)
|
||||
{
|
||||
plutovg_dash_destroy(pluto->state->stroke.dash);
|
||||
pluto->state->stroke.dash = plutovg_dash_create(offset, data, size);
|
||||
}
|
||||
|
||||
double plutovg_get_line_width(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->stroke.width;
|
||||
}
|
||||
|
||||
plutovg_line_cap_t plutovg_get_line_cap(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->stroke.cap;
|
||||
}
|
||||
|
||||
plutovg_line_join_t plutovg_get_line_join(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->stroke.join;
|
||||
}
|
||||
|
||||
double plutovg_get_miter_limit(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->state->stroke.miterlimit;
|
||||
}
|
||||
|
||||
void plutovg_translate(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_matrix_translate(&pluto->state->matrix, x, y);
|
||||
}
|
||||
|
||||
void plutovg_scale(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_matrix_scale(&pluto->state->matrix, x, y);
|
||||
}
|
||||
|
||||
void plutovg_rotate(plutovg_t* pluto, double radians, double x, double y)
|
||||
{
|
||||
plutovg_matrix_rotate(&pluto->state->matrix, radians, x, y);
|
||||
}
|
||||
|
||||
void plutovg_transform(plutovg_t* pluto, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
plutovg_matrix_multiply(&pluto->state->matrix, matrix, &pluto->state->matrix);
|
||||
}
|
||||
|
||||
void plutovg_set_matrix(plutovg_t* pluto, const plutovg_matrix_t* matrix)
|
||||
{
|
||||
pluto->state->matrix = *matrix;
|
||||
}
|
||||
|
||||
void plutovg_identity_matrix(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_matrix_init_identity(&pluto->state->matrix);
|
||||
}
|
||||
|
||||
void plutovg_get_matrix(const plutovg_t* pluto, plutovg_matrix_t* matrix)
|
||||
{
|
||||
*matrix = pluto->state->matrix;
|
||||
}
|
||||
|
||||
void plutovg_move_to(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_path_move_to(pluto->path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_line_to(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_path_line_to(pluto->path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_quad_to(plutovg_t* pluto, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
plutovg_path_quad_to(pluto->path, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void plutovg_cubic_to(plutovg_t* pluto, double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
plutovg_path_cubic_to(pluto->path, x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
|
||||
void plutovg_rel_move_to(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_path_rel_move_to(pluto->path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_rel_line_to(plutovg_t* pluto, double x, double y)
|
||||
{
|
||||
plutovg_path_rel_line_to(pluto->path, x, y);
|
||||
}
|
||||
|
||||
void plutovg_rel_quad_to(plutovg_t* pluto, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
plutovg_path_rel_quad_to(pluto->path, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void plutovg_rel_cubic_to(plutovg_t* pluto, double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
plutovg_path_rel_cubic_to(pluto->path, x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
|
||||
void plutovg_rect(plutovg_t* pluto, double x, double y, double w, double h)
|
||||
{
|
||||
plutovg_path_add_rect(pluto->path, x, y, w, h);
|
||||
}
|
||||
|
||||
void plutovg_round_rect(plutovg_t* pluto, double x, double y, double w, double h, double rx, double ry)
|
||||
{
|
||||
plutovg_path_add_round_rect(pluto->path, x, y, w, h, rx, ry);
|
||||
}
|
||||
|
||||
void plutovg_ellipse(plutovg_t* pluto, double cx, double cy, double rx, double ry)
|
||||
{
|
||||
plutovg_path_add_ellipse(pluto->path, cx, cy, rx, ry);
|
||||
}
|
||||
|
||||
void plutovg_circle(plutovg_t* pluto, double cx, double cy, double r)
|
||||
{
|
||||
plutovg_ellipse(pluto, cx, cy, r, r);
|
||||
}
|
||||
|
||||
void plutovg_add_path(plutovg_t* pluto, const plutovg_path_t* path)
|
||||
{
|
||||
plutovg_path_add_path(pluto->path, path, NULL);
|
||||
}
|
||||
|
||||
void plutovg_new_path(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_path_clear(pluto->path);
|
||||
}
|
||||
|
||||
void plutovg_close_path(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_path_close(pluto->path);
|
||||
}
|
||||
|
||||
plutovg_path_t* plutovg_get_path(const plutovg_t* pluto)
|
||||
{
|
||||
return pluto->path;
|
||||
}
|
||||
|
||||
void plutovg_fill(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_fill_preserve(pluto);
|
||||
plutovg_new_path(pluto);
|
||||
}
|
||||
|
||||
void plutovg_stroke(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_stroke_preserve(pluto);
|
||||
plutovg_new_path(pluto);
|
||||
}
|
||||
|
||||
void plutovg_clip(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_clip_preserve(pluto);
|
||||
plutovg_new_path(pluto);
|
||||
}
|
||||
|
||||
void plutovg_paint(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
if(state->clippath==NULL && pluto->clippath==NULL)
|
||||
{
|
||||
plutovg_path_t* path = plutovg_path_create();
|
||||
plutovg_path_add_rect(path, pluto->clip.x, pluto->clip.y, pluto->clip.w, pluto->clip.h);
|
||||
plutovg_matrix_t matrix;
|
||||
plutovg_matrix_init_identity(&matrix);
|
||||
pluto->clippath = plutovg_rle_create();
|
||||
plutovg_rle_rasterize(pluto->clippath, path, &matrix, &pluto->clip, NULL, plutovg_fill_rule_non_zero);
|
||||
plutovg_path_destroy(path);
|
||||
}
|
||||
|
||||
plutovg_rle_t* rle = state->clippath ? state->clippath : pluto->clippath;
|
||||
plutovg_blend(pluto, rle);
|
||||
}
|
||||
|
||||
void plutovg_fill_preserve(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
plutovg_rle_clear(pluto->rle);
|
||||
plutovg_rle_rasterize(pluto->rle, pluto->path, &state->matrix, &pluto->clip, NULL, state->winding);
|
||||
plutovg_rle_clip_path(pluto->rle, state->clippath);
|
||||
plutovg_blend(pluto, pluto->rle);
|
||||
}
|
||||
|
||||
void plutovg_stroke_preserve(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
plutovg_rle_clear(pluto->rle);
|
||||
plutovg_rle_rasterize(pluto->rle, pluto->path, &state->matrix, &pluto->clip, &state->stroke, plutovg_fill_rule_non_zero);
|
||||
plutovg_rle_clip_path(pluto->rle, state->clippath);
|
||||
plutovg_blend(pluto, pluto->rle);
|
||||
}
|
||||
|
||||
void plutovg_clip_preserve(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_state_t* state = pluto->state;
|
||||
if(state->clippath)
|
||||
{
|
||||
plutovg_rle_clear(pluto->rle);
|
||||
plutovg_rle_rasterize(pluto->rle, pluto->path, &state->matrix, &pluto->clip, NULL, state->winding);
|
||||
plutovg_rle_clip_path(state->clippath, pluto->rle);
|
||||
}
|
||||
else
|
||||
{
|
||||
state->clippath = plutovg_rle_create();
|
||||
plutovg_rle_rasterize(state->clippath, pluto->path, &state->matrix, &pluto->clip, NULL, state->winding);
|
||||
}
|
||||
}
|
||||
|
||||
void plutovg_reset_clip(plutovg_t* pluto)
|
||||
{
|
||||
plutovg_rle_destroy(pluto->state->clippath);
|
||||
pluto->state->clippath = NULL;
|
||||
}
|
288
3rdparty/plutovg/plutovg.h
vendored
Normal file
288
3rdparty/plutovg/plutovg.h
vendored
Normal file
|
@ -0,0 +1,288 @@
|
|||
#ifndef PLUTOVG_H
|
||||
#define PLUTOVG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct plutovg_surface plutovg_surface_t;
|
||||
|
||||
plutovg_surface_t* plutovg_surface_create(int width, int height);
|
||||
plutovg_surface_t* plutovg_surface_create_for_data(unsigned char* data, int width, int height, int stride);
|
||||
plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface);
|
||||
void plutovg_surface_destroy(plutovg_surface_t* surface);
|
||||
int plutovg_surface_get_reference_count(const plutovg_surface_t* surface);
|
||||
unsigned char* plutovg_surface_get_data(const plutovg_surface_t* surface);
|
||||
int plutovg_surface_get_width(const plutovg_surface_t* surface);
|
||||
int plutovg_surface_get_height(const plutovg_surface_t* surface);
|
||||
int plutovg_surface_get_stride(const plutovg_surface_t* surface);
|
||||
|
||||
typedef struct {
|
||||
double x;
|
||||
double y;
|
||||
} plutovg_point_t;
|
||||
|
||||
typedef struct {
|
||||
double x;
|
||||
double y;
|
||||
double w;
|
||||
double h;
|
||||
} plutovg_rect_t;
|
||||
|
||||
void plutovg_rect_init(plutovg_rect_t* rect, double x, double y, double w, double h);
|
||||
void plutovg_rect_init_zero(plutovg_rect_t* rect);
|
||||
|
||||
typedef struct {
|
||||
double m00; double m10;
|
||||
double m01; double m11;
|
||||
double m02; double m12;
|
||||
} plutovg_matrix_t;
|
||||
|
||||
void plutovg_matrix_init(plutovg_matrix_t* matrix, double m00, double m10, double m01, double m11, double m02, double m12);
|
||||
void plutovg_matrix_init_identity(plutovg_matrix_t* matrix);
|
||||
void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_init_scale(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_init_shear(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_init_rotate(plutovg_matrix_t* matrix, double radians, double x, double y);
|
||||
void plutovg_matrix_translate(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_scale(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_shear(plutovg_matrix_t* matrix, double x, double y);
|
||||
void plutovg_matrix_rotate(plutovg_matrix_t* matrix, double radians, double x, double y);
|
||||
void plutovg_matrix_multiply(plutovg_matrix_t* matrix, const plutovg_matrix_t* a, const plutovg_matrix_t* b);
|
||||
int plutovg_matrix_invert(plutovg_matrix_t* matrix);
|
||||
void plutovg_matrix_map(const plutovg_matrix_t* matrix, double x, double y, double* _x, double* _y);
|
||||
void plutovg_matrix_map_point(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst);
|
||||
void plutovg_matrix_map_rect(const plutovg_matrix_t* matrix, const plutovg_rect_t* src, plutovg_rect_t* dst);
|
||||
|
||||
typedef struct plutovg_path plutovg_path_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_path_element_move_to,
|
||||
plutovg_path_element_line_to,
|
||||
plutovg_path_element_cubic_to,
|
||||
plutovg_path_element_close
|
||||
} plutovg_path_element_t;
|
||||
|
||||
plutovg_path_t* plutovg_path_create(void);
|
||||
plutovg_path_t* plutovg_path_reference(plutovg_path_t* path);
|
||||
void plutovg_path_destroy(plutovg_path_t* path);
|
||||
int plutovg_path_get_reference_count(const plutovg_path_t* path);
|
||||
void plutovg_path_move_to(plutovg_path_t* path, double x, double y);
|
||||
void plutovg_path_line_to(plutovg_path_t* path, double x, double y);
|
||||
void plutovg_path_quad_to(plutovg_path_t* path, double x1, double y1, double x2, double y2);
|
||||
void plutovg_path_cubic_to(plutovg_path_t* path, double x1, double y1, double x2, double y2, double x3, double y3);
|
||||
void plutovg_path_close(plutovg_path_t* path);
|
||||
void plutovg_path_rel_move_to(plutovg_path_t* path, double x, double y);
|
||||
void plutovg_path_rel_line_to(plutovg_path_t* path, double x, double y);
|
||||
void plutovg_path_rel_quad_to(plutovg_path_t* path, double x1, double y1, double x2, double y2);
|
||||
void plutovg_path_rel_cubic_to(plutovg_path_t* path, double x1, double y1, double x2, double y2, double x3, double y3);
|
||||
void plutovg_path_add_rect(plutovg_path_t* path, double x, double y, double w, double h);
|
||||
void plutovg_path_add_round_rect(plutovg_path_t* path, double x, double y, double w, double h, double rx, double ry);
|
||||
void plutovg_path_add_ellipse(plutovg_path_t* path, double cx, double cy, double rx, double ry);
|
||||
void plutovg_path_add_circle(plutovg_path_t* path, double cx, double cy, double r);
|
||||
void plutovg_path_add_path(plutovg_path_t* path, const plutovg_path_t* source, const plutovg_matrix_t* matrix);
|
||||
void plutovg_path_transform(plutovg_path_t* path, const plutovg_matrix_t* matrix);
|
||||
void plutovg_path_get_current_point(const plutovg_path_t* path, double* x, double* y);
|
||||
int plutovg_path_get_element_count(const plutovg_path_t* path);
|
||||
plutovg_path_element_t* plutovg_path_get_elements(const plutovg_path_t* path);
|
||||
int plutovg_path_get_point_count(const plutovg_path_t* path);
|
||||
plutovg_point_t* plutovg_path_get_points(const plutovg_path_t* path);
|
||||
void plutovg_path_clear(plutovg_path_t* path);
|
||||
int plutovg_path_empty(const plutovg_path_t* path);
|
||||
plutovg_path_t* plutovg_path_clone(const plutovg_path_t* path);
|
||||
plutovg_path_t* plutovg_path_clone_flat(const plutovg_path_t* path);
|
||||
|
||||
typedef struct {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
} plutovg_color_t;
|
||||
|
||||
void plutovg_color_init_rgb(plutovg_color_t* color, double r, double g, double b);
|
||||
void plutovg_color_init_rgba(plutovg_color_t* color, double r, double g, double b, double a);
|
||||
|
||||
typedef enum {
|
||||
plutovg_spread_method_pad,
|
||||
plutovg_spread_method_reflect,
|
||||
plutovg_spread_method_repeat
|
||||
} plutovg_spread_method_t;
|
||||
|
||||
typedef struct plutovg_gradient plutovg_gradient_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_gradient_type_linear,
|
||||
plutovg_gradient_type_radial
|
||||
} plutovg_gradient_type_t;
|
||||
|
||||
typedef struct {
|
||||
double offset;
|
||||
plutovg_color_t color;
|
||||
} plutovg_gradient_stop_t;
|
||||
|
||||
plutovg_gradient_t* plutovg_gradient_create_linear(double x1, double y1, double x2, double y2);
|
||||
plutovg_gradient_t* plutovg_gradient_create_radial(double cx, double cy, double cr, double fx, double fy, double fr);
|
||||
plutovg_gradient_t* plutovg_gradient_reference(plutovg_gradient_t* gradient);
|
||||
void plutovg_gradient_destroy(plutovg_gradient_t* gradient);
|
||||
int plutovg_gradient_get_reference_count(const plutovg_gradient_t* gradient);
|
||||
void plutovg_gradient_set_type(plutovg_gradient_t* gradient, plutovg_gradient_type_t type);
|
||||
plutovg_gradient_type_t plutovg_gradient_get_type(const plutovg_gradient_t* gradient);
|
||||
void plutovg_gradient_set_spread(plutovg_gradient_t* gradient, plutovg_spread_method_t spread);
|
||||
plutovg_spread_method_t plutovg_gradient_get_spread(const plutovg_gradient_t* gradient);
|
||||
void plutovg_gradient_set_matrix(plutovg_gradient_t* gradient, const plutovg_matrix_t* matrix);
|
||||
void plutovg_gradient_get_matrix(const plutovg_gradient_t* gradient, plutovg_matrix_t* matrix);
|
||||
void plutovg_gradient_add_stop_rgb(plutovg_gradient_t* gradient, double offset, double r, double g, double b);
|
||||
void plutovg_gradient_add_stop_rgba(plutovg_gradient_t* gradient, double offset, double r, double g, double b, double a);
|
||||
void plutovg_gradient_add_stop(plutovg_gradient_t* gradient, const plutovg_gradient_stop_t* stop);
|
||||
void plutovg_gradient_clear_stops(plutovg_gradient_t* gradient);
|
||||
int plutovg_gradient_get_stop_count(const plutovg_gradient_t* gradient);
|
||||
plutovg_gradient_stop_t* plutovg_gradient_get_stops(const plutovg_gradient_t* gradient);
|
||||
void plutovg_gradient_get_values_linear(const plutovg_gradient_t* gradient, double* x1, double* y1, double* x2, double* y2);
|
||||
void plutovg_gradient_get_values_radial(const plutovg_gradient_t* gradient, double* cx, double* cy, double* cr, double* fx, double* fy, double* fr);
|
||||
void plutovg_gradient_set_values_linear(plutovg_gradient_t* gradient, double x1, double y1, double x2, double y2);
|
||||
void plutovg_gradient_set_values_radial(plutovg_gradient_t* gradient, double cx, double cy, double cr, double fx, double fy, double fr);
|
||||
void plutovg_gradient_set_opacity(plutovg_gradient_t* paint, double opacity);
|
||||
double plutovg_gradient_get_opacity(const plutovg_gradient_t* paint);
|
||||
|
||||
typedef struct plutovg_texture plutovg_texture_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_texture_type_plain,
|
||||
plutovg_texture_type_tiled
|
||||
} plutovg_texture_type_t;
|
||||
|
||||
plutovg_texture_t* plutovg_texture_create(plutovg_surface_t* surface);
|
||||
plutovg_texture_t* plutovg_texture_reference(plutovg_texture_t* texture);
|
||||
void plutovg_texture_destroy(plutovg_texture_t* texture);
|
||||
int plutovg_texture_get_reference_count(const plutovg_texture_t* texture);
|
||||
void plutovg_texture_set_type(plutovg_texture_t* texture, plutovg_texture_type_t type);
|
||||
plutovg_texture_type_t plutovg_texture_get_type(const plutovg_texture_t* texture);
|
||||
void plutovg_texture_set_matrix(plutovg_texture_t* texture, const plutovg_matrix_t* matrix);
|
||||
void plutovg_texture_get_matrix(const plutovg_texture_t* texture, plutovg_matrix_t* matrix);
|
||||
void plutovg_texture_set_surface(plutovg_texture_t* texture, plutovg_surface_t* surface);
|
||||
plutovg_surface_t* plutovg_texture_get_surface(const plutovg_texture_t* texture);
|
||||
void plutovg_texture_set_opacity(plutovg_texture_t* texture, double opacity);
|
||||
double plutovg_texture_get_opacity(const plutovg_texture_t* texture);
|
||||
|
||||
typedef struct plutovg_paint plutovg_paint_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_paint_type_color,
|
||||
plutovg_paint_type_gradient,
|
||||
plutovg_paint_type_texture
|
||||
} plutovg_paint_type_t;
|
||||
|
||||
plutovg_paint_t* plutovg_paint_create_rgb(double r, double g, double b);
|
||||
plutovg_paint_t* plutovg_paint_create_rgba(double r, double g, double b, double a);
|
||||
plutovg_paint_t* plutovg_paint_create_linear(double x1, double y1, double x2, double y2);
|
||||
plutovg_paint_t* plutovg_paint_create_radial(double cx, double cy, double cr, double fx, double fy, double fr);
|
||||
plutovg_paint_t* plutovg_paint_create_for_surface(plutovg_surface_t* surface);
|
||||
plutovg_paint_t* plutovg_paint_create_color(const plutovg_color_t* color);
|
||||
plutovg_paint_t* plutovg_paint_create_gradient(plutovg_gradient_t* gradient);
|
||||
plutovg_paint_t* plutovg_paint_create_texture(plutovg_texture_t* texture);
|
||||
plutovg_paint_t* plutovg_paint_reference(plutovg_paint_t* paint);
|
||||
void plutovg_paint_destroy(plutovg_paint_t* paint);
|
||||
int plutovg_paint_get_reference_count(const plutovg_paint_t* paint);
|
||||
plutovg_paint_type_t plutovg_paint_get_type(const plutovg_paint_t* paint);
|
||||
plutovg_color_t* plutovg_paint_get_color(const plutovg_paint_t* paint);
|
||||
plutovg_gradient_t* plutovg_paint_get_gradient(const plutovg_paint_t* paint);
|
||||
plutovg_texture_t* plutovg_paint_get_texture(const plutovg_paint_t* paint);
|
||||
|
||||
typedef enum {
|
||||
plutovg_line_cap_butt,
|
||||
plutovg_line_cap_round,
|
||||
plutovg_line_cap_square
|
||||
} plutovg_line_cap_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_line_join_miter,
|
||||
plutovg_line_join_round,
|
||||
plutovg_line_join_bevel
|
||||
} plutovg_line_join_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_fill_rule_non_zero,
|
||||
plutovg_fill_rule_even_odd
|
||||
} plutovg_fill_rule_t;
|
||||
|
||||
typedef enum {
|
||||
plutovg_operator_src,
|
||||
plutovg_operator_src_over,
|
||||
plutovg_operator_dst_in,
|
||||
plutovg_operator_dst_out
|
||||
} plutovg_operator_t;
|
||||
|
||||
typedef struct plutovg plutovg_t;
|
||||
|
||||
plutovg_t* plutovg_create(plutovg_surface_t* surface);
|
||||
plutovg_t* plutovg_reference(plutovg_t* pluto);
|
||||
void plutovg_destroy(plutovg_t* pluto);
|
||||
int plutovg_get_reference_count(const plutovg_t* pluto);
|
||||
void plutovg_save(plutovg_t* pluto);
|
||||
void plutovg_restore(plutovg_t* pluto);
|
||||
void plutovg_set_source_rgb(plutovg_t* pluto, double r, double g, double b);
|
||||
void plutovg_set_source_rgba(plutovg_t* pluto, double r, double g, double b, double a);
|
||||
void plutovg_set_source_surface(plutovg_t* pluto, plutovg_surface_t* surface, double x, double y);
|
||||
void plutovg_set_source_color(plutovg_t* pluto, const plutovg_color_t* color);
|
||||
void plutovg_set_source_gradient(plutovg_t* pluto, plutovg_gradient_t* gradient);
|
||||
void plutovg_set_source_texture(plutovg_t* pluto, plutovg_texture_t* texture);
|
||||
void plutovg_set_source(plutovg_t* pluto, plutovg_paint_t* source);
|
||||
plutovg_paint_t* plutovg_get_source(const plutovg_t* pluto);
|
||||
|
||||
void plutovg_set_operator(plutovg_t* pluto, plutovg_operator_t op);
|
||||
void plutovg_set_opacity(plutovg_t* pluto, double opacity);
|
||||
void plutovg_set_fill_rule(plutovg_t* pluto, plutovg_fill_rule_t fill_rule);
|
||||
plutovg_operator_t plutovg_get_operator(const plutovg_t* pluto);
|
||||
double plutovg_get_opacity(const plutovg_t* pluto);
|
||||
plutovg_fill_rule_t plutovg_get_fill_rule(const plutovg_t* pluto);
|
||||
|
||||
void plutovg_set_line_width(plutovg_t* pluto, double width);
|
||||
void plutovg_set_line_cap(plutovg_t* pluto, plutovg_line_cap_t cap);
|
||||
void plutovg_set_line_join(plutovg_t* pluto, plutovg_line_join_t join);
|
||||
void plutovg_set_miter_limit(plutovg_t* pluto, double limit);
|
||||
void plutovg_set_dash(plutovg_t* pluto, double offset, const double* data, int size);
|
||||
double plutovg_get_line_width(const plutovg_t* pluto);
|
||||
plutovg_line_cap_t plutovg_get_line_cap(const plutovg_t* pluto);
|
||||
plutovg_line_join_t plutovg_get_line_join(const plutovg_t* pluto);
|
||||
double plutovg_get_miter_limit(const plutovg_t* pluto);
|
||||
|
||||
void plutovg_translate(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_scale(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_rotate(plutovg_t* pluto, double radians, double x, double y);
|
||||
void plutovg_transform(plutovg_t* pluto, const plutovg_matrix_t* matrix);
|
||||
void plutovg_set_matrix(plutovg_t* pluto, const plutovg_matrix_t* matrix);
|
||||
void plutovg_identity_matrix(plutovg_t* pluto);
|
||||
void plutovg_get_matrix(const plutovg_t* pluto, plutovg_matrix_t* matrix);
|
||||
|
||||
void plutovg_move_to(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_line_to(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_quad_to(plutovg_t* pluto, double x1, double y1, double x2, double y2);
|
||||
void plutovg_cubic_to(plutovg_t* pluto, double x1, double y1, double x2, double y2, double x3, double y3);
|
||||
void plutovg_rel_move_to(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_rel_line_to(plutovg_t* pluto, double x, double y);
|
||||
void plutovg_rel_quad_to(plutovg_t* pluto, double x1, double y1, double x2, double y2);
|
||||
void plutovg_rel_cubic_to(plutovg_t* pluto, double x1, double y1, double x2, double y2, double x3, double y3);
|
||||
void plutovg_rect(plutovg_t* pluto, double x, double y, double w, double h);
|
||||
void plutovg_round_rect(plutovg_t* pluto, double x, double y, double w, double h, double rx, double ry);
|
||||
void plutovg_ellipse(plutovg_t* pluto, double cx, double cy, double rx, double ry);
|
||||
void plutovg_circle(plutovg_t* pluto, double cx, double cy, double r);
|
||||
void plutovg_add_path(plutovg_t* pluto, const plutovg_path_t* path);
|
||||
void plutovg_new_path(plutovg_t* pluto);
|
||||
void plutovg_close_path(plutovg_t* pluto);
|
||||
plutovg_path_t* plutovg_get_path(const plutovg_t* pluto);
|
||||
|
||||
void plutovg_fill(plutovg_t* pluto);
|
||||
void plutovg_stroke(plutovg_t* pluto);
|
||||
void plutovg_clip(plutovg_t* pluto);
|
||||
void plutovg_paint(plutovg_t* pluto);
|
||||
|
||||
void plutovg_fill_preserve(plutovg_t* pluto);
|
||||
void plutovg_stroke_preserve(plutovg_t* pluto);
|
||||
void plutovg_clip_preserve(plutovg_t* pluto);
|
||||
void plutovg_reset_clip(plutovg_t* pluto);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PLUTOVG_H
|
11
3rdparty/software/CMakeLists.txt
vendored
Executable file
11
3rdparty/software/CMakeLists.txt
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
target_sources(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/sw_ft_math.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/sw_ft_raster.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/sw_ft_stroker.c"
|
||||
)
|
||||
|
||||
target_include_directories(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
166
3rdparty/software/FTL.TXT
vendored
Normal file
166
3rdparty/software/FTL.TXT
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
The FreeType Project LICENSE
|
||||
----------------------------
|
||||
|
||||
2006-Jan-27
|
||||
|
||||
Copyright 1996-2002, 2006 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg
|
||||
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The FreeType Project is distributed in several archive packages;
|
||||
some of them may contain, in addition to the FreeType font engine,
|
||||
various tools and contributions which rely on, or relate to, the
|
||||
FreeType Project.
|
||||
|
||||
This license applies to all files found in such packages, and
|
||||
which do not fall under their own explicit license. The license
|
||||
affects thus the FreeType font engine, the test programs,
|
||||
documentation and makefiles, at the very least.
|
||||
|
||||
This license was inspired by the BSD, Artistic, and IJG
|
||||
(Independent JPEG Group) licenses, which all encourage inclusion
|
||||
and use of free software in commercial and freeware products
|
||||
alike. As a consequence, its main points are that:
|
||||
|
||||
o We don't promise that this software works. However, we will be
|
||||
interested in any kind of bug reports. (`as is' distribution)
|
||||
|
||||
o You can use this software for whatever you want, in parts or
|
||||
full form, without having to pay us. (`royalty-free' usage)
|
||||
|
||||
o You may not pretend that you wrote this software. If you use
|
||||
it, or only parts of it, in a program, you must acknowledge
|
||||
somewhere in your documentation that you have used the
|
||||
FreeType code. (`credits')
|
||||
|
||||
We specifically permit and encourage the inclusion of this
|
||||
software, with or without modifications, in commercial products.
|
||||
We disclaim all warranties covering The FreeType Project and
|
||||
assume no liability related to The FreeType Project.
|
||||
|
||||
|
||||
Finally, many people asked us for a preferred form for a
|
||||
credit/disclaimer to use in compliance with this license. We thus
|
||||
encourage you to use the following text:
|
||||
|
||||
"""
|
||||
Portions of this software are copyright <20> <year> The FreeType
|
||||
Project (www.freetype.org). All rights reserved.
|
||||
"""
|
||||
|
||||
Please replace <year> with the value from the FreeType version you
|
||||
actually use.
|
||||
|
||||
|
||||
Legal Terms
|
||||
===========
|
||||
|
||||
0. Definitions
|
||||
--------------
|
||||
|
||||
Throughout this license, the terms `package', `FreeType Project',
|
||||
and `FreeType archive' refer to the set of files originally
|
||||
distributed by the authors (David Turner, Robert Wilhelm, and
|
||||
Werner Lemberg) as the `FreeType Project', be they named as alpha,
|
||||
beta or final release.
|
||||
|
||||
`You' refers to the licensee, or person using the project, where
|
||||
`using' is a generic term including compiling the project's source
|
||||
code as well as linking it to form a `program' or `executable'.
|
||||
This program is referred to as `a program using the FreeType
|
||||
engine'.
|
||||
|
||||
This license applies to all files distributed in the original
|
||||
FreeType Project, including all source code, binaries and
|
||||
documentation, unless otherwise stated in the file in its
|
||||
original, unmodified form as distributed in the original archive.
|
||||
If you are unsure whether or not a particular file is covered by
|
||||
this license, you must contact us to verify this.
|
||||
|
||||
The FreeType Project is copyright (C) 1996-2000 by David Turner,
|
||||
Robert Wilhelm, and Werner Lemberg. All rights reserved except as
|
||||
specified below.
|
||||
|
||||
1. No Warranty
|
||||
--------------
|
||||
|
||||
THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
|
||||
USE, OF THE FREETYPE PROJECT.
|
||||
|
||||
2. Redistribution
|
||||
-----------------
|
||||
|
||||
This license grants a worldwide, royalty-free, perpetual and
|
||||
irrevocable right and license to use, execute, perform, compile,
|
||||
display, copy, create derivative works of, distribute and
|
||||
sublicense the FreeType Project (in both source and object code
|
||||
forms) and derivative works thereof for any purpose; and to
|
||||
authorize others to exercise some or all of the rights granted
|
||||
herein, subject to the following conditions:
|
||||
|
||||
o Redistribution of source code must retain this license file
|
||||
(`FTL.TXT') unaltered; any additions, deletions or changes to
|
||||
the original files must be clearly indicated in accompanying
|
||||
documentation. The copyright notices of the unaltered,
|
||||
original files must be preserved in all copies of source
|
||||
files.
|
||||
|
||||
o Redistribution in binary form must provide a disclaimer that
|
||||
states that the software is based in part of the work of the
|
||||
FreeType Team, in the distribution documentation. We also
|
||||
encourage you to put an URL to the FreeType web page in your
|
||||
documentation, though this isn't mandatory.
|
||||
|
||||
These conditions apply to any software derived from or based on
|
||||
the FreeType Project, not just the unmodified files. If you use
|
||||
our work, you must acknowledge us. However, no fee need be paid
|
||||
to us.
|
||||
|
||||
3. Advertising
|
||||
--------------
|
||||
|
||||
Neither the FreeType authors and contributors nor you shall use
|
||||
the name of the other for commercial, advertising, or promotional
|
||||
purposes without specific prior written permission.
|
||||
|
||||
We suggest, but do not require, that you use one or more of the
|
||||
following phrases to refer to this software in your documentation
|
||||
or advertising materials: `FreeType Project', `FreeType Engine',
|
||||
`FreeType library', or `FreeType Distribution'.
|
||||
|
||||
As you have not signed this license, you are not required to
|
||||
accept it. However, as the FreeType Project is copyrighted
|
||||
material, only this license, or another one contracted with the
|
||||
authors, grants you the right to use, distribute, and modify it.
|
||||
Therefore, by using, distributing, or modifying the FreeType
|
||||
Project, you indicate that you understand and accept all the terms
|
||||
of this license.
|
||||
|
||||
4. Contacts
|
||||
-----------
|
||||
|
||||
There are two mailing lists related to FreeType:
|
||||
|
||||
o freetype@nongnu.org
|
||||
|
||||
Discusses general use and applications of FreeType, as well as
|
||||
future and wanted additions to the library and distribution.
|
||||
If you are looking for support, start in this list if you
|
||||
haven't found anything to help you in the documentation.
|
||||
|
||||
o freetype-devel@nongnu.org
|
||||
|
||||
Discusses bugs, as well as engine internals, design issues,
|
||||
specific licenses, porting, etc.
|
||||
|
||||
Our home page can be found at
|
||||
|
||||
http://www.freetype.org
|
461
3rdparty/software/sw_ft_math.c
vendored
Normal file
461
3rdparty/software/sw_ft_math.c
vendored
Normal file
|
@ -0,0 +1,461 @@
|
|||
/***************************************************************************/
|
||||
/* */
|
||||
/* fttrigon.c */
|
||||
/* */
|
||||
/* FreeType trigonometric functions (body). */
|
||||
/* */
|
||||
/* Copyright 2001-2005, 2012-2013 by */
|
||||
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
||||
/* */
|
||||
/* This file is part of the FreeType project, and may only be used, */
|
||||
/* modified, and distributed under the terms of the FreeType project */
|
||||
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
||||
/* this file you indicate that you have read the license and */
|
||||
/* understand and accept it fully. */
|
||||
/* */
|
||||
/***************************************************************************/
|
||||
|
||||
#include "sw_ft_math.h"
|
||||
#include <math.h>
|
||||
|
||||
//form https://github.com/chromium/chromium/blob/59afd8336009c9d97c22854c52e0382b62b3aa5e/third_party/abseil-cpp/absl/base/internal/bits.h
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
static unsigned int __inline clz(unsigned int x) {
|
||||
unsigned long r = 0;
|
||||
if (x != 0)
|
||||
{
|
||||
_BitScanReverse(&r, x);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#define SW_FT_MSB(x) (clz(x))
|
||||
#elif defined(__GNUC__)
|
||||
#define SW_FT_MSB(x) (31 - __builtin_clz(x))
|
||||
#else
|
||||
static unsigned int __inline clz(unsigned int x) {
|
||||
int c = 31;
|
||||
x &= ~x + 1;
|
||||
if (n & 0x0000FFFF) c -= 16;
|
||||
if (n & 0x00FF00FF) c -= 8;
|
||||
if (n & 0x0F0F0F0F) c -= 4;
|
||||
if (n & 0x33333333) c -= 2;
|
||||
if (n & 0x55555555) c -= 1;
|
||||
return c;
|
||||
}
|
||||
#define SW_FT_MSB(x) (clz(x))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define SW_FT_PAD_FLOOR(x, n) ((x) & ~((n)-1))
|
||||
#define SW_FT_PAD_ROUND(x, n) SW_FT_PAD_FLOOR((x) + ((n) / 2), n)
|
||||
#define SW_FT_PAD_CEIL(x, n) SW_FT_PAD_FLOOR((x) + ((n)-1), n)
|
||||
|
||||
#define SW_FT_BEGIN_STMNT do {
|
||||
#define SW_FT_END_STMNT \
|
||||
} \
|
||||
while (0)
|
||||
/* transfer sign leaving a positive number */
|
||||
#define SW_FT_MOVE_SIGN(x, s) \
|
||||
SW_FT_BEGIN_STMNT \
|
||||
if (x < 0) { \
|
||||
x = -x; \
|
||||
s = -s; \
|
||||
} \
|
||||
SW_FT_END_STMNT
|
||||
|
||||
SW_FT_Long SW_FT_MulFix(SW_FT_Long a, SW_FT_Long b)
|
||||
{
|
||||
SW_FT_Int s = 1;
|
||||
SW_FT_Long c;
|
||||
|
||||
SW_FT_MOVE_SIGN(a, s);
|
||||
SW_FT_MOVE_SIGN(b, s);
|
||||
|
||||
c = (SW_FT_Long)(((SW_FT_Int64)a * b + 0x8000L) >> 16);
|
||||
|
||||
return (s > 0) ? c : -c;
|
||||
}
|
||||
|
||||
SW_FT_Long SW_FT_MulDiv(SW_FT_Long a, SW_FT_Long b, SW_FT_Long c)
|
||||
{
|
||||
SW_FT_Int s = 1;
|
||||
SW_FT_Long d;
|
||||
|
||||
SW_FT_MOVE_SIGN(a, s);
|
||||
SW_FT_MOVE_SIGN(b, s);
|
||||
SW_FT_MOVE_SIGN(c, s);
|
||||
|
||||
d = (SW_FT_Long)(c > 0 ? ((SW_FT_Int64)a * b + (c >> 1)) / c : 0x7FFFFFFFL);
|
||||
|
||||
return (s > 0) ? d : -d;
|
||||
}
|
||||
|
||||
SW_FT_Long SW_FT_DivFix(SW_FT_Long a, SW_FT_Long b)
|
||||
{
|
||||
SW_FT_Int s = 1;
|
||||
SW_FT_Long q;
|
||||
|
||||
SW_FT_MOVE_SIGN(a, s);
|
||||
SW_FT_MOVE_SIGN(b, s);
|
||||
|
||||
q = (SW_FT_Long)(b > 0 ? (((SW_FT_UInt64)a << 16) + (b >> 1)) / b
|
||||
: 0x7FFFFFFFL);
|
||||
|
||||
return (s < 0 ? -q : q);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* This is a fixed-point CORDIC implementation of trigonometric */
|
||||
/* functions as well as transformations between Cartesian and polar */
|
||||
/* coordinates. The angles are represented as 16.16 fixed-point values */
|
||||
/* in degrees, i.e., the angular resolution is 2^-16 degrees. Note that */
|
||||
/* only vectors longer than 2^16*180/pi (or at least 22 bits) on a */
|
||||
/* discrete Cartesian grid can have the same or better angular */
|
||||
/* resolution. Therefore, to maintain this precision, some functions */
|
||||
/* require an interim upscaling of the vectors, whereas others operate */
|
||||
/* with 24-bit long vectors directly. */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
|
||||
/* the Cordic shrink factor 0.858785336480436 * 2^32 */
|
||||
#define SW_FT_TRIG_SCALE 0xDBD95B16UL
|
||||
|
||||
/* the highest bit in overflow-safe vector components, */
|
||||
/* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||
#define SW_FT_TRIG_SAFE_MSB 29
|
||||
|
||||
/* this table was generated for SW_FT_PI = 180L << 16, i.e. degrees */
|
||||
#define SW_FT_TRIG_MAX_ITERS 23
|
||||
|
||||
static const SW_FT_Fixed ft_trig_arctan_table[] = {
|
||||
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, 14668L,
|
||||
7334L, 3667L, 1833L, 917L, 458L, 229L, 115L, 57L,
|
||||
29L, 14L, 7L, 4L, 2L, 1L};
|
||||
|
||||
/* multiply a given value by the CORDIC shrink factor */
|
||||
static SW_FT_Fixed ft_trig_downscale(SW_FT_Fixed val)
|
||||
{
|
||||
SW_FT_Fixed s;
|
||||
SW_FT_Int64 v;
|
||||
|
||||
s = val;
|
||||
val = SW_FT_ABS(val);
|
||||
|
||||
v = (val * (SW_FT_Int64)SW_FT_TRIG_SCALE) + 0x100000000UL;
|
||||
val = (SW_FT_Fixed)(v >> 32);
|
||||
|
||||
return (s >= 0) ? val : -val;
|
||||
}
|
||||
|
||||
/* undefined and never called for zero vector */
|
||||
static SW_FT_Int ft_trig_prenorm(SW_FT_Vector* vec)
|
||||
{
|
||||
SW_FT_Pos x, y;
|
||||
SW_FT_Int shift;
|
||||
|
||||
x = vec->x;
|
||||
y = vec->y;
|
||||
|
||||
shift = SW_FT_MSB(SW_FT_ABS(x) | SW_FT_ABS(y));
|
||||
|
||||
if (shift <= SW_FT_TRIG_SAFE_MSB) {
|
||||
shift = SW_FT_TRIG_SAFE_MSB - shift;
|
||||
vec->x = (SW_FT_Pos)((SW_FT_ULong)x << shift);
|
||||
vec->y = (SW_FT_Pos)((SW_FT_ULong)y << shift);
|
||||
} else {
|
||||
shift -= SW_FT_TRIG_SAFE_MSB;
|
||||
vec->x = x >> shift;
|
||||
vec->y = y >> shift;
|
||||
shift = -shift;
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
static void ft_trig_pseudo_rotate(SW_FT_Vector* vec, SW_FT_Angle theta)
|
||||
{
|
||||
SW_FT_Int i;
|
||||
SW_FT_Fixed x, y, xtemp, b;
|
||||
const SW_FT_Fixed* arctanptr;
|
||||
|
||||
x = vec->x;
|
||||
y = vec->y;
|
||||
|
||||
/* Rotate inside [-PI/4,PI/4] sector */
|
||||
while (theta < -SW_FT_ANGLE_PI4) {
|
||||
xtemp = y;
|
||||
y = -x;
|
||||
x = xtemp;
|
||||
theta += SW_FT_ANGLE_PI2;
|
||||
}
|
||||
|
||||
while (theta > SW_FT_ANGLE_PI4) {
|
||||
xtemp = -y;
|
||||
y = x;
|
||||
x = xtemp;
|
||||
theta -= SW_FT_ANGLE_PI2;
|
||||
}
|
||||
|
||||
arctanptr = ft_trig_arctan_table;
|
||||
|
||||
/* Pseudorotations, with right shifts */
|
||||
for (i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++) {
|
||||
SW_FT_Fixed v1 = ((y + b) >> i);
|
||||
SW_FT_Fixed v2 = ((x + b) >> i);
|
||||
if (theta < 0) {
|
||||
xtemp = x + v1;
|
||||
y = y - v2;
|
||||
x = xtemp;
|
||||
theta += *arctanptr++;
|
||||
} else {
|
||||
xtemp = x - v1;
|
||||
y = y + v2;
|
||||
x = xtemp;
|
||||
theta -= *arctanptr++;
|
||||
}
|
||||
}
|
||||
|
||||
vec->x = x;
|
||||
vec->y = y;
|
||||
}
|
||||
|
||||
static void ft_trig_pseudo_polarize(SW_FT_Vector* vec)
|
||||
{
|
||||
SW_FT_Angle theta;
|
||||
SW_FT_Int i;
|
||||
SW_FT_Fixed x, y, xtemp, b;
|
||||
const SW_FT_Fixed* arctanptr;
|
||||
|
||||
x = vec->x;
|
||||
y = vec->y;
|
||||
|
||||
/* Get the vector into [-PI/4,PI/4] sector */
|
||||
if (y > x) {
|
||||
if (y > -x) {
|
||||
theta = SW_FT_ANGLE_PI2;
|
||||
xtemp = y;
|
||||
y = -x;
|
||||
x = xtemp;
|
||||
} else {
|
||||
theta = y > 0 ? SW_FT_ANGLE_PI : -SW_FT_ANGLE_PI;
|
||||
x = -x;
|
||||
y = -y;
|
||||
}
|
||||
} else {
|
||||
if (y < -x) {
|
||||
theta = -SW_FT_ANGLE_PI2;
|
||||
xtemp = -y;
|
||||
y = x;
|
||||
x = xtemp;
|
||||
} else {
|
||||
theta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
arctanptr = ft_trig_arctan_table;
|
||||
|
||||
/* Pseudorotations, with right shifts */
|
||||
for (i = 1, b = 1; i < SW_FT_TRIG_MAX_ITERS; b <<= 1, i++) {
|
||||
SW_FT_Fixed v1 = ((y + b) >> i);
|
||||
SW_FT_Fixed v2 = ((x + b) >> i);
|
||||
if (y > 0) {
|
||||
xtemp = x + v1;
|
||||
y = y - v2;
|
||||
x = xtemp;
|
||||
theta += *arctanptr++;
|
||||
} else {
|
||||
xtemp = x - v1;
|
||||
y = y + v2;
|
||||
x = xtemp;
|
||||
theta -= *arctanptr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* round theta */
|
||||
if (theta >= 0)
|
||||
theta = SW_FT_PAD_ROUND(theta, 32);
|
||||
else
|
||||
theta = -SW_FT_PAD_ROUND(-theta, 32);
|
||||
|
||||
vec->x = x;
|
||||
vec->y = theta;
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Fixed SW_FT_Cos(SW_FT_Angle angle)
|
||||
{
|
||||
SW_FT_Vector v;
|
||||
|
||||
v.x = SW_FT_TRIG_SCALE >> 8;
|
||||
v.y = 0;
|
||||
ft_trig_pseudo_rotate(&v, angle);
|
||||
|
||||
return (v.x + 0x80L) >> 8;
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Fixed SW_FT_Sin(SW_FT_Angle angle)
|
||||
{
|
||||
return SW_FT_Cos(SW_FT_ANGLE_PI2 - angle);
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Fixed SW_FT_Tan(SW_FT_Angle angle)
|
||||
{
|
||||
SW_FT_Vector v;
|
||||
|
||||
v.x = SW_FT_TRIG_SCALE >> 8;
|
||||
v.y = 0;
|
||||
ft_trig_pseudo_rotate(&v, angle);
|
||||
|
||||
return SW_FT_DivFix(v.y, v.x);
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Angle SW_FT_Atan2(SW_FT_Fixed dx, SW_FT_Fixed dy)
|
||||
{
|
||||
SW_FT_Vector v;
|
||||
|
||||
if (dx == 0 && dy == 0) return 0;
|
||||
|
||||
v.x = dx;
|
||||
v.y = dy;
|
||||
ft_trig_prenorm(&v);
|
||||
ft_trig_pseudo_polarize(&v);
|
||||
|
||||
return v.y;
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
void SW_FT_Vector_Unit(SW_FT_Vector* vec, SW_FT_Angle angle)
|
||||
{
|
||||
vec->x = SW_FT_TRIG_SCALE >> 8;
|
||||
vec->y = 0;
|
||||
ft_trig_pseudo_rotate(vec, angle);
|
||||
vec->x = (vec->x + 0x80L) >> 8;
|
||||
vec->y = (vec->y + 0x80L) >> 8;
|
||||
}
|
||||
|
||||
/* these macros return 0 for positive numbers,
|
||||
and -1 for negative ones */
|
||||
#define SW_FT_SIGN_LONG(x) ((x) >> (SW_FT_SIZEOF_LONG * 8 - 1))
|
||||
#define SW_FT_SIGN_INT(x) ((x) >> (SW_FT_SIZEOF_INT * 8 - 1))
|
||||
#define SW_FT_SIGN_INT32(x) ((x) >> 31)
|
||||
#define SW_FT_SIGN_INT16(x) ((x) >> 15)
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
void SW_FT_Vector_Rotate(SW_FT_Vector* vec, SW_FT_Angle angle)
|
||||
{
|
||||
SW_FT_Int shift;
|
||||
SW_FT_Vector v;
|
||||
|
||||
v.x = vec->x;
|
||||
v.y = vec->y;
|
||||
|
||||
if (angle && (v.x != 0 || v.y != 0)) {
|
||||
shift = ft_trig_prenorm(&v);
|
||||
ft_trig_pseudo_rotate(&v, angle);
|
||||
v.x = ft_trig_downscale(v.x);
|
||||
v.y = ft_trig_downscale(v.y);
|
||||
|
||||
if (shift > 0) {
|
||||
SW_FT_Int32 half = (SW_FT_Int32)1L << (shift - 1);
|
||||
|
||||
vec->x = (v.x + half + SW_FT_SIGN_LONG(v.x)) >> shift;
|
||||
vec->y = (v.y + half + SW_FT_SIGN_LONG(v.y)) >> shift;
|
||||
} else {
|
||||
shift = -shift;
|
||||
vec->x = (SW_FT_Pos)((SW_FT_ULong)v.x << shift);
|
||||
vec->y = (SW_FT_Pos)((SW_FT_ULong)v.y << shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Fixed SW_FT_Vector_Length(SW_FT_Vector* vec)
|
||||
{
|
||||
SW_FT_Int shift;
|
||||
SW_FT_Vector v;
|
||||
|
||||
v = *vec;
|
||||
|
||||
/* handle trivial cases */
|
||||
if (v.x == 0) {
|
||||
return SW_FT_ABS(v.y);
|
||||
} else if (v.y == 0) {
|
||||
return SW_FT_ABS(v.x);
|
||||
}
|
||||
|
||||
/* general case */
|
||||
shift = ft_trig_prenorm(&v);
|
||||
ft_trig_pseudo_polarize(&v);
|
||||
|
||||
v.x = ft_trig_downscale(v.x);
|
||||
|
||||
if (shift > 0) return (v.x + (1 << (shift - 1))) >> shift;
|
||||
|
||||
return (SW_FT_Fixed)((SW_FT_UInt32)v.x << -shift);
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
void SW_FT_Vector_Polarize(SW_FT_Vector* vec, SW_FT_Fixed* length,
|
||||
SW_FT_Angle* angle)
|
||||
{
|
||||
SW_FT_Int shift;
|
||||
SW_FT_Vector v;
|
||||
|
||||
v = *vec;
|
||||
|
||||
if (v.x == 0 && v.y == 0) return;
|
||||
|
||||
shift = ft_trig_prenorm(&v);
|
||||
ft_trig_pseudo_polarize(&v);
|
||||
|
||||
v.x = ft_trig_downscale(v.x);
|
||||
|
||||
*length = (shift >= 0) ? (v.x >> shift)
|
||||
: (SW_FT_Fixed)((SW_FT_UInt32)v.x << -shift);
|
||||
*angle = v.y;
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
void SW_FT_Vector_From_Polar(SW_FT_Vector* vec, SW_FT_Fixed length,
|
||||
SW_FT_Angle angle)
|
||||
{
|
||||
vec->x = length;
|
||||
vec->y = 0;
|
||||
|
||||
SW_FT_Vector_Rotate(vec, angle);
|
||||
}
|
||||
|
||||
/* documentation is in fttrigon.h */
|
||||
|
||||
SW_FT_Angle SW_FT_Angle_Diff( SW_FT_Angle angle1, SW_FT_Angle angle2 )
|
||||
{
|
||||
SW_FT_Angle delta = angle2 - angle1;
|
||||
|
||||
while ( delta <= -SW_FT_ANGLE_PI )
|
||||
delta += SW_FT_ANGLE_2PI;
|
||||
|
||||
while ( delta > SW_FT_ANGLE_PI )
|
||||
delta -= SW_FT_ANGLE_2PI;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
/* END */
|
438
3rdparty/software/sw_ft_math.h
vendored
Normal file
438
3rdparty/software/sw_ft_math.h
vendored
Normal file
|
@ -0,0 +1,438 @@
|
|||
#ifndef SW_FT_MATH_H
|
||||
#define SW_FT_MATH_H
|
||||
|
||||
/***************************************************************************/
|
||||
/* */
|
||||
/* fttrigon.h */
|
||||
/* */
|
||||
/* FreeType trigonometric functions (specification). */
|
||||
/* */
|
||||
/* Copyright 2001, 2003, 2005, 2007, 2013 by */
|
||||
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
||||
/* */
|
||||
/* This file is part of the FreeType project, and may only be used, */
|
||||
/* modified, and distributed under the terms of the FreeType project */
|
||||
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
||||
/* this file you indicate that you have read the license and */
|
||||
/* understand and accept it fully. */
|
||||
/* */
|
||||
/***************************************************************************/
|
||||
|
||||
#include "sw_ft_types.h"
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* The min and max functions missing in C. As usual, be careful not to */
|
||||
/* write things like SW_FT_MIN( a++, b++ ) to avoid side effects. */
|
||||
/* */
|
||||
#define SW_FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) )
|
||||
#define SW_FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) )
|
||||
|
||||
#define SW_FT_ABS( a ) ( (a) < 0 ? -(a) : (a) )
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#define SW_FT_HYPOT( x, y ) \
|
||||
( x = SW_FT_ABS( x ), \
|
||||
y = SW_FT_ABS( y ), \
|
||||
x > y ? x + ( 3 * y >> 3 ) \
|
||||
: y + ( 3 * x >> 3 ) )
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* SW_FT_MulFix */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A very simple function used to perform the computation */
|
||||
/* `(a*b)/0x10000' with maximum accuracy. Most of the time this is */
|
||||
/* used to multiply a given value by a 16.16 fixed-point factor. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* a :: The first multiplier. */
|
||||
/* b :: The second multiplier. Use a 16.16 factor here whenever */
|
||||
/* possible (see note below). */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* The result of `(a*b)/0x10000'. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* This function has been optimized for the case where the absolute */
|
||||
/* value of `a' is less than 2048, and `b' is a 16.16 scaling factor. */
|
||||
/* As this happens mainly when scaling from notional units to */
|
||||
/* fractional pixels in FreeType, it resulted in noticeable speed */
|
||||
/* improvements between versions 2.x and 1.x. */
|
||||
/* */
|
||||
/* As a conclusion, always try to place a 16.16 factor as the */
|
||||
/* _second_ argument of this function; this can make a great */
|
||||
/* difference. */
|
||||
/* */
|
||||
SW_FT_Long
|
||||
SW_FT_MulFix( SW_FT_Long a,
|
||||
SW_FT_Long b );
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* SW_FT_MulDiv */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A very simple function used to perform the computation `(a*b)/c' */
|
||||
/* with maximum accuracy (it uses a 64-bit intermediate integer */
|
||||
/* whenever necessary). */
|
||||
/* */
|
||||
/* This function isn't necessarily as fast as some processor specific */
|
||||
/* operations, but is at least completely portable. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* a :: The first multiplier. */
|
||||
/* b :: The second multiplier. */
|
||||
/* c :: The divisor. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* The result of `(a*b)/c'. This function never traps when trying to */
|
||||
/* divide by zero; it simply returns `MaxInt' or `MinInt' depending */
|
||||
/* on the signs of `a' and `b'. */
|
||||
/* */
|
||||
SW_FT_Long
|
||||
SW_FT_MulDiv( SW_FT_Long a,
|
||||
SW_FT_Long b,
|
||||
SW_FT_Long c );
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* SW_FT_DivFix */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A very simple function used to perform the computation */
|
||||
/* `(a*0x10000)/b' with maximum accuracy. Most of the time, this is */
|
||||
/* used to divide a given value by a 16.16 fixed-point factor. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* a :: The numerator. */
|
||||
/* b :: The denominator. Use a 16.16 factor here. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* The result of `(a*0x10000)/b'. */
|
||||
/* */
|
||||
SW_FT_Long
|
||||
SW_FT_DivFix( SW_FT_Long a,
|
||||
SW_FT_Long b );
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Section> */
|
||||
/* computations */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @type:
|
||||
* SW_FT_Angle
|
||||
*
|
||||
* @description:
|
||||
* This type is used to model angle values in FreeType. Note that the
|
||||
* angle is a 16.16 fixed-point value expressed in degrees.
|
||||
*
|
||||
*/
|
||||
typedef SW_FT_Fixed SW_FT_Angle;
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @macro:
|
||||
* SW_FT_ANGLE_PI
|
||||
*
|
||||
* @description:
|
||||
* The angle pi expressed in @SW_FT_Angle units.
|
||||
*
|
||||
*/
|
||||
#define SW_FT_ANGLE_PI ( 180L << 16 )
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @macro:
|
||||
* SW_FT_ANGLE_2PI
|
||||
*
|
||||
* @description:
|
||||
* The angle 2*pi expressed in @SW_FT_Angle units.
|
||||
*
|
||||
*/
|
||||
#define SW_FT_ANGLE_2PI ( SW_FT_ANGLE_PI * 2 )
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @macro:
|
||||
* SW_FT_ANGLE_PI2
|
||||
*
|
||||
* @description:
|
||||
* The angle pi/2 expressed in @SW_FT_Angle units.
|
||||
*
|
||||
*/
|
||||
#define SW_FT_ANGLE_PI2 ( SW_FT_ANGLE_PI / 2 )
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @macro:
|
||||
* SW_FT_ANGLE_PI4
|
||||
*
|
||||
* @description:
|
||||
* The angle pi/4 expressed in @SW_FT_Angle units.
|
||||
*
|
||||
*/
|
||||
#define SW_FT_ANGLE_PI4 ( SW_FT_ANGLE_PI / 4 )
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Sin
|
||||
*
|
||||
* @description:
|
||||
* Return the sinus of a given angle in fixed-point format.
|
||||
*
|
||||
* @input:
|
||||
* angle ::
|
||||
* The input angle.
|
||||
*
|
||||
* @return:
|
||||
* The sinus value.
|
||||
*
|
||||
* @note:
|
||||
* If you need both the sinus and cosinus for a given angle, use the
|
||||
* function @SW_FT_Vector_Unit.
|
||||
*
|
||||
*/
|
||||
SW_FT_Fixed
|
||||
SW_FT_Sin( SW_FT_Angle angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Cos
|
||||
*
|
||||
* @description:
|
||||
* Return the cosinus of a given angle in fixed-point format.
|
||||
*
|
||||
* @input:
|
||||
* angle ::
|
||||
* The input angle.
|
||||
*
|
||||
* @return:
|
||||
* The cosinus value.
|
||||
*
|
||||
* @note:
|
||||
* If you need both the sinus and cosinus for a given angle, use the
|
||||
* function @SW_FT_Vector_Unit.
|
||||
*
|
||||
*/
|
||||
SW_FT_Fixed
|
||||
SW_FT_Cos( SW_FT_Angle angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Tan
|
||||
*
|
||||
* @description:
|
||||
* Return the tangent of a given angle in fixed-point format.
|
||||
*
|
||||
* @input:
|
||||
* angle ::
|
||||
* The input angle.
|
||||
*
|
||||
* @return:
|
||||
* The tangent value.
|
||||
*
|
||||
*/
|
||||
SW_FT_Fixed
|
||||
SW_FT_Tan( SW_FT_Angle angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Atan2
|
||||
*
|
||||
* @description:
|
||||
* Return the arc-tangent corresponding to a given vector (x,y) in
|
||||
* the 2d plane.
|
||||
*
|
||||
* @input:
|
||||
* x ::
|
||||
* The horizontal vector coordinate.
|
||||
*
|
||||
* y ::
|
||||
* The vertical vector coordinate.
|
||||
*
|
||||
* @return:
|
||||
* The arc-tangent value (i.e. angle).
|
||||
*
|
||||
*/
|
||||
SW_FT_Angle
|
||||
SW_FT_Atan2( SW_FT_Fixed x,
|
||||
SW_FT_Fixed y );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Angle_Diff
|
||||
*
|
||||
* @description:
|
||||
* Return the difference between two angles. The result is always
|
||||
* constrained to the ]-PI..PI] interval.
|
||||
*
|
||||
* @input:
|
||||
* angle1 ::
|
||||
* First angle.
|
||||
*
|
||||
* angle2 ::
|
||||
* Second angle.
|
||||
*
|
||||
* @return:
|
||||
* Constrained value of `value2-value1'.
|
||||
*
|
||||
*/
|
||||
SW_FT_Angle
|
||||
SW_FT_Angle_Diff( SW_FT_Angle angle1,
|
||||
SW_FT_Angle angle2 );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Vector_Unit
|
||||
*
|
||||
* @description:
|
||||
* Return the unit vector corresponding to a given angle. After the
|
||||
* call, the value of `vec.x' will be `sin(angle)', and the value of
|
||||
* `vec.y' will be `cos(angle)'.
|
||||
*
|
||||
* This function is useful to retrieve both the sinus and cosinus of a
|
||||
* given angle quickly.
|
||||
*
|
||||
* @output:
|
||||
* vec ::
|
||||
* The address of target vector.
|
||||
*
|
||||
* @input:
|
||||
* angle ::
|
||||
* The input angle.
|
||||
*
|
||||
*/
|
||||
void
|
||||
SW_FT_Vector_Unit( SW_FT_Vector* vec,
|
||||
SW_FT_Angle angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Vector_Rotate
|
||||
*
|
||||
* @description:
|
||||
* Rotate a vector by a given angle.
|
||||
*
|
||||
* @inout:
|
||||
* vec ::
|
||||
* The address of target vector.
|
||||
*
|
||||
* @input:
|
||||
* angle ::
|
||||
* The input angle.
|
||||
*
|
||||
*/
|
||||
void
|
||||
SW_FT_Vector_Rotate( SW_FT_Vector* vec,
|
||||
SW_FT_Angle angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Vector_Length
|
||||
*
|
||||
* @description:
|
||||
* Return the length of a given vector.
|
||||
*
|
||||
* @input:
|
||||
* vec ::
|
||||
* The address of target vector.
|
||||
*
|
||||
* @return:
|
||||
* The vector length, expressed in the same units that the original
|
||||
* vector coordinates.
|
||||
*
|
||||
*/
|
||||
SW_FT_Fixed
|
||||
SW_FT_Vector_Length( SW_FT_Vector* vec );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Vector_Polarize
|
||||
*
|
||||
* @description:
|
||||
* Compute both the length and angle of a given vector.
|
||||
*
|
||||
* @input:
|
||||
* vec ::
|
||||
* The address of source vector.
|
||||
*
|
||||
* @output:
|
||||
* length ::
|
||||
* The vector length.
|
||||
*
|
||||
* angle ::
|
||||
* The vector angle.
|
||||
*
|
||||
*/
|
||||
void
|
||||
SW_FT_Vector_Polarize( SW_FT_Vector* vec,
|
||||
SW_FT_Fixed *length,
|
||||
SW_FT_Angle *angle );
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Vector_From_Polar
|
||||
*
|
||||
* @description:
|
||||
* Compute vector coordinates from a length and angle.
|
||||
*
|
||||
* @output:
|
||||
* vec ::
|
||||
* The address of source vector.
|
||||
*
|
||||
* @input:
|
||||
* length ::
|
||||
* The vector length.
|
||||
*
|
||||
* angle ::
|
||||
* The vector angle.
|
||||
*
|
||||
*/
|
||||
void
|
||||
SW_FT_Vector_From_Polar( SW_FT_Vector* vec,
|
||||
SW_FT_Fixed length,
|
||||
SW_FT_Angle angle );
|
||||
|
||||
|
||||
#endif // SW_FT_MATH_H
|
1423
3rdparty/software/sw_ft_raster.c
vendored
Normal file
1423
3rdparty/software/sw_ft_raster.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
607
3rdparty/software/sw_ft_raster.h
vendored
Normal file
607
3rdparty/software/sw_ft_raster.h
vendored
Normal file
|
@ -0,0 +1,607 @@
|
|||
#ifndef SW_FT_IMG_H
|
||||
#define SW_FT_IMG_H
|
||||
/***************************************************************************/
|
||||
/* */
|
||||
/* ftimage.h */
|
||||
/* */
|
||||
/* FreeType glyph image formats and default raster interface */
|
||||
/* (specification). */
|
||||
/* */
|
||||
/* Copyright 1996-2010, 2013 by */
|
||||
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
||||
/* */
|
||||
/* This file is part of the FreeType project, and may only be used, */
|
||||
/* modified, and distributed under the terms of the FreeType project */
|
||||
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
||||
/* this file you indicate that you have read the license and */
|
||||
/* understand and accept it fully. */
|
||||
/* */
|
||||
/***************************************************************************/
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* Note: A `raster' is simply a scan-line converter, used to render */
|
||||
/* SW_FT_Outlines into SW_FT_Bitmaps. */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "sw_ft_types.h"
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* FT_BBox */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A structure used to hold an outline's bounding box, i.e., the */
|
||||
/* coordinates of its extrema in the horizontal and vertical */
|
||||
/* directions. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* xMin :: The horizontal minimum (left-most). */
|
||||
/* */
|
||||
/* yMin :: The vertical minimum (bottom-most). */
|
||||
/* */
|
||||
/* xMax :: The horizontal maximum (right-most). */
|
||||
/* */
|
||||
/* yMax :: The vertical maximum (top-most). */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* The bounding box is specified with the coordinates of the lower */
|
||||
/* left and the upper right corner. In PostScript, those values are */
|
||||
/* often called (llx,lly) and (urx,ury), respectively. */
|
||||
/* */
|
||||
/* If `yMin' is negative, this value gives the glyph's descender. */
|
||||
/* Otherwise, the glyph doesn't descend below the baseline. */
|
||||
/* Similarly, if `ymax' is positive, this value gives the glyph's */
|
||||
/* ascender. */
|
||||
/* */
|
||||
/* `xMin' gives the horizontal distance from the glyph's origin to */
|
||||
/* the left edge of the glyph's bounding box. If `xMin' is negative, */
|
||||
/* the glyph extends to the left of the origin. */
|
||||
/* */
|
||||
typedef struct SW_FT_BBox_
|
||||
{
|
||||
SW_FT_Pos xMin, yMin;
|
||||
SW_FT_Pos xMax, yMax;
|
||||
|
||||
} SW_FT_BBox;
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* SW_FT_Outline */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* This structure is used to describe an outline to the scan-line */
|
||||
/* converter. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* n_contours :: The number of contours in the outline. */
|
||||
/* */
|
||||
/* n_points :: The number of points in the outline. */
|
||||
/* */
|
||||
/* points :: A pointer to an array of `n_points' @SW_FT_Vector */
|
||||
/* elements, giving the outline's point coordinates. */
|
||||
/* */
|
||||
/* tags :: A pointer to an array of `n_points' chars, giving */
|
||||
/* each outline point's type. */
|
||||
/* */
|
||||
/* If bit~0 is unset, the point is `off' the curve, */
|
||||
/* i.e., a Bézier control point, while it is `on' if */
|
||||
/* set. */
|
||||
/* */
|
||||
/* Bit~1 is meaningful for `off' points only. If set, */
|
||||
/* it indicates a third-order Bézier arc control point; */
|
||||
/* and a second-order control point if unset. */
|
||||
/* */
|
||||
/* If bit~2 is set, bits 5-7 contain the drop-out mode */
|
||||
/* (as defined in the OpenType specification; the value */
|
||||
/* is the same as the argument to the SCANMODE */
|
||||
/* instruction). */
|
||||
/* */
|
||||
/* Bits 3 and~4 are reserved for internal purposes. */
|
||||
/* */
|
||||
/* contours :: An array of `n_contours' shorts, giving the end */
|
||||
/* point of each contour within the outline. For */
|
||||
/* example, the first contour is defined by the points */
|
||||
/* `0' to `contours[0]', the second one is defined by */
|
||||
/* the points `contours[0]+1' to `contours[1]', etc. */
|
||||
/* */
|
||||
/* flags :: A set of bit flags used to characterize the outline */
|
||||
/* and give hints to the scan-converter and hinter on */
|
||||
/* how to convert/grid-fit it. See @SW_FT_OUTLINE_FLAGS.*/
|
||||
/* */
|
||||
typedef struct SW_FT_Outline_
|
||||
{
|
||||
short n_contours; /* number of contours in glyph */
|
||||
short n_points; /* number of points in the glyph */
|
||||
|
||||
SW_FT_Vector* points; /* the outline's points */
|
||||
char* tags; /* the points flags */
|
||||
short* contours; /* the contour end points */
|
||||
char* contours_flag; /* the contour open flags */
|
||||
|
||||
int flags; /* outline masks */
|
||||
|
||||
} SW_FT_Outline;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Enum> */
|
||||
/* SW_FT_OUTLINE_FLAGS */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A list of bit-field constants use for the flags in an outline's */
|
||||
/* `flags' field. */
|
||||
/* */
|
||||
/* <Values> */
|
||||
/* SW_FT_OUTLINE_NONE :: */
|
||||
/* Value~0 is reserved. */
|
||||
/* */
|
||||
/* SW_FT_OUTLINE_OWNER :: */
|
||||
/* If set, this flag indicates that the outline's field arrays */
|
||||
/* (i.e., `points', `flags', and `contours') are `owned' by the */
|
||||
/* outline object, and should thus be freed when it is destroyed. */
|
||||
/* */
|
||||
/* SW_FT_OUTLINE_EVEN_ODD_FILL :: */
|
||||
/* By default, outlines are filled using the non-zero winding rule. */
|
||||
/* If set to 1, the outline will be filled using the even-odd fill */
|
||||
/* rule (only works with the smooth rasterizer). */
|
||||
/* */
|
||||
/* SW_FT_OUTLINE_REVERSE_FILL :: */
|
||||
/* By default, outside contours of an outline are oriented in */
|
||||
/* clock-wise direction, as defined in the TrueType specification. */
|
||||
/* This flag is set if the outline uses the opposite direction */
|
||||
/* (typically for Type~1 fonts). This flag is ignored by the scan */
|
||||
/* converter. */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* There exists a second mechanism to pass the drop-out mode to the */
|
||||
/* B/W rasterizer; see the `tags' field in @SW_FT_Outline. */
|
||||
/* */
|
||||
/* Please refer to the description of the `SCANTYPE' instruction in */
|
||||
/* the OpenType specification (in file `ttinst1.doc') how simple */
|
||||
/* drop-outs, smart drop-outs, and stubs are defined. */
|
||||
/* */
|
||||
#define SW_FT_OUTLINE_NONE 0x0
|
||||
#define SW_FT_OUTLINE_OWNER 0x1
|
||||
#define SW_FT_OUTLINE_EVEN_ODD_FILL 0x2
|
||||
#define SW_FT_OUTLINE_REVERSE_FILL 0x4
|
||||
|
||||
/* */
|
||||
|
||||
#define SW_FT_CURVE_TAG( flag ) ( flag & 3 )
|
||||
|
||||
#define SW_FT_CURVE_TAG_ON 1
|
||||
#define SW_FT_CURVE_TAG_CONIC 0
|
||||
#define SW_FT_CURVE_TAG_CUBIC 2
|
||||
|
||||
|
||||
#define SW_FT_Curve_Tag_On SW_FT_CURVE_TAG_ON
|
||||
#define SW_FT_Curve_Tag_Conic SW_FT_CURVE_TAG_CONIC
|
||||
#define SW_FT_Curve_Tag_Cubic SW_FT_CURVE_TAG_CUBIC
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* A raster is a scan converter, in charge of rendering an outline into */
|
||||
/* a a bitmap. This section contains the public API for rasters. */
|
||||
/* */
|
||||
/* Note that in FreeType 2, all rasters are now encapsulated within */
|
||||
/* specific modules called `renderers'. See `ftrender.h' for more */
|
||||
/* details on renderers. */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Raster */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A handle (pointer) to a raster object. Each object can be used */
|
||||
/* independently to convert an outline into a bitmap or pixmap. */
|
||||
/* */
|
||||
typedef struct SW_FT_RasterRec_* SW_FT_Raster;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* SW_FT_Span */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A structure used to model a single span of gray (or black) pixels */
|
||||
/* when rendering a monochrome or anti-aliased bitmap. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* x :: The span's horizontal start position. */
|
||||
/* */
|
||||
/* len :: The span's length in pixels. */
|
||||
/* */
|
||||
/* coverage :: The span color/coverage, ranging from 0 (background) */
|
||||
/* to 255 (foreground). Only used for anti-aliased */
|
||||
/* rendering. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* This structure is used by the span drawing callback type named */
|
||||
/* @SW_FT_SpanFunc that takes the y~coordinate of the span as a */
|
||||
/* parameter. */
|
||||
/* */
|
||||
/* The coverage value is always between 0 and 255. If you want less */
|
||||
/* gray values, the callback function has to reduce them. */
|
||||
/* */
|
||||
typedef struct SW_FT_Span_
|
||||
{
|
||||
short x;
|
||||
short y;
|
||||
unsigned short len;
|
||||
unsigned char coverage;
|
||||
|
||||
} SW_FT_Span;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <FuncType> */
|
||||
/* SW_FT_SpanFunc */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A function used as a call-back by the anti-aliased renderer in */
|
||||
/* order to let client applications draw themselves the gray pixel */
|
||||
/* spans on each scan line. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* y :: The scanline's y~coordinate. */
|
||||
/* */
|
||||
/* count :: The number of spans to draw on this scanline. */
|
||||
/* */
|
||||
/* spans :: A table of `count' spans to draw on the scanline. */
|
||||
/* */
|
||||
/* user :: User-supplied data that is passed to the callback. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* This callback allows client applications to directly render the */
|
||||
/* gray spans of the anti-aliased bitmap to any kind of surfaces. */
|
||||
/* */
|
||||
/* This can be used to write anti-aliased outlines directly to a */
|
||||
/* given background bitmap, and even perform translucency. */
|
||||
/* */
|
||||
/* Note that the `count' field cannot be greater than a fixed value */
|
||||
/* defined by the `SW_FT_MAX_GRAY_SPANS' configuration macro in */
|
||||
/* `ftoption.h'. By default, this value is set to~32, which means */
|
||||
/* that if there are more than 32~spans on a given scanline, the */
|
||||
/* callback is called several times with the same `y' parameter in */
|
||||
/* order to draw all callbacks. */
|
||||
/* */
|
||||
/* Otherwise, the callback is only called once per scan-line, and */
|
||||
/* only for those scanlines that do have `gray' pixels on them. */
|
||||
/* */
|
||||
typedef void
|
||||
(*SW_FT_SpanFunc)( int count,
|
||||
const SW_FT_Span* spans,
|
||||
void* user );
|
||||
|
||||
typedef void
|
||||
(*SW_FT_BboxFunc)( int x, int y, int w, int h,
|
||||
void* user);
|
||||
|
||||
#define SW_FT_Raster_Span_Func SW_FT_SpanFunc
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Enum> */
|
||||
/* SW_FT_RASTER_FLAG_XXX */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A list of bit flag constants as used in the `flags' field of a */
|
||||
/* @SW_FT_Raster_Params structure. */
|
||||
/* */
|
||||
/* <Values> */
|
||||
/* SW_FT_RASTER_FLAG_DEFAULT :: This value is 0. */
|
||||
/* */
|
||||
/* SW_FT_RASTER_FLAG_AA :: This flag is set to indicate that an */
|
||||
/* anti-aliased glyph image should be */
|
||||
/* generated. Otherwise, it will be */
|
||||
/* monochrome (1-bit). */
|
||||
/* */
|
||||
/* SW_FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */
|
||||
/* rendering. In this mode, client */
|
||||
/* applications must provide their own span */
|
||||
/* callback. This lets them directly */
|
||||
/* draw or compose over an existing bitmap. */
|
||||
/* If this bit is not set, the target */
|
||||
/* pixmap's buffer _must_ be zeroed before */
|
||||
/* rendering. */
|
||||
/* */
|
||||
/* Note that for now, direct rendering is */
|
||||
/* only possible with anti-aliased glyphs. */
|
||||
/* */
|
||||
/* SW_FT_RASTER_FLAG_CLIP :: This flag is only used in direct */
|
||||
/* rendering mode. If set, the output will */
|
||||
/* be clipped to a box specified in the */
|
||||
/* `clip_box' field of the */
|
||||
/* @SW_FT_Raster_Params structure. */
|
||||
/* */
|
||||
/* Note that by default, the glyph bitmap */
|
||||
/* is clipped to the target pixmap, except */
|
||||
/* in direct rendering mode where all spans */
|
||||
/* are generated if no clipping box is set. */
|
||||
/* */
|
||||
#define SW_FT_RASTER_FLAG_DEFAULT 0x0
|
||||
#define SW_FT_RASTER_FLAG_AA 0x1
|
||||
#define SW_FT_RASTER_FLAG_DIRECT 0x2
|
||||
#define SW_FT_RASTER_FLAG_CLIP 0x4
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* SW_FT_Raster_Params */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A structure to hold the arguments used by a raster's render */
|
||||
/* function. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* target :: The target bitmap. */
|
||||
/* */
|
||||
/* source :: A pointer to the source glyph image (e.g., an */
|
||||
/* @SW_FT_Outline). */
|
||||
/* */
|
||||
/* flags :: The rendering flags. */
|
||||
/* */
|
||||
/* gray_spans :: The gray span drawing callback. */
|
||||
/* */
|
||||
/* black_spans :: The black span drawing callback. UNIMPLEMENTED! */
|
||||
/* */
|
||||
/* bit_test :: The bit test callback. UNIMPLEMENTED! */
|
||||
/* */
|
||||
/* bit_set :: The bit set callback. UNIMPLEMENTED! */
|
||||
/* */
|
||||
/* user :: User-supplied data that is passed to each drawing */
|
||||
/* callback. */
|
||||
/* */
|
||||
/* clip_box :: An optional clipping box. It is only used in */
|
||||
/* direct rendering mode. Note that coordinates here */
|
||||
/* should be expressed in _integer_ pixels (and not in */
|
||||
/* 26.6 fixed-point units). */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* An anti-aliased glyph bitmap is drawn if the @SW_FT_RASTER_FLAG_AA */
|
||||
/* bit flag is set in the `flags' field, otherwise a monochrome */
|
||||
/* bitmap is generated. */
|
||||
/* */
|
||||
/* If the @SW_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */
|
||||
/* raster will call the `gray_spans' callback to draw gray pixel */
|
||||
/* spans, in the case of an aa glyph bitmap, it will call */
|
||||
/* `black_spans', and `bit_test' and `bit_set' in the case of a */
|
||||
/* monochrome bitmap. This allows direct composition over a */
|
||||
/* pre-existing bitmap through user-provided callbacks to perform the */
|
||||
/* span drawing/composition. */
|
||||
/* */
|
||||
/* Note that the `bit_test' and `bit_set' callbacks are required when */
|
||||
/* rendering a monochrome bitmap, as they are crucial to implement */
|
||||
/* correct drop-out control as defined in the TrueType specification. */
|
||||
/* */
|
||||
typedef struct SW_FT_Raster_Params_
|
||||
{
|
||||
const void* source;
|
||||
int flags;
|
||||
SW_FT_SpanFunc gray_spans;
|
||||
SW_FT_BboxFunc bbox_cb;
|
||||
void* user;
|
||||
SW_FT_BBox clip_box;
|
||||
|
||||
} SW_FT_Raster_Params;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* SW_FT_Outline_Check */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* Check the contents of an outline descriptor. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* outline :: A handle to a source outline. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* FreeType error code. 0~means success. */
|
||||
/* */
|
||||
SW_FT_Error
|
||||
SW_FT_Outline_Check( SW_FT_Outline* outline );
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Function> */
|
||||
/* SW_FT_Outline_Get_CBox */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* Return an outline's `control box'. The control box encloses all */
|
||||
/* the outline's points, including Bézier control points. Though it */
|
||||
/* coincides with the exact bounding box for most glyphs, it can be */
|
||||
/* slightly larger in some situations (like when rotating an outline */
|
||||
/* that contains Bézier outside arcs). */
|
||||
/* */
|
||||
/* Computing the control box is very fast, while getting the bounding */
|
||||
/* box can take much more time as it needs to walk over all segments */
|
||||
/* and arcs in the outline. To get the latter, you can use the */
|
||||
/* `ftbbox' component, which is dedicated to this single task. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* outline :: A pointer to the source outline descriptor. */
|
||||
/* */
|
||||
/* <Output> */
|
||||
/* acbox :: The outline's control box. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* See @SW_FT_Glyph_Get_CBox for a discussion of tricky fonts. */
|
||||
/* */
|
||||
void
|
||||
SW_FT_Outline_Get_CBox( const SW_FT_Outline* outline,
|
||||
SW_FT_BBox *acbox );
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <FuncType> */
|
||||
/* SW_FT_Raster_NewFunc */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A function used to create a new raster object. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* memory :: A handle to the memory allocator. */
|
||||
/* */
|
||||
/* <Output> */
|
||||
/* raster :: A handle to the new raster object. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* Error code. 0~means success. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* The `memory' parameter is a typeless pointer in order to avoid */
|
||||
/* un-wanted dependencies on the rest of the FreeType code. In */
|
||||
/* practice, it is an @SW_FT_Memory object, i.e., a handle to the */
|
||||
/* standard FreeType memory allocator. However, this field can be */
|
||||
/* completely ignored by a given raster implementation. */
|
||||
/* */
|
||||
typedef int
|
||||
(*SW_FT_Raster_NewFunc)( SW_FT_Raster* raster );
|
||||
|
||||
#define SW_FT_Raster_New_Func SW_FT_Raster_NewFunc
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <FuncType> */
|
||||
/* SW_FT_Raster_DoneFunc */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A function used to destroy a given raster object. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* raster :: A handle to the raster object. */
|
||||
/* */
|
||||
typedef void
|
||||
(*SW_FT_Raster_DoneFunc)( SW_FT_Raster raster );
|
||||
|
||||
#define SW_FT_Raster_Done_Func SW_FT_Raster_DoneFunc
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <FuncType> */
|
||||
/* SW_FT_Raster_ResetFunc */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* FreeType provides an area of memory called the `render pool', */
|
||||
/* available to all registered rasters. This pool can be freely used */
|
||||
/* during a given scan-conversion but is shared by all rasters. Its */
|
||||
/* content is thus transient. */
|
||||
/* */
|
||||
/* This function is called each time the render pool changes, or just */
|
||||
/* after a new raster object is created. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* raster :: A handle to the new raster object. */
|
||||
/* */
|
||||
/* pool_base :: The address in memory of the render pool. */
|
||||
/* */
|
||||
/* pool_size :: The size in bytes of the render pool. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* Rasters can ignore the render pool and rely on dynamic memory */
|
||||
/* allocation if they want to (a handle to the memory allocator is */
|
||||
/* passed to the raster constructor). However, this is not */
|
||||
/* recommended for efficiency purposes. */
|
||||
/* */
|
||||
typedef void
|
||||
(*SW_FT_Raster_ResetFunc)( SW_FT_Raster raster,
|
||||
unsigned char* pool_base,
|
||||
unsigned long pool_size );
|
||||
|
||||
#define SW_FT_Raster_Reset_Func SW_FT_Raster_ResetFunc
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <FuncType> */
|
||||
/* SW_FT_Raster_RenderFunc */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* Invoke a given raster to scan-convert a given glyph image into a */
|
||||
/* target bitmap. */
|
||||
/* */
|
||||
/* <Input> */
|
||||
/* raster :: A handle to the raster object. */
|
||||
/* */
|
||||
/* params :: A pointer to an @SW_FT_Raster_Params structure used to */
|
||||
/* store the rendering parameters. */
|
||||
/* */
|
||||
/* <Return> */
|
||||
/* Error code. 0~means success. */
|
||||
/* */
|
||||
/* <Note> */
|
||||
/* The exact format of the source image depends on the raster's glyph */
|
||||
/* format defined in its @SW_FT_Raster_Funcs structure. It can be an */
|
||||
/* @SW_FT_Outline or anything else in order to support a large array of */
|
||||
/* glyph formats. */
|
||||
/* */
|
||||
/* Note also that the render function can fail and return a */
|
||||
/* `SW_FT_Err_Unimplemented_Feature' error code if the raster used does */
|
||||
/* not support direct composition. */
|
||||
/* */
|
||||
/* XXX: For now, the standard raster doesn't support direct */
|
||||
/* composition but this should change for the final release (see */
|
||||
/* the files `demos/src/ftgrays.c' and `demos/src/ftgrays2.c' */
|
||||
/* for examples of distinct implementations that support direct */
|
||||
/* composition). */
|
||||
/* */
|
||||
typedef int
|
||||
(*SW_FT_Raster_RenderFunc)( SW_FT_Raster raster,
|
||||
const SW_FT_Raster_Params* params );
|
||||
|
||||
#define SW_FT_Raster_Render_Func SW_FT_Raster_RenderFunc
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* SW_FT_Raster_Funcs */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A structure used to describe a given raster class to the library. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* glyph_format :: The supported glyph format for this raster. */
|
||||
/* */
|
||||
/* raster_new :: The raster constructor. */
|
||||
/* */
|
||||
/* raster_reset :: Used to reset the render pool within the raster. */
|
||||
/* */
|
||||
/* raster_render :: A function to render a glyph into a given bitmap. */
|
||||
/* */
|
||||
/* raster_done :: The raster destructor. */
|
||||
/* */
|
||||
typedef struct SW_FT_Raster_Funcs_
|
||||
{
|
||||
SW_FT_Raster_NewFunc raster_new;
|
||||
SW_FT_Raster_ResetFunc raster_reset;
|
||||
SW_FT_Raster_RenderFunc raster_render;
|
||||
SW_FT_Raster_DoneFunc raster_done;
|
||||
|
||||
} SW_FT_Raster_Funcs;
|
||||
|
||||
|
||||
extern const SW_FT_Raster_Funcs sw_ft_grays_raster;
|
||||
|
||||
#endif // SW_FT_IMG_H
|
1939
3rdparty/software/sw_ft_stroker.c
vendored
Normal file
1939
3rdparty/software/sw_ft_stroker.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
319
3rdparty/software/sw_ft_stroker.h
vendored
Normal file
319
3rdparty/software/sw_ft_stroker.h
vendored
Normal file
|
@ -0,0 +1,319 @@
|
|||
#ifndef SW_FT_STROKER_H
|
||||
#define SW_FT_STROKER_H
|
||||
/***************************************************************************/
|
||||
/* */
|
||||
/* ftstroke.h */
|
||||
/* */
|
||||
/* FreeType path stroker (specification). */
|
||||
/* */
|
||||
/* Copyright 2002-2006, 2008, 2009, 2011-2012 by */
|
||||
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
||||
/* */
|
||||
/* This file is part of the FreeType project, and may only be used, */
|
||||
/* modified, and distributed under the terms of the FreeType project */
|
||||
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
|
||||
/* this file you indicate that you have read the license and */
|
||||
/* understand and accept it fully. */
|
||||
/* */
|
||||
/***************************************************************************/
|
||||
|
||||
#include "sw_ft_raster.h"
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @type:
|
||||
* SW_FT_Stroker
|
||||
*
|
||||
* @description:
|
||||
* Opaque handler to a path stroker object.
|
||||
*/
|
||||
typedef struct SW_FT_StrokerRec_* SW_FT_Stroker;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @enum:
|
||||
* SW_FT_Stroker_LineJoin
|
||||
*
|
||||
* @description:
|
||||
* These values determine how two joining lines are rendered
|
||||
* in a stroker.
|
||||
*
|
||||
* @values:
|
||||
* SW_FT_STROKER_LINEJOIN_ROUND ::
|
||||
* Used to render rounded line joins. Circular arcs are used
|
||||
* to join two lines smoothly.
|
||||
*
|
||||
* SW_FT_STROKER_LINEJOIN_BEVEL ::
|
||||
* Used to render beveled line joins. The outer corner of
|
||||
* the joined lines is filled by enclosing the triangular
|
||||
* region of the corner with a straight line between the
|
||||
* outer corners of each stroke.
|
||||
*
|
||||
* SW_FT_STROKER_LINEJOIN_MITER_FIXED ::
|
||||
* Used to render mitered line joins, with fixed bevels if the
|
||||
* miter limit is exceeded. The outer edges of the strokes
|
||||
* for the two segments are extended until they meet at an
|
||||
* angle. If the segments meet at too sharp an angle (such
|
||||
* that the miter would extend from the intersection of the
|
||||
* segments a distance greater than the product of the miter
|
||||
* limit value and the border radius), then a bevel join (see
|
||||
* above) is used instead. This prevents long spikes being
|
||||
* created. SW_FT_STROKER_LINEJOIN_MITER_FIXED generates a miter
|
||||
* line join as used in PostScript and PDF.
|
||||
*
|
||||
* SW_FT_STROKER_LINEJOIN_MITER_VARIABLE ::
|
||||
* SW_FT_STROKER_LINEJOIN_MITER ::
|
||||
* Used to render mitered line joins, with variable bevels if
|
||||
* the miter limit is exceeded. The intersection of the
|
||||
* strokes is clipped at a line perpendicular to the bisector
|
||||
* of the angle between the strokes, at the distance from the
|
||||
* intersection of the segments equal to the product of the
|
||||
* miter limit value and the border radius. This prevents
|
||||
* long spikes being created.
|
||||
* SW_FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line
|
||||
* join as used in XPS. SW_FT_STROKER_LINEJOIN_MITER is an alias
|
||||
* for SW_FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for
|
||||
* backwards compatibility.
|
||||
*/
|
||||
typedef enum SW_FT_Stroker_LineJoin_
|
||||
{
|
||||
SW_FT_STROKER_LINEJOIN_ROUND = 0,
|
||||
SW_FT_STROKER_LINEJOIN_BEVEL = 1,
|
||||
SW_FT_STROKER_LINEJOIN_MITER_VARIABLE = 2,
|
||||
SW_FT_STROKER_LINEJOIN_MITER = SW_FT_STROKER_LINEJOIN_MITER_VARIABLE,
|
||||
SW_FT_STROKER_LINEJOIN_MITER_FIXED = 3
|
||||
|
||||
} SW_FT_Stroker_LineJoin;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @enum:
|
||||
* SW_FT_Stroker_LineCap
|
||||
*
|
||||
* @description:
|
||||
* These values determine how the end of opened sub-paths are
|
||||
* rendered in a stroke.
|
||||
*
|
||||
* @values:
|
||||
* SW_FT_STROKER_LINECAP_BUTT ::
|
||||
* The end of lines is rendered as a full stop on the last
|
||||
* point itself.
|
||||
*
|
||||
* SW_FT_STROKER_LINECAP_ROUND ::
|
||||
* The end of lines is rendered as a half-circle around the
|
||||
* last point.
|
||||
*
|
||||
* SW_FT_STROKER_LINECAP_SQUARE ::
|
||||
* The end of lines is rendered as a square around the
|
||||
* last point.
|
||||
*/
|
||||
typedef enum SW_FT_Stroker_LineCap_
|
||||
{
|
||||
SW_FT_STROKER_LINECAP_BUTT = 0,
|
||||
SW_FT_STROKER_LINECAP_ROUND,
|
||||
SW_FT_STROKER_LINECAP_SQUARE
|
||||
|
||||
} SW_FT_Stroker_LineCap;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @enum:
|
||||
* SW_FT_StrokerBorder
|
||||
*
|
||||
* @description:
|
||||
* These values are used to select a given stroke border
|
||||
* in @SW_FT_Stroker_GetBorderCounts and @SW_FT_Stroker_ExportBorder.
|
||||
*
|
||||
* @values:
|
||||
* SW_FT_STROKER_BORDER_LEFT ::
|
||||
* Select the left border, relative to the drawing direction.
|
||||
*
|
||||
* SW_FT_STROKER_BORDER_RIGHT ::
|
||||
* Select the right border, relative to the drawing direction.
|
||||
*
|
||||
* @note:
|
||||
* Applications are generally interested in the `inside' and `outside'
|
||||
* borders. However, there is no direct mapping between these and the
|
||||
* `left' and `right' ones, since this really depends on the glyph's
|
||||
* drawing orientation, which varies between font formats.
|
||||
*
|
||||
* You can however use @SW_FT_Outline_GetInsideBorder and
|
||||
* @SW_FT_Outline_GetOutsideBorder to get these.
|
||||
*/
|
||||
typedef enum SW_FT_StrokerBorder_
|
||||
{
|
||||
SW_FT_STROKER_BORDER_LEFT = 0,
|
||||
SW_FT_STROKER_BORDER_RIGHT
|
||||
|
||||
} SW_FT_StrokerBorder;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_New
|
||||
*
|
||||
* @description:
|
||||
* Create a new stroker object.
|
||||
*
|
||||
* @input:
|
||||
* library ::
|
||||
* FreeType library handle.
|
||||
*
|
||||
* @output:
|
||||
* astroker ::
|
||||
* A new stroker object handle. NULL in case of error.
|
||||
*
|
||||
* @return:
|
||||
* FreeType error code. 0~means success.
|
||||
*/
|
||||
SW_FT_Error
|
||||
SW_FT_Stroker_New( SW_FT_Stroker *astroker );
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_Set
|
||||
*
|
||||
* @description:
|
||||
* Reset a stroker object's attributes.
|
||||
*
|
||||
* @input:
|
||||
* stroker ::
|
||||
* The target stroker handle.
|
||||
*
|
||||
* radius ::
|
||||
* The border radius.
|
||||
*
|
||||
* line_cap ::
|
||||
* The line cap style.
|
||||
*
|
||||
* line_join ::
|
||||
* The line join style.
|
||||
*
|
||||
* miter_limit ::
|
||||
* The miter limit for the SW_FT_STROKER_LINEJOIN_MITER_FIXED and
|
||||
* SW_FT_STROKER_LINEJOIN_MITER_VARIABLE line join styles,
|
||||
* expressed as 16.16 fixed-point value.
|
||||
*
|
||||
* @note:
|
||||
* The radius is expressed in the same units as the outline
|
||||
* coordinates.
|
||||
*/
|
||||
void
|
||||
SW_FT_Stroker_Set( SW_FT_Stroker stroker,
|
||||
SW_FT_Fixed radius,
|
||||
SW_FT_Stroker_LineCap line_cap,
|
||||
SW_FT_Stroker_LineJoin line_join,
|
||||
SW_FT_Fixed miter_limit );
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_ParseOutline
|
||||
*
|
||||
* @description:
|
||||
* A convenience function used to parse a whole outline with
|
||||
* the stroker. The resulting outline(s) can be retrieved
|
||||
* later by functions like @SW_FT_Stroker_GetCounts and @SW_FT_Stroker_Export.
|
||||
*
|
||||
* @input:
|
||||
* stroker ::
|
||||
* The target stroker handle.
|
||||
*
|
||||
* outline ::
|
||||
* The source outline.
|
||||
*
|
||||
*
|
||||
* @return:
|
||||
* FreeType error code. 0~means success.
|
||||
*
|
||||
* @note:
|
||||
* If `opened' is~0 (the default), the outline is treated as a closed
|
||||
* path, and the stroker generates two distinct `border' outlines.
|
||||
*
|
||||
*
|
||||
* This function calls @SW_FT_Stroker_Rewind automatically.
|
||||
*/
|
||||
SW_FT_Error
|
||||
SW_FT_Stroker_ParseOutline( SW_FT_Stroker stroker,
|
||||
const SW_FT_Outline* outline);
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_GetCounts
|
||||
*
|
||||
* @description:
|
||||
* Call this function once you have finished parsing your paths
|
||||
* with the stroker. It returns the number of points and
|
||||
* contours necessary to export all points/borders from the stroked
|
||||
* outline/path.
|
||||
*
|
||||
* @input:
|
||||
* stroker ::
|
||||
* The target stroker handle.
|
||||
*
|
||||
* @output:
|
||||
* anum_points ::
|
||||
* The number of points.
|
||||
*
|
||||
* anum_contours ::
|
||||
* The number of contours.
|
||||
*
|
||||
* @return:
|
||||
* FreeType error code. 0~means success.
|
||||
*/
|
||||
SW_FT_Error
|
||||
SW_FT_Stroker_GetCounts( SW_FT_Stroker stroker,
|
||||
SW_FT_UInt *anum_points,
|
||||
SW_FT_UInt *anum_contours );
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_Export
|
||||
*
|
||||
* @description:
|
||||
* Call this function after @SW_FT_Stroker_GetBorderCounts to
|
||||
* export all borders to your own @SW_FT_Outline structure.
|
||||
*
|
||||
* Note that this function appends the border points and
|
||||
* contours to your outline, but does not try to resize its
|
||||
* arrays.
|
||||
*
|
||||
* @input:
|
||||
* stroker ::
|
||||
* The target stroker handle.
|
||||
*
|
||||
* outline ::
|
||||
* The target outline handle.
|
||||
*/
|
||||
void
|
||||
SW_FT_Stroker_Export( SW_FT_Stroker stroker,
|
||||
SW_FT_Outline* outline );
|
||||
|
||||
|
||||
/**************************************************************
|
||||
*
|
||||
* @function:
|
||||
* SW_FT_Stroker_Done
|
||||
*
|
||||
* @description:
|
||||
* Destroy a stroker object.
|
||||
*
|
||||
* @input:
|
||||
* stroker ::
|
||||
* A stroker handle. Can be NULL.
|
||||
*/
|
||||
void
|
||||
SW_FT_Stroker_Done( SW_FT_Stroker stroker );
|
||||
|
||||
|
||||
#endif // SW_FT_STROKER_H
|
160
3rdparty/software/sw_ft_types.h
vendored
Normal file
160
3rdparty/software/sw_ft_types.h
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
#ifndef SW_FT_TYPES_H
|
||||
#define SW_FT_TYPES_H
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Fixed */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* This type is used to store 16.16 fixed-point values, like scaling */
|
||||
/* values or matrix coefficients. */
|
||||
/* */
|
||||
typedef signed long SW_FT_Fixed;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Int */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef for the int type. */
|
||||
/* */
|
||||
typedef signed int SW_FT_Int;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_UInt */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef for the unsigned int type. */
|
||||
/* */
|
||||
typedef unsigned int SW_FT_UInt;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Long */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef for signed long. */
|
||||
/* */
|
||||
typedef signed long SW_FT_Long;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_ULong */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef for unsigned long. */
|
||||
/* */
|
||||
typedef unsigned long SW_FT_ULong;
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Short */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef for signed short. */
|
||||
/* */
|
||||
typedef signed short SW_FT_Short;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Byte */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A simple typedef for the _unsigned_ char type. */
|
||||
/* */
|
||||
typedef unsigned char SW_FT_Byte;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Bool */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A typedef of unsigned char, used for simple booleans. As usual, */
|
||||
/* values 1 and~0 represent true and false, respectively. */
|
||||
/* */
|
||||
typedef unsigned char SW_FT_Bool;
|
||||
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Error */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* The FreeType error code type. A value of~0 is always interpreted */
|
||||
/* as a successful operation. */
|
||||
/* */
|
||||
typedef int SW_FT_Error;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Type> */
|
||||
/* SW_FT_Pos */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* The type SW_FT_Pos is used to store vectorial coordinates. Depending */
|
||||
/* on the context, these can represent distances in integer font */
|
||||
/* units, or 16.16, or 26.6 fixed-point pixel coordinates. */
|
||||
/* */
|
||||
typedef signed long SW_FT_Pos;
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* */
|
||||
/* <Struct> */
|
||||
/* SW_FT_Vector */
|
||||
/* */
|
||||
/* <Description> */
|
||||
/* A simple structure used to store a 2D vector; coordinates are of */
|
||||
/* the SW_FT_Pos type. */
|
||||
/* */
|
||||
/* <Fields> */
|
||||
/* x :: The horizontal coordinate. */
|
||||
/* y :: The vertical coordinate. */
|
||||
/* */
|
||||
typedef struct SW_FT_Vector_
|
||||
{
|
||||
SW_FT_Pos x;
|
||||
SW_FT_Pos y;
|
||||
|
||||
} SW_FT_Vector;
|
||||
|
||||
|
||||
typedef long long int SW_FT_Int64;
|
||||
typedef unsigned long long int SW_FT_UInt64;
|
||||
|
||||
typedef signed int SW_FT_Int32;
|
||||
typedef unsigned int SW_FT_UInt32;
|
||||
|
||||
|
||||
#define SW_FT_BOOL( x ) ( (SW_FT_Bool)( x ) )
|
||||
|
||||
#define SW_FT_SIZEOF_LONG 4
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif // SW_FT_TYPES_H
|
39
CMakeLists.txt
Executable file
39
CMakeLists.txt
Executable file
|
@ -0,0 +1,39 @@
|
|||
cmake_minimum_required(VERSION 3.3)
|
||||
|
||||
project(lunasvg VERSION 2.3.2 LANGUAGES CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Builds as shared library" OFF)
|
||||
option(LUNASVG_BUILD_EXAMPLES "Builds examples" OFF)
|
||||
|
||||
add_library(lunasvg)
|
||||
|
||||
add_subdirectory(include)
|
||||
add_subdirectory(source)
|
||||
add_subdirectory(3rdparty/software)
|
||||
add_subdirectory(3rdparty/plutovg)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(lunasvg PUBLIC LUNASVG_SHARED)
|
||||
target_compile_definitions(lunasvg PRIVATE LUNASVG_EXPORT)
|
||||
endif()
|
||||
|
||||
if(LUNASVG_BUILD_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
set(LUNASVG_LIBDIR ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
set(LUNASVG_INCDIR ${CMAKE_INSTALL_PREFIX}/include)
|
||||
|
||||
install(FILES
|
||||
include/lunasvg.h
|
||||
DESTINATION ${LUNASVG_INCDIR}
|
||||
)
|
||||
|
||||
install(TARGETS lunasvg
|
||||
LIBRARY DESTINATION ${LUNASVG_LIBDIR}
|
||||
ARCHIVE DESTINATION ${LUNASVG_LIBDIR}
|
||||
INCLUDES DESTINATION ${LUNASVG_INCDIR}
|
||||
)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Nwutobo Samuel Ugochukwu <sammycageagle@gmail.com>
|
||||
|
||||
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.
|
89
README.md
Normal file
89
README.md
Normal file
|
@ -0,0 +1,89 @@
|
|||
[![Releases](https://img.shields.io/badge/Version-2.3.2-orange.svg)](https://github.com/sammycage/lunasvg/releases)
|
||||
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sammycage/lunasvg/blob/master/LICENSE)
|
||||
[![Build Status](https://github.com/sammycage/lunasvg/actions/workflows/ci.yml/badge.svg)](https://github.com/sammycage/lunasvg/actions)
|
||||
|
||||
# LunaSVG - SVG rendering library in C++
|
||||
|
||||
![LunaSVG](https://github.com/sammycage/lunasvg/blob/master/luna.png)
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include <lunasvg.h>
|
||||
|
||||
using namespace lunasvg;
|
||||
|
||||
int main()
|
||||
{
|
||||
auto document = Document::loadFromFile("tiger.svg");
|
||||
auto bitmap = document->renderToBitmap();
|
||||
|
||||
// do something useful with the bitmap here.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Basic Shapes
|
||||
- Document Structures
|
||||
- Coordinate Systems, Transformations and Units
|
||||
- SolidColors
|
||||
- Gradients
|
||||
- Patterns
|
||||
- Masks
|
||||
- ClipPaths
|
||||
- Markers
|
||||
- StyleSheet
|
||||
|
||||
## TODO
|
||||
|
||||
- Texts
|
||||
- Filters
|
||||
- Images
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
git clone https://github.com/sammycage/lunasvg.git
|
||||
cd lunasvg
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j 2
|
||||
```
|
||||
|
||||
To install lunasvg library.
|
||||
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||
While building lunasvg example it generates a simple SVG to PNG converter which can be used to convert SVG file to PNG file.
|
||||
|
||||
Run Demo.
|
||||
```
|
||||
svg2png [filename] [resolution] [bgColor]
|
||||
```
|
||||
|
||||
## Projects Using LunaSVG
|
||||
|
||||
- [OpenSiv3D](https://github.com/Siv3D/OpenSiv3D)
|
||||
- [PICsimLab](https://github.com/lcgamboa/picsimlab)
|
||||
- [MoneyManagerEx](https://github.com/moneymanagerex/moneymanagerex)
|
||||
- [RmlUi](https://github.com/mikke89/RmlUi)
|
||||
- [EKA2L1](https://github.com/EKA2L/EKA2L1)
|
||||
- [ObEngine](https://github.com/ObEngine/ObEngine)
|
||||
- [OTTO](https://github.com/bitfieldaudio/OTTO)
|
||||
|
||||
## Support Me
|
||||
|
||||
If you like the work lunasvg is doing please consider a small donation :
|
||||
|
||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.me/sammycage)
|
||||
[![Donate](https://img.shields.io/badge/Donate-BuyMeACoffee-yellow.svg)](https://www.buymeacoffee.com/sammycage)
|
||||
[![Sponsor](https://img.shields.io/badge/Sponsor-Patreon-orange.svg)](https://patreon.com/sammycage)
|
8
example/CMakeLists.txt
Normal file
8
example/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
cmake_minimum_required(VERSION 3.3)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
project(svg2png CXX)
|
||||
|
||||
add_executable(svg2png svg2png.cpp)
|
||||
target_link_libraries(svg2png lunasvg)
|
511
example/stb_image_write.h
Normal file
511
example/stb_image_write.h
Normal file
|
@ -0,0 +1,511 @@
|
|||
/* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h
|
||||
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
|
||||
Before including,
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
in the file that you want to have the implementation.
|
||||
|
||||
|
||||
ABOUT:
|
||||
|
||||
This header file is a library for writing images to C stdio. It could be
|
||||
adapted to write to memory or a general streaming interface; let me know.
|
||||
|
||||
The PNG output is not optimal; it is 20-50% larger than the file
|
||||
written by a decent optimizing implementation. This library is designed
|
||||
for source code compactness and simplicitly, not optimal image file size
|
||||
or run-time performance.
|
||||
|
||||
USAGE:
|
||||
|
||||
There are three functions, one for each image file format:
|
||||
|
||||
int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||
int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||
|
||||
Each function returns 0 on failure and non-0 on success.
|
||||
|
||||
The functions create an image file defined by the parameters. The image
|
||||
is a rectangle of pixels stored from left-to-right, top-to-bottom.
|
||||
Each pixel contains 'comp' channels of data stored interleaved with 8-bits
|
||||
per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
|
||||
monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
|
||||
The *data pointer points to the first byte of the top-left-most pixel.
|
||||
For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
|
||||
a row of pixels to the first byte of the next row of pixels.
|
||||
|
||||
PNG creates output files with the same number of components as the input.
|
||||
The BMP and TGA formats expand Y to RGB in the file format. BMP does not
|
||||
output alpha.
|
||||
|
||||
PNG supports writing rectangles of data even when the bytes storing rows of
|
||||
data are not consecutive in memory (e.g. sub-rectangles of a larger image),
|
||||
by supplying the stride between the beginning of adjacent rows. The other
|
||||
formats do not. (Thus you cannot write a native-format BMP through the BMP
|
||||
writer, both because it is in BGR order and because it may have padding
|
||||
at the end of the line.)
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_STB_IMAGE_WRITE_H
|
||||
#define INCLUDE_STB_IMAGE_WRITE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
||||
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif//INCLUDE_STB_IMAGE_WRITE_H
|
||||
|
||||
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef unsigned int stbiw_uint32;
|
||||
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
|
||||
|
||||
static void writefv(FILE *f, const char *fmt, va_list v)
|
||||
{
|
||||
while (*fmt) {
|
||||
switch (*fmt++) {
|
||||
case ' ': break;
|
||||
case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
|
||||
case '2': { int x = va_arg(v,int); unsigned char b[2];
|
||||
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
|
||||
fwrite(b,2,1,f); break; }
|
||||
case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
|
||||
b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
|
||||
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
|
||||
fwrite(b,4,1,f); break; }
|
||||
default:
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
|
||||
{
|
||||
unsigned char arr[3];
|
||||
arr[0] = a, arr[1] = b, arr[2] = c;
|
||||
fwrite(arr, 3, 1, f);
|
||||
}
|
||||
|
||||
static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
|
||||
{
|
||||
unsigned char bg[3] = { 255, 0, 255}, px[3];
|
||||
stbiw_uint32 zero = 0;
|
||||
int i,j,k, j_end;
|
||||
|
||||
if (y <= 0)
|
||||
return;
|
||||
|
||||
if (vdir < 0)
|
||||
j_end = -1, j = y-1;
|
||||
else
|
||||
j_end = y, j = 0;
|
||||
|
||||
for (; j != j_end; j += vdir) {
|
||||
for (i=0; i < x; ++i) {
|
||||
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
||||
if (write_alpha < 0)
|
||||
fwrite(&d[comp-1], 1, 1, f);
|
||||
switch (comp) {
|
||||
case 1:
|
||||
case 2: write3(f, d[0],d[0],d[0]);
|
||||
break;
|
||||
case 4:
|
||||
if (!write_alpha) {
|
||||
// composite against pink background
|
||||
for (k=0; k < 3; ++k)
|
||||
px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
|
||||
write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case 3:
|
||||
write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);
|
||||
break;
|
||||
}
|
||||
if (write_alpha > 0)
|
||||
fwrite(&d[comp-1], 1, 1, f);
|
||||
}
|
||||
fwrite(&zero,scanline_pad,1,f);
|
||||
}
|
||||
}
|
||||
|
||||
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
|
||||
{
|
||||
FILE *f;
|
||||
if (y < 0 || x < 0) return 0;
|
||||
f = fopen(filename, "wb");
|
||||
if (f) {
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
writefv(f, fmt, v);
|
||||
va_end(v);
|
||||
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
|
||||
fclose(f);
|
||||
}
|
||||
return f != NULL;
|
||||
}
|
||||
|
||||
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
|
||||
{
|
||||
int pad = (-x*3) & 3;
|
||||
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad,
|
||||
"11 4 22 4" "4 44 22 444444",
|
||||
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
|
||||
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
|
||||
}
|
||||
|
||||
int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
|
||||
{
|
||||
int has_alpha = !(comp & 1);
|
||||
return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0,
|
||||
"111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha);
|
||||
}
|
||||
|
||||
// stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
|
||||
#define stbi__sbraw(a) ((int *) (a) - 2)
|
||||
#define stbi__sbm(a) stbi__sbraw(a)[0]
|
||||
#define stbi__sbn(a) stbi__sbraw(a)[1]
|
||||
|
||||
#define stbi__sbneedgrow(a,n) ((a)==0 || stbi__sbn(a)+n >= stbi__sbm(a))
|
||||
#define stbi__sbmaybegrow(a,n) (stbi__sbneedgrow(a,(n)) ? stbi__sbgrow(a,n) : 0)
|
||||
#define stbi__sbgrow(a,n) stbi__sbgrowf((void **) &(a), (n), sizeof(*(a)))
|
||||
|
||||
#define stbi__sbpush(a, v) (stbi__sbmaybegrow(a,1), (a)[stbi__sbn(a)++] = (v))
|
||||
#define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
|
||||
#define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)),0 : 0)
|
||||
|
||||
static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
|
||||
{
|
||||
int m = *arr ? 2*stbi__sbm(*arr)+increment : increment+1;
|
||||
void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
|
||||
assert(p);
|
||||
if (p) {
|
||||
if (!*arr) ((int *) p)[1] = 0;
|
||||
*arr = (void *) ((int *) p + 2);
|
||||
stbi__sbm(*arr) = m;
|
||||
}
|
||||
return *arr;
|
||||
}
|
||||
|
||||
static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
|
||||
{
|
||||
while (*bitcount >= 8) {
|
||||
stbi__sbpush(data, (unsigned char) *bitbuffer);
|
||||
*bitbuffer >>= 8;
|
||||
*bitcount -= 8;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static int stbi__zlib_bitrev(int code, int codebits)
|
||||
{
|
||||
int res=0;
|
||||
while (codebits--) {
|
||||
res = (res << 1) | (code & 1);
|
||||
code >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < limit && i < 258; ++i)
|
||||
if (a[i] != b[i]) break;
|
||||
return i;
|
||||
}
|
||||
|
||||
static unsigned int stbi__zhash(unsigned char *data)
|
||||
{
|
||||
stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
return hash;
|
||||
}
|
||||
|
||||
#define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
|
||||
#define stbi__zlib_add(code,codebits) \
|
||||
(bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
|
||||
#define stbi__zlib_huffa(b,c) stbi__zlib_add(stbi__zlib_bitrev(b,c),c)
|
||||
// default huffman tables
|
||||
#define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
|
||||
#define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
|
||||
#define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256,7)
|
||||
#define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280,8)
|
||||
#define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
|
||||
#define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
|
||||
|
||||
#define stbi__ZHASH 16384
|
||||
|
||||
unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
|
||||
{
|
||||
static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
|
||||
static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
|
||||
static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
|
||||
static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
|
||||
unsigned int bitbuf=0;
|
||||
int i,j, bitcount=0;
|
||||
unsigned char *out = NULL;
|
||||
unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
|
||||
if (quality < 5) quality = 5;
|
||||
|
||||
stbi__sbpush(out, 0x78); // DEFLATE 32K window
|
||||
stbi__sbpush(out, 0x5e); // FLEVEL = 1
|
||||
stbi__zlib_add(1,1); // BFINAL = 1
|
||||
stbi__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
|
||||
|
||||
for (i=0; i < stbi__ZHASH; ++i)
|
||||
hash_table[i] = NULL;
|
||||
|
||||
i=0;
|
||||
while (i < data_len-3) {
|
||||
// hash next 3 bytes of data to be compressed
|
||||
int h = stbi__zhash(data+i)&(stbi__ZHASH-1), best=3;
|
||||
unsigned char *bestloc = 0;
|
||||
unsigned char **hlist = hash_table[h];
|
||||
int n = stbi__sbcount(hlist);
|
||||
for (j=0; j < n; ++j) {
|
||||
if (hlist[j]-data > i-32768) { // if entry lies within window
|
||||
int d = stbi__zlib_countm(hlist[j], data+i, data_len-i);
|
||||
if (d >= best) best=d,bestloc=hlist[j];
|
||||
}
|
||||
}
|
||||
// when hash table entry is too long, delete half the entries
|
||||
if (hash_table[h] && stbi__sbn(hash_table[h]) == 2*quality) {
|
||||
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
|
||||
stbi__sbn(hash_table[h]) = quality;
|
||||
}
|
||||
stbi__sbpush(hash_table[h],data+i);
|
||||
|
||||
if (bestloc) {
|
||||
// "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
|
||||
h = stbi__zhash(data+i+1)&(stbi__ZHASH-1);
|
||||
hlist = hash_table[h];
|
||||
n = stbi__sbcount(hlist);
|
||||
for (j=0; j < n; ++j) {
|
||||
if (hlist[j]-data > i-32767) {
|
||||
int e = stbi__zlib_countm(hlist[j], data+i+1, data_len-i-1);
|
||||
if (e > best) { // if next match is better, bail on current match
|
||||
bestloc = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestloc) {
|
||||
int d = data+i - bestloc; // distance back
|
||||
assert(d <= 32767 && best <= 258);
|
||||
for (j=0; best > lengthc[j+1]-1; ++j);
|
||||
stbi__zlib_huff(j+257);
|
||||
if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
|
||||
for (j=0; d > distc[j+1]-1; ++j);
|
||||
stbi__zlib_add(stbi__zlib_bitrev(j,5),5);
|
||||
if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
|
||||
i += best;
|
||||
} else {
|
||||
stbi__zlib_huffb(data[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// write out final bytes
|
||||
for (;i < data_len; ++i)
|
||||
stbi__zlib_huffb(data[i]);
|
||||
stbi__zlib_huff(256); // end of block
|
||||
// pad with 0 bits to byte boundary
|
||||
while (bitcount)
|
||||
stbi__zlib_add(0,1);
|
||||
|
||||
for (i=0; i < stbi__ZHASH; ++i)
|
||||
(void) stbi__sbfree(hash_table[i]);
|
||||
|
||||
{
|
||||
// compute adler32 on input
|
||||
unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;
|
||||
int j=0;
|
||||
while (j < data_len) {
|
||||
for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
|
||||
s1 %= 65521, s2 %= 65521;
|
||||
j += blocklen;
|
||||
blocklen = 5552;
|
||||
}
|
||||
stbi__sbpush(out, (unsigned char) (s2 >> 8));
|
||||
stbi__sbpush(out, (unsigned char) s2);
|
||||
stbi__sbpush(out, (unsigned char) (s1 >> 8));
|
||||
stbi__sbpush(out, (unsigned char) s1);
|
||||
}
|
||||
*out_len = stbi__sbn(out);
|
||||
// make returned pointer freeable
|
||||
memmove(stbi__sbraw(out), out, *out_len);
|
||||
return (unsigned char *) stbi__sbraw(out);
|
||||
}
|
||||
|
||||
unsigned int stbi__crc32(unsigned char *buffer, int len)
|
||||
{
|
||||
static unsigned int crc_table[256];
|
||||
unsigned int crc = ~0u;
|
||||
int i,j;
|
||||
if (crc_table[1] == 0)
|
||||
for(i=0; i < 256; i++)
|
||||
for (crc_table[i]=i, j=0; j < 8; ++j)
|
||||
crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
|
||||
for (i=0; i < len; ++i)
|
||||
crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
#define stbi__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)
|
||||
#define stbi__wp32(data,v) stbi__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
|
||||
#define stbi__wptag(data,s) stbi__wpng4(data, s[0],s[1],s[2],s[3])
|
||||
|
||||
static void stbi__wpcrc(unsigned char **data, int len)
|
||||
{
|
||||
unsigned int crc = stbi__crc32(*data - len - 4, len+4);
|
||||
stbi__wp32(*data, crc);
|
||||
}
|
||||
|
||||
static unsigned char stbi__paeth(int a, int b, int c)
|
||||
{
|
||||
int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
|
||||
if (pa <= pb && pa <= pc) return (unsigned char) a;
|
||||
if (pb <= pc) return (unsigned char) b;
|
||||
return (unsigned char) c;
|
||||
}
|
||||
|
||||
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
|
||||
{
|
||||
int ctype[5] = { -1, 0, 4, 2, 6 };
|
||||
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
|
||||
unsigned char *out,*o, *filt, *zlib;
|
||||
signed char *line_buffer;
|
||||
int i,j,k,p,zlen;
|
||||
|
||||
if (stride_bytes == 0)
|
||||
stride_bytes = x * n;
|
||||
|
||||
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
|
||||
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
|
||||
for (j=0; j < y; ++j) {
|
||||
static int mapping[] = { 0,1,2,3,4 };
|
||||
static int firstmap[] = { 0,1,0,5,6 };
|
||||
int *mymap = j ? mapping : firstmap;
|
||||
int best = 0, bestval = 0x7fffffff;
|
||||
for (p=0; p < 2; ++p) {
|
||||
for (k= p?best:0; k < 5; ++k) {
|
||||
int type = mymap[k],est=0;
|
||||
unsigned char *z = pixels + stride_bytes*j;
|
||||
for (i=0; i < n; ++i)
|
||||
switch (type) {
|
||||
case 0: line_buffer[i] = z[i]; break;
|
||||
case 1: line_buffer[i] = z[i]; break;
|
||||
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||
case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
|
||||
case 4: line_buffer[i] = (signed char) (z[i] - stbi__paeth(0,z[i-stride_bytes],0)); break;
|
||||
case 5: line_buffer[i] = z[i]; break;
|
||||
case 6: line_buffer[i] = z[i]; break;
|
||||
}
|
||||
for (i=n; i < x*n; ++i) {
|
||||
switch (type) {
|
||||
case 0: line_buffer[i] = z[i]; break;
|
||||
case 1: line_buffer[i] = z[i] - z[i-n]; break;
|
||||
case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
|
||||
case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
|
||||
case 4: line_buffer[i] = z[i] - stbi__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
|
||||
case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
|
||||
case 6: line_buffer[i] = z[i] - stbi__paeth(z[i-n], 0,0); break;
|
||||
}
|
||||
}
|
||||
if (p) break;
|
||||
for (i=0; i < x*n; ++i)
|
||||
est += abs((signed char) line_buffer[i]);
|
||||
if (est < bestval) { bestval = est; best = k; }
|
||||
}
|
||||
}
|
||||
// when we get here, best contains the filter type, and line_buffer contains the data
|
||||
filt[j*(x*n+1)] = (unsigned char) best;
|
||||
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
|
||||
}
|
||||
free(line_buffer);
|
||||
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
|
||||
free(filt);
|
||||
if (!zlib) return 0;
|
||||
|
||||
// each tag requires 12 bytes of overhead
|
||||
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
|
||||
if (!out) return 0;
|
||||
*out_len = 8 + 12+13 + 12+zlen + 12;
|
||||
|
||||
o=out;
|
||||
memcpy(o,sig,8); o+= 8;
|
||||
stbi__wp32(o, 13); // header length
|
||||
stbi__wptag(o, "IHDR");
|
||||
stbi__wp32(o, x);
|
||||
stbi__wp32(o, y);
|
||||
*o++ = 8;
|
||||
*o++ = (unsigned char) ctype[n];
|
||||
*o++ = 0;
|
||||
*o++ = 0;
|
||||
*o++ = 0;
|
||||
stbi__wpcrc(&o,13);
|
||||
|
||||
stbi__wp32(o, zlen);
|
||||
stbi__wptag(o, "IDAT");
|
||||
memcpy(o, zlib, zlen); o += zlen; free(zlib);
|
||||
stbi__wpcrc(&o, zlen);
|
||||
|
||||
stbi__wp32(o,0);
|
||||
stbi__wptag(o, "IEND");
|
||||
stbi__wpcrc(&o,0);
|
||||
|
||||
assert(o == out + *out_len);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
|
||||
{
|
||||
FILE *f;
|
||||
int len;
|
||||
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
|
||||
if (!png) return 0;
|
||||
f = fopen(filename, "wb");
|
||||
if (!f) { free(png); return 0; }
|
||||
fwrite(png, 1, len, f);
|
||||
fclose(f);
|
||||
free(png);
|
||||
return 1;
|
||||
}
|
||||
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
/* Revision history
|
||||
|
||||
0.92 (2010-08-01)
|
||||
casts to unsigned char to fix warnings
|
||||
0.91 (2010-07-17)
|
||||
first public release
|
||||
0.90 first internal release
|
||||
*/
|
66
example/svg2png.cpp
Normal file
66
example/svg2png.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <lunasvg.h>
|
||||
|
||||
using namespace lunasvg;
|
||||
|
||||
int help()
|
||||
{
|
||||
std::cout << "Usage: \n svg2png [filename] [resolution] [bgColor]\n\nExamples: \n $ svg2png input.svg\n $ svg2png input.svg 512x512\n $ svg2png input.svg 512x512 0xff00ffff\n\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool setup(int argc, char** argv, std::string& filename, std::uint32_t& width, std::uint32_t& height, std::uint32_t& bgColor)
|
||||
{
|
||||
if(argc > 1) filename.assign(argv[1]);
|
||||
if(argc > 2)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << argv[2];
|
||||
ss >> width;
|
||||
|
||||
if(ss.fail() || ss.get() != 'x')
|
||||
return false;
|
||||
|
||||
ss >> height;
|
||||
}
|
||||
|
||||
if(argc > 3)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << std::hex << argv[3];
|
||||
ss >> std::hex >> bgColor;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string filename;
|
||||
std::uint32_t width = 0, height = 0;
|
||||
std::uint32_t bgColor = 0x00000000;
|
||||
if(!setup(argc, argv, filename, width, height, bgColor)) return help();
|
||||
|
||||
auto document = Document::loadFromFile(filename);
|
||||
if(!document) return help();
|
||||
|
||||
auto bitmap = document->renderToBitmap(width, height, bgColor);
|
||||
if(!bitmap.valid()) return help();
|
||||
|
||||
auto basename = filename.substr(filename.find_last_of("/\\") + 1);
|
||||
basename.append(".png");
|
||||
|
||||
bitmap.convertToRGBA();
|
||||
stbi_write_png(basename.c_str(), int(bitmap.width()), int(bitmap.height()), 4, bitmap.data(), 0);
|
||||
|
||||
std::cout << "Generated PNG file : " << basename << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
4
include/CMakeLists.txt
Executable file
4
include/CMakeLists.txt
Executable file
|
@ -0,0 +1,4 @@
|
|||
target_include_directories(lunasvg
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
270
include/lunasvg.h
Normal file
270
include/lunasvg.h
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Nwutobo Samuel Ugochukwu <sammycageagle@gmail.com>
|
||||
*
|
||||
* 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 LUNASVG_H
|
||||
#define LUNASVG_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if defined(_MSC_VER) && defined(LUNASVG_SHARED)
|
||||
#ifdef LUNASVG_EXPORT
|
||||
#define LUNASVG_API __declspec(dllexport)
|
||||
#else
|
||||
#define LUNASVG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define LUNASVG_API
|
||||
#endif
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class Rect;
|
||||
|
||||
class LUNASVG_API Box
|
||||
{
|
||||
public:
|
||||
Box() = default;
|
||||
Box(double x, double y, double w, double h);
|
||||
Box(const Rect& rect);
|
||||
|
||||
public:
|
||||
double x{0};
|
||||
double y{0};
|
||||
double w{0};
|
||||
double h{0};
|
||||
};
|
||||
|
||||
class Transform;
|
||||
|
||||
class LUNASVG_API Matrix
|
||||
{
|
||||
public:
|
||||
Matrix() = default;
|
||||
Matrix(double a, double b, double c, double d, double e, double f);
|
||||
Matrix(const Transform& transform);
|
||||
|
||||
Matrix& rotate(double angle);
|
||||
Matrix& rotate(double angle, double cx, double cy);
|
||||
Matrix& scale(double sx, double sy);
|
||||
Matrix& shear(double shx, double shy);
|
||||
Matrix& translate(double tx, double ty);
|
||||
Matrix& transform(double a, double b, double c, double d, double e, double f);
|
||||
Matrix& identity();
|
||||
Matrix& invert();
|
||||
|
||||
Matrix& operator*=(const Matrix& matrix);
|
||||
Matrix& premultiply(const Matrix& matrix);
|
||||
Matrix& postmultiply(const Matrix& matrix);
|
||||
|
||||
Matrix inverted() const;
|
||||
Matrix operator*(const Matrix& matrix) const;
|
||||
Box map(const Box& box) const;
|
||||
|
||||
static Matrix rotated(double angle);
|
||||
static Matrix rotated(double angle, double cx, double cy);
|
||||
static Matrix scaled(double sx, double sy);
|
||||
static Matrix sheared(double shx, double shy);
|
||||
static Matrix translated(double tx, double ty);
|
||||
|
||||
public:
|
||||
double a{1};
|
||||
double b{0};
|
||||
double c{0};
|
||||
double d{1};
|
||||
double e{0};
|
||||
double f{0};
|
||||
};
|
||||
|
||||
class LUNASVG_API Bitmap
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @note Bitmap format is ARGB Premultiplied.
|
||||
*/
|
||||
Bitmap();
|
||||
Bitmap(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride);
|
||||
Bitmap(std::uint32_t width, std::uint32_t height);
|
||||
|
||||
void reset(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride);
|
||||
void reset(std::uint32_t width, std::uint32_t height);
|
||||
|
||||
std::uint8_t* data() const;
|
||||
std::uint32_t width() const;
|
||||
std::uint32_t height() const;
|
||||
std::uint32_t stride() const;
|
||||
|
||||
void clear(std::uint32_t color);
|
||||
void convert(int ri, int gi, int bi, int ai, bool unpremultiply);
|
||||
void convertToRGBA() { convert(0, 1, 2, 3, true); }
|
||||
|
||||
bool valid() const { return !!m_impl; }
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
class LayoutSymbol;
|
||||
|
||||
class LUNASVG_API Document
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a document from a file
|
||||
* @param filename - file to load
|
||||
* @return pointer to document on success, otherwise nullptr
|
||||
*/
|
||||
static std::unique_ptr<Document> loadFromFile(const std::string& filename);
|
||||
|
||||
/**
|
||||
* @brief Creates a document from a string
|
||||
* @param string - string to load
|
||||
* @return pointer to document on success, otherwise nullptr
|
||||
*/
|
||||
static std::unique_ptr<Document> loadFromData(const std::string& string);
|
||||
|
||||
/**
|
||||
* @brief Creates a document from a string data and size
|
||||
* @param data - string data to load
|
||||
* @param size - size of the data to load, in bytes
|
||||
* @return pointer to document on success, otherwise nullptr
|
||||
*/
|
||||
static std::unique_ptr<Document> loadFromData(const char* data, std::size_t size);
|
||||
|
||||
/**
|
||||
* @brief Creates a document from a null terminated string data
|
||||
* @param data - null terminated string data to load
|
||||
* @return pointer to document on success, otherwise nullptr
|
||||
*/
|
||||
static std::unique_ptr<Document> loadFromData(const char* data);
|
||||
|
||||
/**
|
||||
* @brief Pre-Rotates the document matrix clockwise around the current origin
|
||||
* @param angle - rotation angle, in degrees
|
||||
* @return this
|
||||
*/
|
||||
Document* rotate(double angle);
|
||||
|
||||
/**
|
||||
* @brief Pre-Rotates the document matrix clockwise around the given point
|
||||
* @param angle - rotation angle, in degrees
|
||||
* @param cx - horizontal translation
|
||||
* @param cy - vertical translation
|
||||
* @return this
|
||||
*/
|
||||
Document* rotate(double angle, double cx, double cy);
|
||||
|
||||
/**
|
||||
* @brief Pre-Scales the document matrix by sx horizontally and sy vertically
|
||||
* @param sx - horizontal scale factor
|
||||
* @param sy - vertical scale factor
|
||||
* @return this
|
||||
*/
|
||||
Document* scale(double sx, double sy);
|
||||
|
||||
/**
|
||||
* @brief Pre-Shears the document matrix by shx horizontally and shy vertically
|
||||
* @param shx - horizontal skew factor, in degree
|
||||
* @param shy - vertical skew factor, in degree
|
||||
* @return this
|
||||
*/
|
||||
Document* shear(double shx, double shy);
|
||||
|
||||
/**
|
||||
* @brief Pre-Translates the document matrix by tx horizontally and ty vertically
|
||||
* @param tx - horizontal translation
|
||||
* @param ty - vertical translation
|
||||
* @return this
|
||||
*/
|
||||
Document* translate(double tx, double ty);
|
||||
|
||||
/**
|
||||
* @brief Pre-Multiplies the document matrix by Matrix(a, b, c, d, e, f)
|
||||
* @param a - horizontal scale factor
|
||||
* @param b - horizontal skew factor
|
||||
* @param c - vertical skew factor
|
||||
* @param d - vertical scale factor
|
||||
* @param e - horizontal translation
|
||||
* @param f - vertical translation
|
||||
* @return this
|
||||
*/
|
||||
Document* transform(double a, double b, double c, double d, double e, double f);
|
||||
|
||||
/**
|
||||
* @brief Resets the document matrix to identity
|
||||
* @return this
|
||||
*/
|
||||
Document* identity();
|
||||
|
||||
void setMatrix(const Matrix& matrix);
|
||||
|
||||
/**
|
||||
* @brief Returns the current transformation matrix of the document
|
||||
* @return the current transformation matrix
|
||||
*/
|
||||
Matrix matrix() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the smallest rectangle in which the document fits
|
||||
* @return the smallest rectangle in which the document fits
|
||||
*/
|
||||
Box box() const;
|
||||
|
||||
/**
|
||||
* @brief Returns width of the document
|
||||
* @return the width of the document in pixels
|
||||
*/
|
||||
double width() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the height of the document
|
||||
* @return the height of the document in pixels
|
||||
*/
|
||||
double height() const;
|
||||
|
||||
/**
|
||||
* @brief Renders the document to a bitmap
|
||||
* @param matrix - the current transformation matrix
|
||||
* @param bitmap - target image on which the content will be drawn
|
||||
*/
|
||||
void render(Bitmap bitmap, const Matrix& matrix = Matrix{}) const;
|
||||
|
||||
/**
|
||||
* @brief Renders the document to a bitmap
|
||||
* @param width - maximum width, in pixels
|
||||
* @param height - maximum height, in pixels
|
||||
* @param backgroundColor - background color in 0xRRGGBBAA format
|
||||
* @return the raster representation of the document
|
||||
*/
|
||||
Bitmap renderToBitmap(std::uint32_t width = 0, std::uint32_t height = 0, std::uint32_t backgroundColor = 0x00000000) const;
|
||||
|
||||
~Document();
|
||||
private:
|
||||
Document();
|
||||
|
||||
std::unique_ptr<LayoutSymbol> root;
|
||||
};
|
||||
|
||||
} //namespace lunasvg
|
||||
|
||||
#endif // LUNASVG_H
|
28
source/CMakeLists.txt
Executable file
28
source/CMakeLists.txt
Executable file
|
@ -0,0 +1,28 @@
|
|||
target_sources(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/lunasvg.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/element.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/property.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/parser.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/layoutcontext.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/canvas.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/clippathelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/defselement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/gelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/geometryelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/graphicselement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/maskelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/markerelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/paintelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/stopelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/styledelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/styleelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/svgelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/symbolelement.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/useelement.cpp"
|
||||
)
|
||||
|
||||
target_include_directories(lunasvg
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
270
source/canvas.cpp
Normal file
270
source/canvas.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
#include "canvas.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
static plutovg_matrix_t to_plutovg_matrix(const Transform& transform);
|
||||
static plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding);
|
||||
static plutovg_operator_t to_plutovg_operator(BlendMode mode);
|
||||
static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap);
|
||||
static plutovg_line_join_t to_plutovg_line_join(LineJoin join);
|
||||
static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread);
|
||||
static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops);
|
||||
static void to_plutovg_path(plutovg_t* pluto, const Path& path);
|
||||
|
||||
std::shared_ptr<Canvas> Canvas::create(unsigned char* data, unsigned int width, unsigned int height, unsigned int stride)
|
||||
{
|
||||
return std::shared_ptr<Canvas>(new Canvas(data, static_cast<int>(width), static_cast<int>(height), static_cast<int>(stride)));
|
||||
}
|
||||
|
||||
std::shared_ptr<Canvas> Canvas::create(double x, double y, double width, double height)
|
||||
{
|
||||
if(width <= 0.0 || height <= 0.0)
|
||||
return std::shared_ptr<Canvas>(new Canvas(0, 0, 1, 1));
|
||||
|
||||
auto l = static_cast<int>(floor(x));
|
||||
auto t = static_cast<int>(floor(y));
|
||||
auto r = static_cast<int>(ceil(x + width));
|
||||
auto b = static_cast<int>(ceil(y + height));
|
||||
return std::shared_ptr<Canvas>(new Canvas(l, t, r - l, b - t));
|
||||
}
|
||||
|
||||
std::shared_ptr<Canvas> Canvas::create(const Rect& box)
|
||||
{
|
||||
return create(box.x, box.y, box.w, box.h);
|
||||
}
|
||||
|
||||
Canvas::Canvas(unsigned char* data, int width, int height, int stride)
|
||||
{
|
||||
surface = plutovg_surface_create_for_data(data, width, height, stride);
|
||||
pluto = plutovg_create(surface);
|
||||
plutovg_matrix_init_identity(&translation);
|
||||
plutovg_rect_init(&rect, 0, 0, width, height);
|
||||
}
|
||||
|
||||
Canvas::Canvas(int x, int y, int width, int height)
|
||||
{
|
||||
surface = plutovg_surface_create(width, height);
|
||||
pluto = plutovg_create(surface);
|
||||
plutovg_matrix_init_translate(&translation, -x, -y);
|
||||
plutovg_rect_init(&rect, x, y, width, height);
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
plutovg_surface_destroy(surface);
|
||||
plutovg_destroy(pluto);
|
||||
}
|
||||
|
||||
void Canvas::setColor(const Color& color)
|
||||
{
|
||||
plutovg_set_source_rgba(pluto, color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
void Canvas::setLinearGradient(double x1, double y1, double x2, double y2, const GradientStops& stops, SpreadMethod spread, const Transform& transform)
|
||||
{
|
||||
auto gradient = plutovg_gradient_create_linear(x1, y1, x2, y2);
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
to_plutovg_stops(gradient, stops);
|
||||
plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread));
|
||||
plutovg_gradient_set_matrix(gradient, &matrix);
|
||||
plutovg_set_source_gradient(pluto, gradient);
|
||||
plutovg_gradient_destroy(gradient);
|
||||
}
|
||||
|
||||
void Canvas::setRadialGradient(double cx, double cy, double r, double fx, double fy, const GradientStops& stops, SpreadMethod spread, const Transform& transform)
|
||||
{
|
||||
auto gradient = plutovg_gradient_create_radial(cx, cy, r, fx, fy, 0);
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
to_plutovg_stops(gradient, stops);
|
||||
plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread));
|
||||
plutovg_gradient_set_matrix(gradient, &matrix);
|
||||
plutovg_set_source_gradient(pluto, gradient);
|
||||
plutovg_gradient_destroy(gradient);
|
||||
}
|
||||
|
||||
void Canvas::setTexture(const Canvas* source, TextureType type, const Transform& transform)
|
||||
{
|
||||
auto texture = plutovg_texture_create(source->surface);
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
if(type == TextureType::Plain)
|
||||
plutovg_texture_set_type(texture, plutovg_texture_type_plain);
|
||||
else
|
||||
plutovg_texture_set_type(texture, plutovg_texture_type_tiled);
|
||||
|
||||
plutovg_texture_set_matrix(texture, &matrix);
|
||||
plutovg_set_source_texture(pluto, texture);
|
||||
plutovg_texture_destroy(texture);
|
||||
}
|
||||
|
||||
void Canvas::fill(const Path& path, const Transform& transform, WindRule winding, BlendMode mode, double opacity)
|
||||
{
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
plutovg_matrix_multiply(&matrix, &matrix, &translation);
|
||||
to_plutovg_path(pluto, path);
|
||||
plutovg_set_matrix(pluto, &matrix);
|
||||
plutovg_set_fill_rule(pluto, to_plutovg_fill_rule(winding));
|
||||
plutovg_set_opacity(pluto, opacity);
|
||||
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
||||
plutovg_fill(pluto);
|
||||
}
|
||||
|
||||
void Canvas::stroke(const Path& path, const Transform& transform, double width, LineCap cap, LineJoin join, double miterlimit, const DashData& dash, BlendMode mode, double opacity)
|
||||
{
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
plutovg_matrix_multiply(&matrix, &matrix, &translation);
|
||||
to_plutovg_path(pluto, path);
|
||||
plutovg_set_matrix(pluto, &matrix);
|
||||
plutovg_set_line_width(pluto, width);
|
||||
plutovg_set_line_cap(pluto, to_plutovg_line_cap(cap));
|
||||
plutovg_set_line_join(pluto, to_plutovg_line_join(join));
|
||||
plutovg_set_miter_limit(pluto, miterlimit);
|
||||
plutovg_set_dash(pluto, dash.offset, dash.array.data(), static_cast<int>(dash.array.size()));
|
||||
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
||||
plutovg_set_opacity(pluto, opacity);
|
||||
plutovg_stroke(pluto);
|
||||
}
|
||||
|
||||
void Canvas::blend(const Canvas* source, BlendMode mode, double opacity)
|
||||
{
|
||||
plutovg_set_source_surface(pluto, source->surface, source->rect.x, source->rect.y);
|
||||
plutovg_set_operator(pluto, to_plutovg_operator(mode));
|
||||
plutovg_set_opacity(pluto, opacity);
|
||||
plutovg_set_matrix(pluto, &translation);
|
||||
plutovg_paint(pluto);
|
||||
}
|
||||
|
||||
void Canvas::mask(const Rect& clip, const Transform& transform)
|
||||
{
|
||||
auto matrix = to_plutovg_matrix(transform);
|
||||
auto path = plutovg_path_create();
|
||||
plutovg_path_add_rect(path, clip.x, clip.y, clip.w, clip.h);
|
||||
plutovg_path_transform(path, &matrix);
|
||||
plutovg_rect(pluto, rect.x, rect.y, rect.w, rect.h);
|
||||
plutovg_add_path(pluto, path);
|
||||
plutovg_path_destroy(path);
|
||||
|
||||
plutovg_set_source_rgba(pluto, 0, 0, 0, 0);
|
||||
plutovg_set_fill_rule(pluto, plutovg_fill_rule_even_odd);
|
||||
plutovg_set_operator(pluto, plutovg_operator_src);
|
||||
plutovg_set_opacity(pluto, 0.0);
|
||||
plutovg_set_matrix(pluto, &translation);
|
||||
plutovg_fill(pluto);
|
||||
}
|
||||
|
||||
void Canvas::luminance()
|
||||
{
|
||||
auto width = plutovg_surface_get_width(surface);
|
||||
auto height = plutovg_surface_get_height(surface);
|
||||
auto stride = plutovg_surface_get_stride(surface);
|
||||
auto data = plutovg_surface_get_data(surface);
|
||||
for(int y = 0;y < height;y++)
|
||||
{
|
||||
auto pixels = reinterpret_cast<uint32_t*>(data + stride * y);
|
||||
for(int x = 0;x < width;x++)
|
||||
{
|
||||
auto pixel = pixels[x];
|
||||
auto r = (pixel >> 16) & 0xFF;
|
||||
auto g = (pixel >> 8) & 0xFF;
|
||||
auto b = (pixel >> 0) & 0xFF;
|
||||
auto l = (2*r + 3*g + b) / 6;
|
||||
|
||||
pixels[x] = l << 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Canvas::width() const
|
||||
{
|
||||
return plutovg_surface_get_width(surface);
|
||||
}
|
||||
|
||||
unsigned int Canvas::height() const
|
||||
{
|
||||
return plutovg_surface_get_height(surface);
|
||||
}
|
||||
|
||||
unsigned int Canvas::stride() const
|
||||
{
|
||||
return plutovg_surface_get_stride(surface);
|
||||
}
|
||||
|
||||
unsigned char* Canvas::data() const
|
||||
{
|
||||
return plutovg_surface_get_data(surface);
|
||||
}
|
||||
|
||||
Rect Canvas::box() const
|
||||
{
|
||||
return Rect(rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
|
||||
plutovg_matrix_t to_plutovg_matrix(const Transform& transform)
|
||||
{
|
||||
plutovg_matrix_t matrix;
|
||||
plutovg_matrix_init(&matrix, transform.m00, transform.m10, transform.m01, transform.m11, transform.m02, transform.m12);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding)
|
||||
{
|
||||
return winding == WindRule::EvenOdd ? plutovg_fill_rule_even_odd : plutovg_fill_rule_non_zero;
|
||||
}
|
||||
|
||||
plutovg_operator_t to_plutovg_operator(BlendMode mode)
|
||||
{
|
||||
return mode == BlendMode::Src ? plutovg_operator_src : mode == BlendMode::Src_Over ? plutovg_operator_src_over : mode == BlendMode::Dst_In ? plutovg_operator_dst_in : plutovg_operator_dst_out;
|
||||
}
|
||||
|
||||
plutovg_line_cap_t to_plutovg_line_cap(LineCap cap)
|
||||
{
|
||||
return cap == LineCap::Butt ? plutovg_line_cap_butt : cap == LineCap::Round ? plutovg_line_cap_round : plutovg_line_cap_square;
|
||||
}
|
||||
|
||||
plutovg_line_join_t to_plutovg_line_join(LineJoin join)
|
||||
{
|
||||
return join == LineJoin::Miter ? plutovg_line_join_miter : join == LineJoin::Round ? plutovg_line_join_round : plutovg_line_join_bevel;
|
||||
}
|
||||
|
||||
static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread)
|
||||
{
|
||||
return spread == SpreadMethod::Pad ? plutovg_spread_method_pad : spread == SpreadMethod::Reflect ? plutovg_spread_method_reflect : plutovg_spread_method_repeat;
|
||||
}
|
||||
|
||||
static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops)
|
||||
{
|
||||
for(const auto& stop : stops)
|
||||
{
|
||||
auto offset = std::get<0>(stop);
|
||||
auto& color = std::get<1>(stop);
|
||||
plutovg_gradient_add_stop_rgba(gradient, offset, color.r, color.g, color.b, color.a);
|
||||
}
|
||||
}
|
||||
|
||||
void to_plutovg_path(plutovg_t* pluto, const Path& path)
|
||||
{
|
||||
PathIterator it(path);
|
||||
std::array<Point, 3> p;
|
||||
while(!it.isDone())
|
||||
{
|
||||
switch(it.currentSegment(p)) {
|
||||
case PathCommand::MoveTo:
|
||||
plutovg_move_to(pluto, p[0].x, p[0].y);
|
||||
break;
|
||||
case PathCommand::LineTo:
|
||||
plutovg_line_to(pluto, p[0].x, p[0].y);
|
||||
break;
|
||||
case PathCommand::CubicTo:
|
||||
plutovg_cubic_to(pluto, p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y);
|
||||
break;
|
||||
case PathCommand::Close:
|
||||
plutovg_close_path(pluto);
|
||||
break;
|
||||
}
|
||||
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
76
source/canvas.h
Normal file
76
source/canvas.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef CANVAS_H
|
||||
#define CANVAS_H
|
||||
|
||||
#include "property.h"
|
||||
#include "plutovg.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
using GradientStop = std::pair<double, Color>;
|
||||
using GradientStops = std::vector<GradientStop>;
|
||||
|
||||
using DashArray = std::vector<double>;
|
||||
|
||||
struct DashData
|
||||
{
|
||||
DashArray array;
|
||||
double offset{0.0};
|
||||
};
|
||||
|
||||
enum class TextureType
|
||||
{
|
||||
Plain,
|
||||
Tiled
|
||||
};
|
||||
|
||||
enum class BlendMode
|
||||
{
|
||||
Src,
|
||||
Src_Over,
|
||||
Dst_In,
|
||||
Dst_Out
|
||||
};
|
||||
|
||||
class CanvasImpl;
|
||||
|
||||
class Canvas
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<Canvas> create(unsigned char* data, unsigned int width, unsigned int height, unsigned int stride);
|
||||
static std::shared_ptr<Canvas> create(double x, double y, double width, double height);
|
||||
static std::shared_ptr<Canvas> create(const Rect& box);
|
||||
|
||||
void setColor(const Color& color);
|
||||
void setLinearGradient(double x1, double y1, double x2, double y2, const GradientStops& stops, SpreadMethod spread, const Transform& transform);
|
||||
void setRadialGradient(double cx, double cy, double r, double fx, double fy, const GradientStops& stops, SpreadMethod spread, const Transform& transform);
|
||||
void setTexture(const Canvas* source, TextureType type, const Transform& transform);
|
||||
|
||||
void fill(const Path& path, const Transform& transform, WindRule winding, BlendMode mode, double opacity);
|
||||
void stroke(const Path& path, const Transform& transform, double width, LineCap cap, LineJoin join, double miterlimit, const DashData& dash, BlendMode mode, double opacity);
|
||||
void blend(const Canvas* source, BlendMode mode, double opacity);
|
||||
void mask(const Rect& clip, const Transform& transform);
|
||||
|
||||
void luminance();
|
||||
|
||||
unsigned int width() const;
|
||||
unsigned int height() const;
|
||||
unsigned int stride() const;
|
||||
unsigned char* data() const;
|
||||
Rect box() const;
|
||||
|
||||
~Canvas();
|
||||
private:
|
||||
Canvas(unsigned char* data, int width, int height, int stride);
|
||||
Canvas(int x, int y, int width, int height);
|
||||
|
||||
plutovg_surface_t* surface;
|
||||
plutovg_t* pluto;
|
||||
plutovg_matrix_t translation;
|
||||
plutovg_rect_t rect;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // CANVAS_H
|
37
source/clippathelement.cpp
Normal file
37
source/clippathelement.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "clippathelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
ClipPathElement::ClipPathElement()
|
||||
: GraphicsElement(ElementId::ClipPath)
|
||||
{
|
||||
}
|
||||
|
||||
Units ClipPathElement::clipPathUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::ClipPathUnits);
|
||||
return Parser::parseUnits(value, Units::UserSpaceOnUse);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutClipPath> ClipPathElement::getClipper(LayoutContext* context) const
|
||||
{
|
||||
if(context->hasReference(this))
|
||||
return nullptr;
|
||||
|
||||
LayoutBreaker layoutBreaker(context, this);
|
||||
auto clipper = std::make_unique<LayoutClipPath>();
|
||||
clipper->units = clipPathUnits();
|
||||
clipper->transform = transform();
|
||||
clipper->clipper = context->getClipper(clip_path());
|
||||
layoutChildren(context, clipper.get());
|
||||
return clipper;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> ClipPathElement::clone() const
|
||||
{
|
||||
return cloneElement<ClipPathElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
23
source/clippathelement.h
Normal file
23
source/clippathelement.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef CLIPPATHELEMENT_H
|
||||
#define CLIPPATHELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class LayoutClipPath;
|
||||
|
||||
class ClipPathElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
ClipPathElement();
|
||||
|
||||
Units clipPathUnits() const;
|
||||
std::unique_ptr<LayoutClipPath> getClipper(LayoutContext* context) const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // CLIPPATHELEMENT_H
|
15
source/defselement.cpp
Normal file
15
source/defselement.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "defselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
DefsElement::DefsElement()
|
||||
: GraphicsElement(ElementId::Defs)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> DefsElement::clone() const
|
||||
{
|
||||
return cloneElement<DefsElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
18
source/defselement.h
Normal file
18
source/defselement.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef DEFSELEMENT_H
|
||||
#define DEFSELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class DefsElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
DefsElement();
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // DEFSELEMENT_H
|
187
source/element.cpp
Normal file
187
source/element.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
#include "element.h"
|
||||
#include "parser.h"
|
||||
#include "svgelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
void PropertyList::set(PropertyId id, const std::string& value, int specificity)
|
||||
{
|
||||
auto property = get(id);
|
||||
if(property == nullptr)
|
||||
{
|
||||
Property property{id, value, specificity};
|
||||
m_properties.push_back(std::move(property));
|
||||
return;
|
||||
}
|
||||
|
||||
if(property->specificity > specificity)
|
||||
return;
|
||||
|
||||
property->specificity = specificity;
|
||||
property->value = value;
|
||||
}
|
||||
|
||||
Property* PropertyList::get(PropertyId id) const
|
||||
{
|
||||
auto data = m_properties.data();
|
||||
auto end = data + m_properties.size();
|
||||
while(data < end)
|
||||
{
|
||||
if(data->id == id)
|
||||
return const_cast<Property*>(data);
|
||||
++data;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PropertyList::add(const Property& property)
|
||||
{
|
||||
set(property.id, property.value, property.specificity);
|
||||
}
|
||||
|
||||
void PropertyList::add(const PropertyList& properties)
|
||||
{
|
||||
auto it = properties.m_properties.begin();
|
||||
auto end = properties.m_properties.end();
|
||||
for(;it != end;++it)
|
||||
add(*it);
|
||||
}
|
||||
|
||||
void Node::layout(LayoutContext*, LayoutContainer*) const
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> TextNode::clone() const
|
||||
{
|
||||
auto node = std::make_unique<TextNode>();
|
||||
node->text = text;
|
||||
return std::move(node);
|
||||
}
|
||||
|
||||
Element::Element(ElementId id)
|
||||
: id(id)
|
||||
{
|
||||
}
|
||||
|
||||
void Element::set(PropertyId id, const std::string& value, int specificity)
|
||||
{
|
||||
properties.set(id, value, specificity);
|
||||
}
|
||||
|
||||
static const std::string EmptyString;
|
||||
|
||||
const std::string& Element::get(PropertyId id) const
|
||||
{
|
||||
auto property = properties.get(id);
|
||||
if(property == nullptr)
|
||||
return EmptyString;
|
||||
|
||||
return property->value;
|
||||
}
|
||||
|
||||
static const std::string InheritString{"inherit"};
|
||||
|
||||
const std::string& Element::find(PropertyId id) const
|
||||
{
|
||||
auto element = this;
|
||||
do {
|
||||
auto& value = element->get(id);
|
||||
if(!value.empty() && value != InheritString)
|
||||
return value;
|
||||
element = element->parent;
|
||||
} while(element);
|
||||
|
||||
return EmptyString;
|
||||
}
|
||||
|
||||
bool Element::has(PropertyId id) const
|
||||
{
|
||||
return properties.get(id);
|
||||
}
|
||||
|
||||
Element* Element::previousSibling() const
|
||||
{
|
||||
if(parent == nullptr)
|
||||
return nullptr;
|
||||
|
||||
Element* element = nullptr;
|
||||
auto it = parent->children.begin();
|
||||
auto end = parent->children.end();
|
||||
for(;it != end;++it)
|
||||
{
|
||||
auto node = it->get();
|
||||
if(node->isText())
|
||||
continue;
|
||||
|
||||
if(node == this)
|
||||
return element;
|
||||
element = static_cast<Element*>(node);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Element* Element::nextSibling() const
|
||||
{
|
||||
if(parent == nullptr)
|
||||
return nullptr;
|
||||
|
||||
Element* element = nullptr;
|
||||
auto it = parent->children.rbegin();
|
||||
auto end = parent->children.rend();
|
||||
for(;it != end;++it)
|
||||
{
|
||||
auto node = it->get();
|
||||
if(node->isText())
|
||||
continue;
|
||||
|
||||
if(node == this)
|
||||
return element;
|
||||
element = static_cast<Element*>(node);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node* Element::addChild(std::unique_ptr<Node> child)
|
||||
{
|
||||
child->parent = this;
|
||||
children.push_back(std::move(child));
|
||||
return &*children.back();
|
||||
}
|
||||
|
||||
void Element::layoutChildren(LayoutContext* context, LayoutContainer* current) const
|
||||
{
|
||||
for(auto& child : children)
|
||||
child->layout(context, current);
|
||||
}
|
||||
|
||||
Rect Element::currentViewport() const
|
||||
{
|
||||
if(parent == nullptr)
|
||||
{
|
||||
auto element = static_cast<const SVGElement*>(this);
|
||||
if(element->has(PropertyId::ViewBox))
|
||||
return element->viewBox();
|
||||
return Rect{0, 0, 300, 150};
|
||||
}
|
||||
|
||||
if(parent->id == ElementId::Svg)
|
||||
{
|
||||
auto element = static_cast<SVGElement*>(parent);
|
||||
if(element->has(PropertyId::ViewBox))
|
||||
return element->viewBox();
|
||||
|
||||
LengthContext lengthContext(element);
|
||||
auto _x = lengthContext.valueForLength(element->x(), LengthMode::Width);
|
||||
auto _y = lengthContext.valueForLength(element->y(), LengthMode::Height);
|
||||
auto _w = lengthContext.valueForLength(element->width(), LengthMode::Width);
|
||||
auto _h = lengthContext.valueForLength(element->height(), LengthMode::Height);
|
||||
return Rect{_x, _y, _w, _h};
|
||||
}
|
||||
|
||||
return parent->currentViewport();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
218
source/element.h
Normal file
218
source/element.h
Normal file
|
@ -0,0 +1,218 @@
|
|||
#ifndef ELEMENT_H
|
||||
#define ELEMENT_H
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include "property.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
enum class ElementId
|
||||
{
|
||||
Unknown = 0,
|
||||
Star,
|
||||
Circle,
|
||||
ClipPath,
|
||||
Defs,
|
||||
Ellipse,
|
||||
G,
|
||||
Line,
|
||||
LinearGradient,
|
||||
Marker,
|
||||
Mask,
|
||||
Path,
|
||||
Pattern,
|
||||
Polygon,
|
||||
Polyline,
|
||||
RadialGradient,
|
||||
Rect,
|
||||
SolidColor,
|
||||
Stop,
|
||||
Style,
|
||||
Svg,
|
||||
Symbol,
|
||||
Use
|
||||
};
|
||||
|
||||
enum class PropertyId
|
||||
{
|
||||
Unknown = 0,
|
||||
Class,
|
||||
Clip_Path,
|
||||
Clip_Rule,
|
||||
ClipPathUnits,
|
||||
Color,
|
||||
Cx,
|
||||
Cy,
|
||||
D,
|
||||
Display,
|
||||
Fill,
|
||||
Fill_Opacity,
|
||||
Fill_Rule,
|
||||
Fx,
|
||||
Fy,
|
||||
GradientTransform,
|
||||
GradientUnits,
|
||||
Height,
|
||||
Href,
|
||||
Id,
|
||||
Marker_End,
|
||||
Marker_Mid,
|
||||
Marker_Start,
|
||||
MarkerHeight,
|
||||
MarkerUnits,
|
||||
MarkerWidth,
|
||||
Mask,
|
||||
MaskContentUnits,
|
||||
MaskUnits,
|
||||
Offset,
|
||||
Opacity,
|
||||
Orient,
|
||||
Overflow,
|
||||
PatternContentUnits,
|
||||
PatternTransform,
|
||||
PatternUnits,
|
||||
Points,
|
||||
PreserveAspectRatio,
|
||||
R,
|
||||
RefX,
|
||||
RefY,
|
||||
Rx,
|
||||
Ry,
|
||||
Solid_Color,
|
||||
Solid_Opacity,
|
||||
SpreadMethod,
|
||||
Stop_Color,
|
||||
Stop_Opacity,
|
||||
Stroke,
|
||||
Stroke_Dasharray,
|
||||
Stroke_Dashoffset,
|
||||
Stroke_Linecap,
|
||||
Stroke_Linejoin,
|
||||
Stroke_Miterlimit,
|
||||
Stroke_Opacity,
|
||||
Stroke_Width,
|
||||
Style,
|
||||
Transform,
|
||||
ViewBox,
|
||||
Visibility,
|
||||
Width,
|
||||
X,
|
||||
X1,
|
||||
X2,
|
||||
Y,
|
||||
Y1,
|
||||
Y2
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
PropertyId id;
|
||||
std::string value;
|
||||
int specificity;
|
||||
};
|
||||
|
||||
class PropertyList
|
||||
{
|
||||
public:
|
||||
PropertyList() = default;
|
||||
|
||||
void set(PropertyId id, const std::string& value, int specificity);
|
||||
Property* get(PropertyId id) const;
|
||||
void add(const Property& property);
|
||||
void add(const PropertyList& properties);
|
||||
|
||||
private:
|
||||
std::vector<Property> m_properties;
|
||||
};
|
||||
|
||||
class LayoutContext;
|
||||
class LayoutContainer;
|
||||
class Element;
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node() = default;
|
||||
virtual ~Node() = default;
|
||||
|
||||
virtual bool isText() const { return false; }
|
||||
virtual bool isPaint() const { return false; }
|
||||
virtual bool isGeometry() const { return false; }
|
||||
virtual void layout(LayoutContext*, LayoutContainer*) const;
|
||||
virtual std::unique_ptr<Node> clone() const = 0;
|
||||
|
||||
public:
|
||||
Element* parent = nullptr;
|
||||
};
|
||||
|
||||
class TextNode : public Node
|
||||
{
|
||||
public:
|
||||
TextNode() = default;
|
||||
|
||||
bool isText() const { return true; }
|
||||
std::unique_ptr<Node> clone() const;
|
||||
|
||||
public:
|
||||
std::string text;
|
||||
};
|
||||
|
||||
using NodeList = std::list<std::unique_ptr<Node>>;
|
||||
|
||||
class Element : public Node
|
||||
{
|
||||
public:
|
||||
Element(ElementId id);
|
||||
|
||||
void set(PropertyId id, const std::string& value, int specificity);
|
||||
const std::string& get(PropertyId id) const;
|
||||
const std::string& find(PropertyId id) const;
|
||||
bool has(PropertyId id) const;
|
||||
|
||||
Element* previousSibling() const;
|
||||
Element* nextSibling() const;
|
||||
Node* addChild(std::unique_ptr<Node> child);
|
||||
void layoutChildren(LayoutContext* context, LayoutContainer* current) const;
|
||||
Rect currentViewport() const;
|
||||
|
||||
template<typename T>
|
||||
void transverse(T callback)
|
||||
{
|
||||
if(callback(this))
|
||||
return;
|
||||
|
||||
for(auto& child : children)
|
||||
{
|
||||
if(child->isText())
|
||||
{
|
||||
if(callback(child.get()))
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto element = static_cast<Element*>(child.get());
|
||||
element->transverse(callback);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::unique_ptr<T> cloneElement() const
|
||||
{
|
||||
auto element = std::make_unique<T>();
|
||||
element->properties = properties;
|
||||
for(auto& child : children)
|
||||
element->addChild(child->clone());
|
||||
return element;
|
||||
}
|
||||
|
||||
public:
|
||||
ElementId id;
|
||||
NodeList children;
|
||||
PropertyList properties;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // ELEMENT_H
|
30
source/gelement.cpp
Normal file
30
source/gelement.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "gelement.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
GElement::GElement()
|
||||
: GraphicsElement(ElementId::G)
|
||||
{
|
||||
}
|
||||
|
||||
void GElement::layout(LayoutContext* context, LayoutContainer* current) const
|
||||
{
|
||||
if(isDisplayNone())
|
||||
return;
|
||||
|
||||
auto group = std::make_unique<LayoutGroup>();
|
||||
group->transform = transform();
|
||||
group->opacity = opacity();
|
||||
group->masker = context->getMasker(mask());
|
||||
group->clipper = context->getClipper(clip_path());
|
||||
layoutChildren(context, group.get());
|
||||
current->addChildIfNotEmpty(std::move(group));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> GElement::clone() const
|
||||
{
|
||||
return cloneElement<GElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
19
source/gelement.h
Normal file
19
source/gelement.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef GELEMENT_H
|
||||
#define GELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class GElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
GElement();
|
||||
|
||||
void layout(LayoutContext* context, LayoutContainer* current) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // GELEMENT_H
|
335
source/geometryelement.cpp
Normal file
335
source/geometryelement.cpp
Normal file
|
@ -0,0 +1,335 @@
|
|||
#include "geometryelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
GeometryElement::GeometryElement(ElementId id)
|
||||
: GraphicsElement(id)
|
||||
{
|
||||
}
|
||||
|
||||
void GeometryElement::layout(LayoutContext* context, LayoutContainer* current) const
|
||||
{
|
||||
if(isDisplayNone())
|
||||
return;
|
||||
|
||||
auto path = this->path();
|
||||
if(path.empty())
|
||||
return;
|
||||
|
||||
auto shape = std::make_unique<LayoutShape>();
|
||||
shape->path = std::move(path);
|
||||
shape->transform = transform();
|
||||
shape->fillData = context->fillData(this);
|
||||
shape->strokeData = context->strokeData(this);
|
||||
shape->markerData = context->markerData(this, shape->path);
|
||||
shape->visibility = visibility();
|
||||
shape->clipRule = clip_rule();
|
||||
shape->opacity = opacity();
|
||||
shape->masker = context->getMasker(mask());
|
||||
shape->clipper = context->getClipper(clip_path());
|
||||
current->addChild(std::move(shape));
|
||||
}
|
||||
|
||||
PathElement::PathElement()
|
||||
: GeometryElement(ElementId::Path)
|
||||
{
|
||||
}
|
||||
|
||||
Path PathElement::d() const
|
||||
{
|
||||
auto& value = get(PropertyId::D);
|
||||
return Parser::parsePath(value);
|
||||
}
|
||||
|
||||
Path PathElement::path() const
|
||||
{
|
||||
return d();
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> PathElement::clone() const
|
||||
{
|
||||
return cloneElement<PathElement>();
|
||||
}
|
||||
|
||||
PolyElement::PolyElement(ElementId id)
|
||||
: GeometryElement(id)
|
||||
{
|
||||
}
|
||||
|
||||
PointList PolyElement::points() const
|
||||
{
|
||||
auto& value = get(PropertyId::Points);
|
||||
return Parser::parsePointList(value);
|
||||
}
|
||||
|
||||
PolygonElement::PolygonElement()
|
||||
: PolyElement(ElementId::Polygon)
|
||||
{
|
||||
}
|
||||
|
||||
Path PolygonElement::path() const
|
||||
{
|
||||
auto points = this->points();
|
||||
if(points.empty())
|
||||
return Path{};
|
||||
|
||||
Path path;
|
||||
path.moveTo(points[0].x, points[0].y);
|
||||
for(std::size_t i = 1;i < points.size();i++)
|
||||
path.lineTo(points[i].x, points[i].y);
|
||||
|
||||
path.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> PolygonElement::clone() const
|
||||
{
|
||||
return cloneElement<PolygonElement>();
|
||||
}
|
||||
|
||||
PolylineElement::PolylineElement()
|
||||
: PolyElement(ElementId::Polyline)
|
||||
{
|
||||
}
|
||||
|
||||
Path PolylineElement::path() const
|
||||
{
|
||||
auto points = this->points();
|
||||
if(points.empty())
|
||||
return Path{};
|
||||
|
||||
Path path;
|
||||
path.moveTo(points[0].x, points[0].y);
|
||||
for(std::size_t i = 1;i < points.size();i++)
|
||||
path.lineTo(points[i].x, points[i].y);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> PolylineElement::clone() const
|
||||
{
|
||||
return cloneElement<PolylineElement>();
|
||||
}
|
||||
|
||||
CircleElement::CircleElement()
|
||||
: GeometryElement(ElementId::Circle)
|
||||
{
|
||||
}
|
||||
|
||||
Length CircleElement::cx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cx);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length CircleElement::cy() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cy);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length CircleElement::r() const
|
||||
{
|
||||
auto& value = get(PropertyId::R);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Path CircleElement::path() const
|
||||
{
|
||||
auto r = this->r();
|
||||
if(r.isZero())
|
||||
return Path{};
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _cx = lengthContext.valueForLength(cx(), LengthMode::Width);
|
||||
auto _cy = lengthContext.valueForLength(cy(), LengthMode::Height);
|
||||
auto _r = lengthContext.valueForLength(r, LengthMode::Both);
|
||||
|
||||
Path path;
|
||||
path.ellipse(_cx, _cy, _r, _r);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> CircleElement::clone() const
|
||||
{
|
||||
return cloneElement<CircleElement>();
|
||||
}
|
||||
|
||||
EllipseElement::EllipseElement()
|
||||
: GeometryElement(ElementId::Ellipse)
|
||||
{
|
||||
}
|
||||
|
||||
Length EllipseElement::cx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cx);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length EllipseElement::cy() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cy);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length EllipseElement::rx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Rx);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length EllipseElement::ry() const
|
||||
{
|
||||
auto& value = get(PropertyId::Ry);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Path EllipseElement::path() const
|
||||
{
|
||||
auto rx = this->rx();
|
||||
auto ry = this->ry();
|
||||
if(rx.isZero() || ry.isZero())
|
||||
return Path{};
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _cx = lengthContext.valueForLength(cx(), LengthMode::Width);
|
||||
auto _cy = lengthContext.valueForLength(cy(), LengthMode::Height);
|
||||
auto _rx = lengthContext.valueForLength(rx, LengthMode::Width);
|
||||
auto _ry = lengthContext.valueForLength(ry, LengthMode::Height);
|
||||
|
||||
Path path;
|
||||
path.ellipse(_cx, _cy, _rx, _ry);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> EllipseElement::clone() const
|
||||
{
|
||||
return cloneElement<EllipseElement>();
|
||||
}
|
||||
|
||||
LineElement::LineElement()
|
||||
: GeometryElement(ElementId::Line)
|
||||
{
|
||||
}
|
||||
|
||||
Length LineElement::x1() const
|
||||
{
|
||||
auto& value = get(PropertyId::X1);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length LineElement::y1() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y1);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length LineElement::x2() const
|
||||
{
|
||||
auto& value = get(PropertyId::X2);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length LineElement::y2() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y2);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Path LineElement::path() const
|
||||
{
|
||||
LengthContext lengthContext(this);
|
||||
auto _x1 = lengthContext.valueForLength(x1(), LengthMode::Width);
|
||||
auto _y1 = lengthContext.valueForLength(y1(), LengthMode::Height);
|
||||
auto _x2 = lengthContext.valueForLength(x2(), LengthMode::Width);
|
||||
auto _y2 = lengthContext.valueForLength(y2(), LengthMode::Height);
|
||||
|
||||
Path path;
|
||||
path.moveTo(_x1, _y1);
|
||||
path.lineTo(_x2, _y2);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> LineElement::clone() const
|
||||
{
|
||||
return cloneElement<LineElement>();
|
||||
}
|
||||
|
||||
RectElement::RectElement()
|
||||
: GeometryElement(ElementId::Rect)
|
||||
{
|
||||
}
|
||||
|
||||
Length RectElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length RectElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length RectElement::rx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Rx);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown);
|
||||
}
|
||||
|
||||
Length RectElement::ry() const
|
||||
{
|
||||
auto& value = get(PropertyId::Ry);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown);
|
||||
}
|
||||
|
||||
Length RectElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length RectElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Path RectElement::path() const
|
||||
{
|
||||
auto w = this->width();
|
||||
auto h = this->height();
|
||||
if(w.isZero() || h.isZero())
|
||||
return Path{};
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _x = lengthContext.valueForLength(x(), LengthMode::Width);
|
||||
auto _y = lengthContext.valueForLength(y(), LengthMode::Height);
|
||||
auto _w = lengthContext.valueForLength(w, LengthMode::Width);
|
||||
auto _h = lengthContext.valueForLength(h, LengthMode::Height);
|
||||
|
||||
auto rx = this->rx();
|
||||
auto ry = this->ry();
|
||||
|
||||
auto _rx = lengthContext.valueForLength(rx, LengthMode::Width);
|
||||
auto _ry = lengthContext.valueForLength(ry, LengthMode::Height);
|
||||
|
||||
if(!rx.isValid()) _rx = _ry;
|
||||
if(!ry.isValid()) _ry = _rx;
|
||||
|
||||
Path path;
|
||||
path.rect(_x, _y, _w, _h, _rx, _ry);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> RectElement::clone() const
|
||||
{
|
||||
return cloneElement<RectElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
122
source/geometryelement.h
Normal file
122
source/geometryelement.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
#ifndef GEOMETRYELEMENT_H
|
||||
#define GEOMETRYELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class LayoutShape;
|
||||
|
||||
class GeometryElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
GeometryElement(ElementId id);
|
||||
|
||||
bool isGeometry() const { return true; }
|
||||
virtual void layout(LayoutContext* context, LayoutContainer* current) const;
|
||||
virtual Path path() const = 0;
|
||||
};
|
||||
|
||||
class PathElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
PathElement();
|
||||
|
||||
Path d() const;
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class PolyElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
PolyElement(ElementId id);
|
||||
|
||||
PointList points() const;
|
||||
};
|
||||
|
||||
class PolygonElement : public PolyElement
|
||||
{
|
||||
public:
|
||||
PolygonElement();
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class PolylineElement : public PolyElement
|
||||
{
|
||||
public:
|
||||
PolylineElement();
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class CircleElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
CircleElement();
|
||||
|
||||
Length cx() const;
|
||||
Length cy() const;
|
||||
Length r() const;
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class EllipseElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
EllipseElement();
|
||||
|
||||
Length cx() const;
|
||||
Length cy() const;
|
||||
Length rx() const;
|
||||
Length ry() const;
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class LineElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
LineElement();
|
||||
|
||||
Length x1() const;
|
||||
Length y1() const;
|
||||
Length x2() const;
|
||||
Length y2() const;
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class RectElement : public GeometryElement
|
||||
{
|
||||
public:
|
||||
RectElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length rx() const;
|
||||
Length ry() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
|
||||
Path path() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // GEOMETRYELEMENT_H
|
17
source/graphicselement.cpp
Normal file
17
source/graphicselement.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "graphicselement.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
GraphicsElement::GraphicsElement(ElementId id)
|
||||
: StyledElement(id)
|
||||
{
|
||||
}
|
||||
|
||||
Transform GraphicsElement::transform() const
|
||||
{
|
||||
auto& value = get(PropertyId::Transform);
|
||||
return Parser::parseTransform(value);
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
18
source/graphicselement.h
Normal file
18
source/graphicselement.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef GRAPHICSELEMENT_H
|
||||
#define GRAPHICSELEMENT_H
|
||||
|
||||
#include "styledelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class GraphicsElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
GraphicsElement(ElementId id);
|
||||
|
||||
Transform transform() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // GRAPHICSELEMENT_H
|
766
source/layoutcontext.cpp
Normal file
766
source/layoutcontext.cpp
Normal file
|
@ -0,0 +1,766 @@
|
|||
#include "layoutcontext.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include "maskelement.h"
|
||||
#include "clippathelement.h"
|
||||
#include "paintelement.h"
|
||||
#include "markerelement.h"
|
||||
#include "geometryelement.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
LayoutObject::LayoutObject(LayoutId id)
|
||||
: id(id)
|
||||
{
|
||||
}
|
||||
|
||||
LayoutObject::~LayoutObject()
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutObject::render(RenderState&) const
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutObject::apply(RenderState&) const
|
||||
{
|
||||
}
|
||||
|
||||
Rect LayoutObject::map(const Rect&) const
|
||||
{
|
||||
return Rect::Invalid;
|
||||
}
|
||||
|
||||
LayoutContainer::LayoutContainer(LayoutId id)
|
||||
: LayoutObject(id)
|
||||
{
|
||||
}
|
||||
|
||||
const Rect& LayoutContainer::fillBoundingBox() const
|
||||
{
|
||||
if(m_fillBoundingBox.valid())
|
||||
return m_fillBoundingBox;
|
||||
|
||||
for(const auto& child : children)
|
||||
{
|
||||
if(child->isHidden())
|
||||
continue;
|
||||
m_fillBoundingBox.unite(child->map(child->fillBoundingBox()));
|
||||
}
|
||||
|
||||
return m_fillBoundingBox;
|
||||
}
|
||||
|
||||
const Rect& LayoutContainer::strokeBoundingBox() const
|
||||
{
|
||||
if(m_strokeBoundingBox.valid())
|
||||
return m_strokeBoundingBox;
|
||||
|
||||
for(const auto& child : children)
|
||||
{
|
||||
if(child->isHidden())
|
||||
continue;
|
||||
m_strokeBoundingBox.unite(child->map(child->strokeBoundingBox()));
|
||||
}
|
||||
|
||||
return m_strokeBoundingBox;
|
||||
}
|
||||
|
||||
LayoutObject* LayoutContainer::addChild(std::unique_ptr<LayoutObject> child)
|
||||
{
|
||||
children.push_back(std::move(child));
|
||||
return &*children.back();
|
||||
}
|
||||
|
||||
LayoutObject* LayoutContainer::addChildIfNotEmpty(std::unique_ptr<LayoutContainer> child)
|
||||
{
|
||||
if(child->children.empty())
|
||||
return nullptr;
|
||||
|
||||
return addChild(std::move(child));
|
||||
}
|
||||
|
||||
void LayoutContainer::renderChildren(RenderState& state) const
|
||||
{
|
||||
for(const auto& child : children)
|
||||
child->render(state);
|
||||
}
|
||||
|
||||
LayoutClipPath::LayoutClipPath()
|
||||
: LayoutContainer(LayoutId::ClipPath)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutClipPath::apply(RenderState& state) const
|
||||
{
|
||||
RenderState newState(this, RenderMode::Clipping);
|
||||
newState.canvas = Canvas::create(state.canvas->box());
|
||||
newState.transform = transform * state.transform;
|
||||
if(units == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
newState.transform.translate(box.x, box.y);
|
||||
newState.transform.scale(box.w, box.h);
|
||||
}
|
||||
|
||||
renderChildren(newState);
|
||||
if(clipper) clipper->apply(newState);
|
||||
state.canvas->blend(newState.canvas.get(), BlendMode::Dst_In, 1.0);
|
||||
}
|
||||
|
||||
LayoutMask::LayoutMask()
|
||||
: LayoutContainer(LayoutId::Mask)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutMask::apply(RenderState& state) const
|
||||
{
|
||||
Rect rect{x, y, width, height};
|
||||
if(units == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
rect.x = rect.x * box.w + box.x;
|
||||
rect.y = rect.y * box.h + box.y;
|
||||
rect.w = rect.w * box.w;
|
||||
rect.h = rect.h * box.h;
|
||||
}
|
||||
|
||||
RenderState newState(this, state.mode());
|
||||
newState.canvas = Canvas::create(state.canvas->box());
|
||||
newState.transform = state.transform;
|
||||
if(contentUnits == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
newState.transform.translate(box.x, box.y);
|
||||
newState.transform.scale(box.w, box.h);
|
||||
}
|
||||
|
||||
renderChildren(newState);
|
||||
if(clipper) clipper->apply(newState);
|
||||
if(masker) masker->apply(newState);
|
||||
newState.canvas->mask(rect, state.transform);
|
||||
newState.canvas->luminance();
|
||||
state.canvas->blend(newState.canvas.get(), BlendMode::Dst_In, opacity);
|
||||
}
|
||||
|
||||
LayoutSymbol::LayoutSymbol()
|
||||
: LayoutContainer(LayoutId::Symbol)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutSymbol::render(RenderState& state) const
|
||||
{
|
||||
BlendInfo info{clipper, masker, opacity, clip};
|
||||
RenderState newState(this, state.mode());
|
||||
newState.transform = transform * state.transform;
|
||||
newState.beginGroup(state, info);
|
||||
renderChildren(newState);
|
||||
newState.endGroup(state, info);
|
||||
}
|
||||
|
||||
Rect LayoutSymbol::map(const Rect& rect) const
|
||||
{
|
||||
return transform.map(rect);
|
||||
}
|
||||
|
||||
LayoutGroup::LayoutGroup()
|
||||
: LayoutContainer(LayoutId::Group)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutGroup::render(RenderState& state) const
|
||||
{
|
||||
BlendInfo info{clipper, masker, opacity, Rect::Invalid};
|
||||
RenderState newState(this, state.mode());
|
||||
newState.transform = transform * state.transform;
|
||||
newState.beginGroup(state, info);
|
||||
renderChildren(newState);
|
||||
newState.endGroup(state, info);
|
||||
}
|
||||
|
||||
Rect LayoutGroup::map(const Rect& rect) const
|
||||
{
|
||||
return transform.map(rect);
|
||||
}
|
||||
|
||||
LayoutMarker::LayoutMarker()
|
||||
: LayoutContainer(LayoutId::Marker)
|
||||
{
|
||||
}
|
||||
|
||||
Transform LayoutMarker::markerTransform(const Point& origin, double angle, double strokeWidth) const
|
||||
{
|
||||
auto transform = Transform::translated(origin.x, origin.y);
|
||||
if(orient.type() == MarkerOrient::Auto)
|
||||
transform.rotate(angle);
|
||||
else
|
||||
transform.rotate(orient.value());
|
||||
|
||||
if(units == MarkerUnits::StrokeWidth)
|
||||
transform.scale(strokeWidth, strokeWidth);
|
||||
|
||||
transform.translate(-refX, -refY);
|
||||
return transform;
|
||||
}
|
||||
|
||||
Rect LayoutMarker::markerBoundingBox(const Point& origin, double angle, double strokeWidth) const
|
||||
{
|
||||
auto box = transform.map(strokeBoundingBox());
|
||||
auto transform = markerTransform(origin, angle, strokeWidth);
|
||||
return transform.map(box);
|
||||
}
|
||||
|
||||
void LayoutMarker::renderMarker(RenderState& state, const Point& origin, double angle, double strokeWidth) const
|
||||
{
|
||||
BlendInfo info{clipper, masker, opacity, clip};
|
||||
RenderState newState(this, state.mode());
|
||||
newState.transform = transform * markerTransform(origin, angle, strokeWidth) * state.transform;
|
||||
newState.beginGroup(state, info);
|
||||
renderChildren(newState);
|
||||
newState.endGroup(state, info);
|
||||
}
|
||||
|
||||
LayoutPattern::LayoutPattern()
|
||||
: LayoutContainer(LayoutId::Pattern)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutPattern::apply(RenderState& state) const
|
||||
{
|
||||
Rect rect{x, y, width, height};
|
||||
if(units == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
rect.x = rect.x * box.w + box.x;
|
||||
rect.y = rect.y * box.h + box.y;
|
||||
rect.w = rect.w * box.w;
|
||||
rect.h = rect.h * box.h;
|
||||
}
|
||||
|
||||
auto ctm = state.transform * transform;
|
||||
auto scalex = std::sqrt(ctm.m00 * ctm.m00 + ctm.m01 * ctm.m01);
|
||||
auto scaley = std::sqrt(ctm.m10 * ctm.m10 + ctm.m11 * ctm.m11);
|
||||
|
||||
auto width = rect.w * scalex;
|
||||
auto height = rect.h * scaley;
|
||||
|
||||
RenderState newState(this, RenderMode::Display);
|
||||
newState.canvas = Canvas::create(0, 0, width, height);
|
||||
newState.transform = Transform::scaled(scalex, scaley);
|
||||
|
||||
if(viewBox.valid())
|
||||
{
|
||||
auto viewTransform = preserveAspectRatio.getMatrix(rect.w, rect.h, viewBox);
|
||||
newState.transform.premultiply(viewTransform);
|
||||
}
|
||||
else if(contentUnits == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
newState.transform.scale(box.w, box.h);
|
||||
}
|
||||
|
||||
auto transform = this->transform;
|
||||
transform.translate(rect.x, rect.y);
|
||||
transform.scale(1.0/scalex, 1.0/scaley);
|
||||
|
||||
renderChildren(newState);
|
||||
state.canvas->setTexture(newState.canvas.get(), TextureType::Tiled, transform);
|
||||
}
|
||||
|
||||
LayoutGradient::LayoutGradient(LayoutId id)
|
||||
: LayoutObject(id)
|
||||
{
|
||||
}
|
||||
|
||||
LayoutLinearGradient::LayoutLinearGradient()
|
||||
: LayoutGradient(LayoutId::LinearGradient)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutLinearGradient::apply(RenderState& state) const
|
||||
{
|
||||
auto transform = this->transform;
|
||||
if(units == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
transform *= Transform(box.w, 0, 0, box.h, box.x, box.y);
|
||||
}
|
||||
|
||||
state.canvas->setLinearGradient(x1, y1, x2, y2, stops, spreadMethod, transform);
|
||||
}
|
||||
|
||||
LayoutRadialGradient::LayoutRadialGradient()
|
||||
: LayoutGradient(LayoutId::RadialGradient)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutRadialGradient::apply(RenderState& state) const
|
||||
{
|
||||
auto transform = this->transform;
|
||||
if(units == Units::ObjectBoundingBox)
|
||||
{
|
||||
const auto& box = state.objectBoundingBox();
|
||||
transform *= Transform(box.w, 0, 0, box.h, box.x, box.y);
|
||||
}
|
||||
|
||||
state.canvas->setRadialGradient(cx, cy, r, fx, fy, stops, spreadMethod, transform);
|
||||
}
|
||||
|
||||
LayoutSolidColor::LayoutSolidColor()
|
||||
: LayoutObject(LayoutId::SolidColor)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutSolidColor::apply(RenderState& state) const
|
||||
{
|
||||
state.canvas->setColor(color);
|
||||
}
|
||||
|
||||
void FillData::fill(RenderState& state, const Path& path) const
|
||||
{
|
||||
if(opacity == 0.0 || (painter == nullptr && color.isNone()))
|
||||
return;
|
||||
|
||||
if(painter == nullptr)
|
||||
state.canvas->setColor(color);
|
||||
else
|
||||
painter->apply(state);
|
||||
|
||||
state.canvas->fill(path, state.transform, fillRule, BlendMode::Src_Over, opacity);
|
||||
}
|
||||
|
||||
void StrokeData::stroke(RenderState& state, const Path& path) const
|
||||
{
|
||||
if(opacity == 0.0 || (painter == nullptr && color.isNone()))
|
||||
return;
|
||||
|
||||
if(painter == nullptr)
|
||||
state.canvas->setColor(color);
|
||||
else
|
||||
painter->apply(state);
|
||||
|
||||
state.canvas->stroke(path, state.transform, width, cap, join, miterlimit, dash, BlendMode::Src_Over, opacity);
|
||||
}
|
||||
|
||||
static const double sqrt2 = 1.41421356237309504880;
|
||||
|
||||
void StrokeData::inflate(Rect& box) const
|
||||
{
|
||||
if(opacity == 0.0 || (painter == nullptr && color.isNone()))
|
||||
return;
|
||||
|
||||
double caplimit = width / 2.0;
|
||||
if(cap == LineCap::Square)
|
||||
caplimit *= sqrt2;
|
||||
|
||||
double joinlimit = width / 2.0;
|
||||
if(join == LineJoin::Miter)
|
||||
joinlimit *= miterlimit;
|
||||
|
||||
double delta = std::max(caplimit, joinlimit);
|
||||
box.x -= delta;
|
||||
box.y -= delta;
|
||||
box.w += delta * 2.0;
|
||||
box.h += delta * 2.0;
|
||||
}
|
||||
|
||||
MarkerPosition::MarkerPosition(const LayoutMarker* marker, const Point& origin, double angle)
|
||||
: marker(marker), origin(origin), angle(angle)
|
||||
{
|
||||
}
|
||||
|
||||
void MarkerData::add(const LayoutMarker* marker, const Point& origin, double angle)
|
||||
{
|
||||
positions.emplace_back(marker, origin, angle);
|
||||
}
|
||||
|
||||
void MarkerData::render(RenderState& state) const
|
||||
{
|
||||
for(const auto& position : positions)
|
||||
position.marker->renderMarker(state, position.origin, position.angle, strokeWidth);
|
||||
}
|
||||
|
||||
void MarkerData::inflate(Rect& box) const
|
||||
{
|
||||
for(const auto& position : positions)
|
||||
box.unite(position.marker->markerBoundingBox(position.origin, position.angle, strokeWidth));
|
||||
}
|
||||
|
||||
LayoutShape::LayoutShape()
|
||||
: LayoutObject(LayoutId::Shape)
|
||||
{
|
||||
}
|
||||
|
||||
void LayoutShape::render(RenderState& state) const
|
||||
{
|
||||
if(visibility == Visibility::Hidden)
|
||||
return;
|
||||
|
||||
BlendInfo info{clipper, masker, opacity, Rect::Invalid};
|
||||
RenderState newState(this, state.mode());
|
||||
newState.transform = transform * state.transform;
|
||||
newState.beginGroup(state, info);
|
||||
|
||||
if(newState.mode() == RenderMode::Display)
|
||||
{
|
||||
fillData.fill(newState, path);
|
||||
strokeData.stroke(newState, path);
|
||||
markerData.render(newState);
|
||||
}
|
||||
else
|
||||
{
|
||||
newState.canvas->setColor(Color::Black);
|
||||
newState.canvas->fill(path, newState.transform, clipRule, BlendMode::Src, 1.0);
|
||||
}
|
||||
|
||||
newState.endGroup(state, info);
|
||||
}
|
||||
|
||||
Rect LayoutShape::map(const Rect& rect) const
|
||||
{
|
||||
return transform.map(rect);
|
||||
}
|
||||
|
||||
const Rect& LayoutShape::fillBoundingBox() const
|
||||
{
|
||||
if(m_fillBoundingBox.valid())
|
||||
return m_fillBoundingBox;
|
||||
|
||||
m_fillBoundingBox = path.box();
|
||||
return m_fillBoundingBox;
|
||||
}
|
||||
|
||||
const Rect& LayoutShape::strokeBoundingBox() const
|
||||
{
|
||||
if(m_strokeBoundingBox.valid())
|
||||
return m_strokeBoundingBox;
|
||||
|
||||
m_strokeBoundingBox = fillBoundingBox();
|
||||
strokeData.inflate(m_strokeBoundingBox);
|
||||
markerData.inflate(m_strokeBoundingBox);
|
||||
return m_strokeBoundingBox;
|
||||
}
|
||||
|
||||
RenderState::RenderState(const LayoutObject* object, RenderMode mode)
|
||||
: m_object(object), m_mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
void RenderState::beginGroup(RenderState& state, const BlendInfo& info)
|
||||
{
|
||||
if(!info.clipper && !info.clip.valid() && (m_mode == RenderMode::Display && !(info.masker || info.opacity < 1.0)))
|
||||
{
|
||||
canvas = state.canvas;
|
||||
return;
|
||||
}
|
||||
|
||||
auto box = transform.map(m_object->strokeBoundingBox());
|
||||
box.intersect(transform.map(info.clip));
|
||||
box.intersect(state.canvas->box());
|
||||
canvas = Canvas::create(box);
|
||||
}
|
||||
|
||||
void RenderState::endGroup(RenderState& state, const BlendInfo& info)
|
||||
{
|
||||
if(state.canvas == canvas)
|
||||
return;
|
||||
|
||||
if(info.clipper)
|
||||
info.clipper->apply(*this);
|
||||
|
||||
if(info.masker && m_mode == RenderMode::Display)
|
||||
info.masker->apply(*this);
|
||||
|
||||
if(info.clip.valid())
|
||||
canvas->mask(info.clip, transform);
|
||||
|
||||
state.canvas->blend(canvas.get(), BlendMode::Src_Over, m_mode == RenderMode::Display ? info.opacity : 1.0);
|
||||
}
|
||||
|
||||
LayoutContext::LayoutContext(const ParseDocument* document, LayoutSymbol* root)
|
||||
: m_document(document), m_root(root)
|
||||
{
|
||||
}
|
||||
|
||||
Element* LayoutContext::getElementById(const std::string& id) const
|
||||
{
|
||||
return m_document->getElementById(id);
|
||||
}
|
||||
|
||||
LayoutObject* LayoutContext::getResourcesById(const std::string& id) const
|
||||
{
|
||||
auto it = m_resourcesCache.find(id);
|
||||
if(it == m_resourcesCache.end())
|
||||
return nullptr;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
LayoutObject* LayoutContext::addToResourcesCache(const std::string& id, std::unique_ptr<LayoutObject> resources)
|
||||
{
|
||||
if(resources == nullptr)
|
||||
return nullptr;
|
||||
|
||||
m_resourcesCache.emplace(id, resources.get());
|
||||
return m_root->addChild(std::move(resources));
|
||||
}
|
||||
|
||||
LayoutMask* LayoutContext::getMasker(const std::string& id)
|
||||
{
|
||||
if(id.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ref = getResourcesById(id);
|
||||
if(ref && ref->id == LayoutId::Mask)
|
||||
return static_cast<LayoutMask*>(ref);
|
||||
|
||||
auto element = getElementById(id);
|
||||
if(element == nullptr || element->id != ElementId::Mask)
|
||||
return nullptr;
|
||||
|
||||
auto masker = static_cast<MaskElement*>(element)->getMasker(this);
|
||||
return static_cast<LayoutMask*>(addToResourcesCache(id, std::move(masker)));
|
||||
}
|
||||
|
||||
LayoutClipPath* LayoutContext::getClipper(const std::string& id)
|
||||
{
|
||||
if(id.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ref = getResourcesById(id);
|
||||
if(ref && ref->id == LayoutId::ClipPath)
|
||||
return static_cast<LayoutClipPath*>(ref);
|
||||
|
||||
auto element = getElementById(id);
|
||||
if(element == nullptr || element->id != ElementId::ClipPath)
|
||||
return nullptr;
|
||||
|
||||
auto clipper = static_cast<ClipPathElement*>(element)->getClipper(this);
|
||||
return static_cast<LayoutClipPath*>(addToResourcesCache(id, std::move(clipper)));
|
||||
}
|
||||
|
||||
LayoutMarker* LayoutContext::getMarker(const std::string& id)
|
||||
{
|
||||
if(id.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ref = getResourcesById(id);
|
||||
if(ref && ref->id == LayoutId::Marker)
|
||||
return static_cast<LayoutMarker*>(ref);
|
||||
|
||||
auto element = getElementById(id);
|
||||
if(element == nullptr || element->id != ElementId::Marker)
|
||||
return nullptr;
|
||||
|
||||
auto marker = static_cast<MarkerElement*>(element)->getMarker(this);
|
||||
return static_cast<LayoutMarker*>(addToResourcesCache(id, std::move(marker)));
|
||||
}
|
||||
|
||||
LayoutObject* LayoutContext::getPainter(const std::string& id)
|
||||
{
|
||||
if(id.empty())
|
||||
return nullptr;
|
||||
|
||||
auto ref = getResourcesById(id);
|
||||
if(ref && ref->isPaint())
|
||||
return ref;
|
||||
|
||||
auto element = getElementById(id);
|
||||
if(element == nullptr || !element->isPaint())
|
||||
return nullptr;
|
||||
|
||||
auto painter = static_cast<PaintElement*>(element)->getPainter(this);
|
||||
return addToResourcesCache(id, std::move(painter));
|
||||
}
|
||||
|
||||
FillData LayoutContext::fillData(const StyledElement* element)
|
||||
{
|
||||
auto fill = element->fill();
|
||||
if(fill.isNone())
|
||||
return FillData{};
|
||||
|
||||
FillData fillData;
|
||||
fillData.painter = getPainter(fill.ref());
|
||||
fillData.color = fill.color();
|
||||
fillData.opacity = element->fill_opacity();
|
||||
fillData.fillRule = element->fill_rule();
|
||||
return fillData;
|
||||
}
|
||||
|
||||
DashData LayoutContext::dashData(const StyledElement* element)
|
||||
{
|
||||
auto dasharray = element->stroke_dasharray();
|
||||
if(dasharray.empty())
|
||||
return DashData{};
|
||||
|
||||
LengthContext lengthContex(element);
|
||||
DashArray dashes;
|
||||
for(auto& dash : dasharray)
|
||||
{
|
||||
auto value = lengthContex.valueForLength(dash, LengthMode::Both);
|
||||
dashes.push_back(value);
|
||||
}
|
||||
|
||||
auto num_dash = dashes.size();
|
||||
if(num_dash % 2)
|
||||
num_dash *= 2;
|
||||
|
||||
DashData dashData;
|
||||
dashData.array.resize(num_dash);
|
||||
double sum = 0.0;
|
||||
for(std::size_t i = 0;i < num_dash;i++)
|
||||
{
|
||||
dashData.array[i] = dashes[i % dashes.size()];
|
||||
sum += dashData.array[i];
|
||||
}
|
||||
|
||||
if(sum == 0.0)
|
||||
return DashData{};
|
||||
|
||||
auto offset = lengthContex.valueForLength(element->stroke_dashoffset(), LengthMode::Both);
|
||||
dashData.offset = std::fmod(offset, sum);
|
||||
if(dashData.offset < 0.0)
|
||||
dashData.offset += sum;
|
||||
|
||||
return dashData;
|
||||
}
|
||||
|
||||
StrokeData LayoutContext::strokeData(const StyledElement* element)
|
||||
{
|
||||
auto stroke = element->stroke();
|
||||
if(stroke.isNone())
|
||||
return StrokeData{};
|
||||
|
||||
LengthContext lengthContex(element);
|
||||
StrokeData strokeData;
|
||||
strokeData.painter = getPainter(stroke.ref());
|
||||
strokeData.color = stroke.color();
|
||||
strokeData.opacity = element->stroke_opacity();
|
||||
strokeData.width = lengthContex.valueForLength(element->stroke_width(), LengthMode::Both);
|
||||
strokeData.miterlimit = element->stroke_miterlimit();
|
||||
strokeData.cap = element->stroke_linecap();
|
||||
strokeData.join = element->stroke_linejoin();
|
||||
strokeData.dash = dashData(element);
|
||||
return strokeData;
|
||||
}
|
||||
|
||||
static const double pi = 3.14159265358979323846;
|
||||
|
||||
MarkerData LayoutContext::markerData(const GeometryElement* element, const Path& path)
|
||||
{
|
||||
auto markerStart = getMarker(element->marker_start());
|
||||
auto markerMid = getMarker(element->marker_mid());
|
||||
auto markerEnd = getMarker(element->marker_end());
|
||||
|
||||
if(markerStart == nullptr && markerMid == nullptr && markerEnd == nullptr)
|
||||
return MarkerData{};
|
||||
|
||||
LengthContext lengthContex(element);
|
||||
MarkerData markerData;
|
||||
markerData.strokeWidth = lengthContex.valueForLength(element->stroke_width(), LengthMode::Both);
|
||||
|
||||
PathIterator it(path);
|
||||
Point origin;
|
||||
Point startPoint;
|
||||
Point inslopePoints[2];
|
||||
Point outslopePoints[2];
|
||||
|
||||
int index = 0;
|
||||
std::array<Point, 3> points;
|
||||
while(!it.isDone())
|
||||
{
|
||||
switch(it.currentSegment(points)) {
|
||||
case PathCommand::MoveTo:
|
||||
startPoint = points[0];
|
||||
inslopePoints[0] = origin;
|
||||
inslopePoints[1] = points[0];
|
||||
origin = points[0];
|
||||
break;
|
||||
case PathCommand::LineTo:
|
||||
inslopePoints[0] = origin;
|
||||
inslopePoints[1] = points[0];
|
||||
origin = points[0];
|
||||
break;
|
||||
case PathCommand::CubicTo:
|
||||
inslopePoints[0] = points[1];
|
||||
inslopePoints[1] = points[2];
|
||||
origin = points[2];
|
||||
break;
|
||||
case PathCommand::Close:
|
||||
inslopePoints[0] = origin;
|
||||
inslopePoints[1] = points[0];
|
||||
origin = startPoint;
|
||||
startPoint = Point{};
|
||||
break;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
it.next();
|
||||
|
||||
if(!it.isDone() && (markerStart || markerMid))
|
||||
{
|
||||
it.currentSegment(points);
|
||||
outslopePoints[0] = origin;
|
||||
outslopePoints[1] = points[0];
|
||||
|
||||
if(index == 1 && markerStart)
|
||||
{
|
||||
Point slope{outslopePoints[1].x - outslopePoints[0].x, outslopePoints[1].y - outslopePoints[0].y};
|
||||
auto angle = 180.0 * std::atan2(slope.y, slope.x) / pi;
|
||||
|
||||
markerData.add(markerStart, origin, angle);
|
||||
}
|
||||
|
||||
if(index > 1 && markerMid)
|
||||
{
|
||||
Point inslope{inslopePoints[1].x - inslopePoints[0].x, inslopePoints[1].y - inslopePoints[0].y};
|
||||
Point outslope{outslopePoints[1].x - outslopePoints[0].x, outslopePoints[1].y - outslopePoints[0].y};
|
||||
auto inangle = 180.0 * std::atan2(inslope.y, inslope.x) / pi;
|
||||
auto outangle = 180.0 * std::atan2(outslope.y, outslope.x) / pi;
|
||||
auto angle = (inangle + outangle) * 0.5;
|
||||
|
||||
markerData.add(markerMid, origin, angle);
|
||||
}
|
||||
}
|
||||
|
||||
if(it.isDone() && markerEnd)
|
||||
{
|
||||
Point slope{inslopePoints[1].x - inslopePoints[0].x, inslopePoints[1].y - inslopePoints[0].y};
|
||||
auto angle = 180.0 * std::atan2(slope.y, slope.x) / pi;
|
||||
|
||||
markerData.add(markerEnd, origin, angle);
|
||||
}
|
||||
}
|
||||
|
||||
return markerData;
|
||||
}
|
||||
|
||||
void LayoutContext::addReference(const Element* element)
|
||||
{
|
||||
m_references.insert(element);
|
||||
}
|
||||
|
||||
void LayoutContext::removeReference(const Element* element)
|
||||
{
|
||||
m_references.erase(element);
|
||||
}
|
||||
|
||||
bool LayoutContext::hasReference(const Element* element) const
|
||||
{
|
||||
return m_references.count(element);
|
||||
}
|
||||
|
||||
LayoutBreaker::LayoutBreaker(LayoutContext* context, const Element* element)
|
||||
: m_context(context), m_element(element)
|
||||
{
|
||||
context->addReference(element);
|
||||
}
|
||||
|
||||
LayoutBreaker::~LayoutBreaker()
|
||||
{
|
||||
m_context->removeReference(m_element);
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
395
source/layoutcontext.h
Normal file
395
source/layoutcontext.h
Normal file
|
@ -0,0 +1,395 @@
|
|||
#ifndef LAYOUTCONTEXT_H
|
||||
#define LAYOUTCONTEXT_H
|
||||
|
||||
#include "property.h"
|
||||
#include "canvas.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
enum class LayoutId
|
||||
{
|
||||
Symbol,
|
||||
Group,
|
||||
Shape,
|
||||
Mask,
|
||||
ClipPath,
|
||||
Marker,
|
||||
LinearGradient,
|
||||
RadialGradient,
|
||||
Pattern,
|
||||
SolidColor
|
||||
};
|
||||
|
||||
class RenderState;
|
||||
|
||||
class LayoutObject
|
||||
{
|
||||
public:
|
||||
LayoutObject(LayoutId id);
|
||||
virtual ~LayoutObject();
|
||||
virtual void render(RenderState&) const;
|
||||
virtual void apply(RenderState&) const;
|
||||
virtual Rect map(const Rect&) const;
|
||||
|
||||
virtual const Rect& fillBoundingBox() const { return Rect::Invalid;}
|
||||
virtual const Rect& strokeBoundingBox() const { return Rect::Invalid;}
|
||||
|
||||
bool isPaint() const { return id == LayoutId::LinearGradient || id == LayoutId::RadialGradient || id == LayoutId::Pattern || id == LayoutId::SolidColor; }
|
||||
bool isHidden() const { return isPaint() || id == LayoutId::ClipPath || id == LayoutId::Mask || id == LayoutId::Marker; }
|
||||
|
||||
public:
|
||||
LayoutId id;
|
||||
};
|
||||
|
||||
using LayoutList = std::list<std::unique_ptr<LayoutObject>>;
|
||||
|
||||
class LayoutContainer : public LayoutObject
|
||||
{
|
||||
public:
|
||||
LayoutContainer(LayoutId id);
|
||||
|
||||
const Rect& fillBoundingBox() const;
|
||||
const Rect& strokeBoundingBox() const;
|
||||
|
||||
LayoutObject* addChild(std::unique_ptr<LayoutObject> child);
|
||||
LayoutObject* addChildIfNotEmpty(std::unique_ptr<LayoutContainer> child);
|
||||
void renderChildren(RenderState& state) const;
|
||||
|
||||
public:
|
||||
LayoutList children;
|
||||
|
||||
protected:
|
||||
mutable Rect m_fillBoundingBox{Rect::Invalid};
|
||||
mutable Rect m_strokeBoundingBox{Rect::Invalid};
|
||||
};
|
||||
|
||||
class LayoutClipPath : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutClipPath();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
Units units;
|
||||
Transform transform;
|
||||
const LayoutClipPath* clipper;
|
||||
};
|
||||
|
||||
class LayoutMask : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutMask();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
double x;
|
||||
double y;
|
||||
double width;
|
||||
double height;
|
||||
Units units;
|
||||
Units contentUnits;
|
||||
double opacity;
|
||||
const LayoutMask* masker;
|
||||
const LayoutClipPath* clipper;
|
||||
};
|
||||
|
||||
class LayoutSymbol : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutSymbol();
|
||||
|
||||
void render(RenderState& state) const;
|
||||
Rect map(const Rect& rect) const;
|
||||
|
||||
public:
|
||||
double width;
|
||||
double height;
|
||||
Transform transform;
|
||||
Rect clip;
|
||||
double opacity;
|
||||
const LayoutMask* masker;
|
||||
const LayoutClipPath* clipper;
|
||||
};
|
||||
|
||||
class LayoutGroup : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutGroup();
|
||||
|
||||
void render(RenderState& state) const;
|
||||
Rect map(const Rect& rect) const;
|
||||
|
||||
public:
|
||||
Transform transform;
|
||||
double opacity;
|
||||
const LayoutMask* masker;
|
||||
const LayoutClipPath* clipper;
|
||||
};
|
||||
|
||||
class LayoutMarker : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutMarker();
|
||||
|
||||
Transform markerTransform(const Point& origin, double angle, double strokeWidth) const;
|
||||
Rect markerBoundingBox(const Point& origin, double angle, double strokeWidth) const;
|
||||
void renderMarker(RenderState& state, const Point& origin, double angle, double strokeWidth) const;
|
||||
|
||||
public:
|
||||
double refX;
|
||||
double refY;
|
||||
Transform transform;
|
||||
Angle orient;
|
||||
MarkerUnits units;
|
||||
Rect clip;
|
||||
double opacity;
|
||||
const LayoutMask* masker;
|
||||
const LayoutClipPath* clipper;
|
||||
};
|
||||
|
||||
class LayoutPattern : public LayoutContainer
|
||||
{
|
||||
public:
|
||||
LayoutPattern();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
double x;
|
||||
double y;
|
||||
double width;
|
||||
double height;
|
||||
Transform transform;
|
||||
Units units;
|
||||
Units contentUnits;
|
||||
Rect viewBox;
|
||||
PreserveAspectRatio preserveAspectRatio;
|
||||
};
|
||||
|
||||
class LayoutGradient : public LayoutObject
|
||||
{
|
||||
public:
|
||||
LayoutGradient(LayoutId id);
|
||||
|
||||
public:
|
||||
Transform transform;
|
||||
SpreadMethod spreadMethod;
|
||||
Units units;
|
||||
GradientStops stops;
|
||||
};
|
||||
|
||||
class LayoutLinearGradient : public LayoutGradient
|
||||
{
|
||||
public:
|
||||
LayoutLinearGradient();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
double x1;
|
||||
double y1;
|
||||
double x2;
|
||||
double y2;
|
||||
};
|
||||
|
||||
class LayoutRadialGradient : public LayoutGradient
|
||||
{
|
||||
public:
|
||||
LayoutRadialGradient();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
double cx;
|
||||
double cy;
|
||||
double r;
|
||||
double fx;
|
||||
double fy;
|
||||
};
|
||||
|
||||
class LayoutSolidColor : public LayoutObject
|
||||
{
|
||||
public:
|
||||
LayoutSolidColor();
|
||||
|
||||
void apply(RenderState& state) const;
|
||||
|
||||
public:
|
||||
Color color;
|
||||
};
|
||||
|
||||
class FillData
|
||||
{
|
||||
public:
|
||||
FillData() = default;
|
||||
|
||||
void fill(RenderState& state, const Path& path) const;
|
||||
|
||||
public:
|
||||
const LayoutObject* painter{nullptr};
|
||||
Color color{Color::Transparent};
|
||||
double opacity{0};
|
||||
WindRule fillRule{WindRule::NonZero};
|
||||
};
|
||||
|
||||
class StrokeData
|
||||
{
|
||||
public:
|
||||
StrokeData() = default;
|
||||
|
||||
void stroke(RenderState& state, const Path& path) const;
|
||||
void inflate(Rect& box) const;
|
||||
|
||||
public:
|
||||
const LayoutObject* painter{nullptr};
|
||||
Color color{Color::Transparent};
|
||||
double opacity{0};
|
||||
double width{1};
|
||||
double miterlimit{4};
|
||||
LineCap cap{LineCap::Butt};
|
||||
LineJoin join{LineJoin::Miter};
|
||||
DashData dash;
|
||||
};
|
||||
|
||||
class MarkerPosition
|
||||
{
|
||||
public:
|
||||
MarkerPosition(const LayoutMarker* marker, const Point& origin, double angle);
|
||||
|
||||
public:
|
||||
const LayoutMarker* marker;
|
||||
Point origin;
|
||||
double angle;
|
||||
};
|
||||
|
||||
using MarkerPositionList = std::vector<MarkerPosition>;
|
||||
|
||||
class MarkerData
|
||||
{
|
||||
public:
|
||||
MarkerData() = default;
|
||||
|
||||
void add(const LayoutMarker* marker, const Point& origin, double angle);
|
||||
void render(RenderState& state) const;
|
||||
void inflate(Rect& box) const;
|
||||
|
||||
public:
|
||||
MarkerPositionList positions;
|
||||
double strokeWidth{1};
|
||||
};
|
||||
|
||||
class LayoutShape : public LayoutObject
|
||||
{
|
||||
public:
|
||||
LayoutShape();
|
||||
|
||||
void render(RenderState& state) const;
|
||||
Rect map(const Rect& rect) const;
|
||||
const Rect& fillBoundingBox() const;
|
||||
const Rect& strokeBoundingBox() const;
|
||||
|
||||
public:
|
||||
Path path;
|
||||
Transform transform;
|
||||
FillData fillData;
|
||||
StrokeData strokeData;
|
||||
MarkerData markerData;
|
||||
Visibility visibility;
|
||||
WindRule clipRule;
|
||||
double opacity;
|
||||
const LayoutMask* masker;
|
||||
const LayoutClipPath* clipper;
|
||||
|
||||
private:
|
||||
mutable Rect m_fillBoundingBox{Rect::Invalid};
|
||||
mutable Rect m_strokeBoundingBox{Rect::Invalid};
|
||||
};
|
||||
|
||||
enum class RenderMode
|
||||
{
|
||||
Display,
|
||||
Clipping
|
||||
};
|
||||
|
||||
struct BlendInfo
|
||||
{
|
||||
const LayoutClipPath* clipper;
|
||||
const LayoutMask* masker;
|
||||
double opacity;
|
||||
Rect clip;
|
||||
};
|
||||
|
||||
class RenderState
|
||||
{
|
||||
public:
|
||||
RenderState(const LayoutObject* object, RenderMode mode);
|
||||
|
||||
void beginGroup(RenderState& state, const BlendInfo& info);
|
||||
void endGroup(RenderState& state, const BlendInfo& info);
|
||||
|
||||
const LayoutObject* object() const { return m_object;}
|
||||
RenderMode mode() const { return m_mode; }
|
||||
const Rect& objectBoundingBox() const { return m_object->fillBoundingBox(); }
|
||||
|
||||
public:
|
||||
std::shared_ptr<Canvas> canvas;
|
||||
Transform transform;
|
||||
|
||||
private:
|
||||
const LayoutObject* m_object;
|
||||
RenderMode m_mode;
|
||||
};
|
||||
|
||||
class ParseDocument;
|
||||
class StyledElement;
|
||||
class GeometryElement;
|
||||
|
||||
class LayoutContext
|
||||
{
|
||||
public:
|
||||
LayoutContext(const ParseDocument* document, LayoutSymbol* root);
|
||||
|
||||
Element* getElementById(const std::string& id) const;
|
||||
LayoutObject* getResourcesById(const std::string& id) const;
|
||||
LayoutObject* addToResourcesCache(const std::string& id, std::unique_ptr<LayoutObject> resources);
|
||||
LayoutMask* getMasker(const std::string& id);
|
||||
LayoutClipPath* getClipper(const std::string& id);
|
||||
LayoutMarker* getMarker(const std::string& id);
|
||||
LayoutObject* getPainter(const std::string& id);
|
||||
|
||||
FillData fillData(const StyledElement* element);
|
||||
DashData dashData(const StyledElement* element);
|
||||
StrokeData strokeData(const StyledElement* element);
|
||||
MarkerData markerData(const GeometryElement* element, const Path& path);
|
||||
|
||||
void addReference(const Element* element);
|
||||
void removeReference(const Element* element);
|
||||
bool hasReference(const Element* element) const;
|
||||
|
||||
private:
|
||||
const ParseDocument* m_document;
|
||||
LayoutSymbol* m_root;
|
||||
std::map<std::string, LayoutObject*> m_resourcesCache;
|
||||
std::set<const Element*> m_references;
|
||||
};
|
||||
|
||||
class LayoutBreaker
|
||||
{
|
||||
public:
|
||||
LayoutBreaker(LayoutContext* context, const Element* element);
|
||||
~LayoutBreaker();
|
||||
|
||||
private:
|
||||
LayoutContext* m_context;
|
||||
const Element* m_element;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // LAYOUTCONTEXT_H
|
413
source/lunasvg.cpp
Normal file
413
source/lunasvg.cpp
Normal file
|
@ -0,0 +1,413 @@
|
|||
#include "lunasvg.h"
|
||||
#include "layoutcontext.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
struct Bitmap::Impl
|
||||
{
|
||||
Impl(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride);
|
||||
Impl(std::uint32_t width, std::uint32_t height);
|
||||
|
||||
std::unique_ptr<std::uint8_t[]> ownData;
|
||||
std::uint8_t* data;
|
||||
std::uint32_t width;
|
||||
std::uint32_t height;
|
||||
std::uint32_t stride;
|
||||
};
|
||||
|
||||
Bitmap::Impl::Impl(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride)
|
||||
: data(data), width(width), height(height), stride(stride)
|
||||
{
|
||||
}
|
||||
|
||||
Bitmap::Impl::Impl(std::uint32_t width, std::uint32_t height)
|
||||
: ownData(new std::uint8_t[width*height*4]), data(nullptr), width(width), height(height), stride(width * 4)
|
||||
{
|
||||
}
|
||||
|
||||
Bitmap::Bitmap()
|
||||
{
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride)
|
||||
: m_impl(new Impl(data, width, height, stride))
|
||||
{
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(std::uint32_t width, std::uint32_t height)
|
||||
: m_impl(new Impl(width, height))
|
||||
{
|
||||
}
|
||||
|
||||
void Bitmap::reset(std::uint8_t* data, std::uint32_t width, std::uint32_t height, std::uint32_t stride)
|
||||
{
|
||||
m_impl.reset(new Impl(data, width, height, stride));
|
||||
}
|
||||
|
||||
void Bitmap::reset(std::uint32_t width, std::uint32_t height)
|
||||
{
|
||||
m_impl.reset(new Impl(width, height));
|
||||
}
|
||||
|
||||
std::uint8_t* Bitmap::data() const
|
||||
{
|
||||
return m_impl ? m_impl->data ? m_impl->data : m_impl->ownData.get() : nullptr;
|
||||
}
|
||||
|
||||
std::uint32_t Bitmap::width() const
|
||||
{
|
||||
return m_impl ? m_impl->width : 0;
|
||||
}
|
||||
|
||||
std::uint32_t Bitmap::height() const
|
||||
{
|
||||
return m_impl ? m_impl->height : 0;
|
||||
}
|
||||
|
||||
std::uint32_t Bitmap::stride() const
|
||||
{
|
||||
return m_impl ? m_impl->stride : 0;
|
||||
}
|
||||
|
||||
void Bitmap::clear(std::uint32_t color)
|
||||
{
|
||||
auto r = (color >> 24) & 0xFF;
|
||||
auto g = (color >> 16) & 0xFF;
|
||||
auto b = (color >> 8) & 0xFF;
|
||||
auto a = (color >> 0) & 0xFF;
|
||||
|
||||
auto pr = (r * a) / 255;
|
||||
auto pg = (g * a) / 255;
|
||||
auto pb = (b * a) / 255;
|
||||
|
||||
auto width = this->width();
|
||||
auto height = this->height();
|
||||
auto stride = this->stride();
|
||||
auto rowData = this->data();
|
||||
|
||||
for(std::uint32_t y = 0;y < height;y++)
|
||||
{
|
||||
auto data = rowData;
|
||||
for(std::uint32_t x = 0;x < width;x++)
|
||||
{
|
||||
data[0] = pb;
|
||||
data[1] = pg;
|
||||
data[2] = pr;
|
||||
data[3] = a;
|
||||
data += 4;
|
||||
}
|
||||
rowData += stride;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::convert(int ri, int gi, int bi, int ai, bool unpremultiply)
|
||||
{
|
||||
auto width = this->width();
|
||||
auto height = this->height();
|
||||
auto stride = this->stride();
|
||||
auto rowData = this->data();
|
||||
|
||||
for(std::uint32_t y = 0;y < height;y++)
|
||||
{
|
||||
auto data = rowData;
|
||||
for(std::uint32_t x = 0;x < width;x++)
|
||||
{
|
||||
auto b = data[0];
|
||||
auto g = data[1];
|
||||
auto r = data[2];
|
||||
auto a = data[3];
|
||||
|
||||
if(unpremultiply && a != 0)
|
||||
{
|
||||
r = (r * 255) / a;
|
||||
g = (g * 255) / a;
|
||||
b = (b * 255) / a;
|
||||
}
|
||||
|
||||
data[ri] = r;
|
||||
data[gi] = g;
|
||||
data[bi] = b;
|
||||
data[ai] = a;
|
||||
data += 4;
|
||||
}
|
||||
rowData += stride;
|
||||
}
|
||||
}
|
||||
|
||||
Box::Box(double x, double y, double w, double h)
|
||||
: x(x), y(y), w(w), h(h)
|
||||
{}
|
||||
|
||||
Box::Box(const Rect& rect)
|
||||
: x(rect.x), y(rect.y), w(rect.w), h(rect.h)
|
||||
{}
|
||||
|
||||
Matrix::Matrix(double a, double b, double c, double d, double e, double f)
|
||||
: a(a), b(b), c(c), d(d), e(e), f(f)
|
||||
{}
|
||||
|
||||
Matrix::Matrix(const Transform& transform)
|
||||
: a(transform.m00), b(transform.m10), c(transform.m01), d(transform.m11), e(transform.m02), f(transform.m12)
|
||||
{
|
||||
}
|
||||
|
||||
Matrix& Matrix::rotate(double angle)
|
||||
{
|
||||
*this = rotated(angle) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::rotate(double angle, double cx, double cy)
|
||||
{
|
||||
*this = rotated(angle, cx, cy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::scale(double sx, double sy)
|
||||
{
|
||||
*this = scaled(sx, sy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::shear(double shx, double shy)
|
||||
{
|
||||
*this = sheared(shx, shy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::translate(double tx, double ty)
|
||||
{
|
||||
*this = translated(tx, ty) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::transform(double a, double b, double c, double d, double e, double f)
|
||||
{
|
||||
*this = Matrix{a, b, c, d, e, f} * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::identity()
|
||||
{
|
||||
*this = Matrix{1, 0, 0, 1, 0, 0};
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::invert()
|
||||
{
|
||||
*this = inverted();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::operator*=(const Matrix& matrix)
|
||||
{
|
||||
*this = *this * matrix;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::premultiply(const Matrix& matrix)
|
||||
{
|
||||
*this = matrix * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix& Matrix::postmultiply(const Matrix& matrix)
|
||||
{
|
||||
*this = *this * matrix;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matrix Matrix::inverted() const
|
||||
{
|
||||
return Transform(*this).inverted();
|
||||
}
|
||||
|
||||
Matrix Matrix::operator*(const Matrix& matrix) const
|
||||
{
|
||||
return Transform(*this) * Transform(matrix);
|
||||
}
|
||||
|
||||
Box Matrix::map(const Box& box) const
|
||||
{
|
||||
return Transform(*this).map(box);
|
||||
}
|
||||
|
||||
Matrix Matrix::rotated(double angle)
|
||||
{
|
||||
return Transform::rotated(angle);
|
||||
}
|
||||
|
||||
Matrix Matrix::rotated(double angle, double cx, double cy)
|
||||
{
|
||||
return Transform::rotated(angle, cx, cy);
|
||||
}
|
||||
|
||||
Matrix Matrix::scaled(double sx, double sy)
|
||||
{
|
||||
return Transform::scaled(sx, sy);;
|
||||
}
|
||||
|
||||
Matrix Matrix::sheared(double shx, double shy)
|
||||
{
|
||||
return Transform::sheared(shx, shy);
|
||||
}
|
||||
|
||||
Matrix Matrix::translated(double tx, double ty)
|
||||
{
|
||||
return Transform::translated(tx, ty);
|
||||
}
|
||||
|
||||
std::unique_ptr<Document> Document::loadFromFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream fs;
|
||||
fs.open(filename);
|
||||
if(!fs.is_open())
|
||||
return nullptr;
|
||||
|
||||
std::string content;
|
||||
std::getline(fs, content, '\0');
|
||||
fs.close();
|
||||
|
||||
return loadFromData(content);
|
||||
}
|
||||
|
||||
std::unique_ptr<Document> Document::loadFromData(const std::string& string)
|
||||
{
|
||||
return loadFromData(string.data(), string.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<Document> Document::loadFromData(const char* data, std::size_t size)
|
||||
{
|
||||
ParseDocument parser;
|
||||
if(!parser.parse(data, size))
|
||||
return nullptr;
|
||||
|
||||
auto root = parser.layout();
|
||||
if(!root || root->children.empty())
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<Document> document(new Document);
|
||||
document->root = std::move(root);
|
||||
return document;
|
||||
}
|
||||
|
||||
std::unique_ptr<Document> Document::loadFromData(const char* data)
|
||||
{
|
||||
return loadFromData(data, std::strlen(data));
|
||||
}
|
||||
|
||||
Document* Document::rotate(double angle)
|
||||
{
|
||||
root->transform.rotate(angle);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::rotate(double angle, double cx, double cy)
|
||||
{
|
||||
root->transform.rotate(angle, cx, cy);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::scale(double sx, double sy)
|
||||
{
|
||||
root->transform.scale(sx, sy);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::shear(double shx, double shy)
|
||||
{
|
||||
root->transform.shear(shx, shy);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::translate(double tx, double ty)
|
||||
{
|
||||
root->transform.translate(tx, ty);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::transform(double a, double b, double c, double d, double e, double f)
|
||||
{
|
||||
root->transform.transform(a, b, c, d, e, f);
|
||||
return this;
|
||||
}
|
||||
|
||||
Document* Document::identity()
|
||||
{
|
||||
root->transform.identity();
|
||||
return this;
|
||||
}
|
||||
|
||||
void Document::setMatrix(const Matrix& matrix)
|
||||
{
|
||||
root->transform = Transform(matrix);
|
||||
}
|
||||
|
||||
Matrix Document::matrix() const
|
||||
{
|
||||
return root->transform;
|
||||
}
|
||||
|
||||
Box Document::box() const
|
||||
{
|
||||
return root->map(root->strokeBoundingBox());
|
||||
}
|
||||
|
||||
double Document::width() const
|
||||
{
|
||||
return root->width;
|
||||
}
|
||||
|
||||
double Document::height() const
|
||||
{
|
||||
return root->height;
|
||||
}
|
||||
|
||||
void Document::render(Bitmap bitmap, const Matrix& matrix) const
|
||||
{
|
||||
RenderState state(nullptr, RenderMode::Display);
|
||||
state.canvas = Canvas::create(bitmap.data(), bitmap.width(), bitmap.height(), bitmap.stride());
|
||||
state.transform = Transform(matrix);
|
||||
root->render(state);
|
||||
}
|
||||
|
||||
Bitmap Document::renderToBitmap(std::uint32_t width, std::uint32_t height, std::uint32_t backgroundColor) const
|
||||
{
|
||||
if(root->width == 0.0 || root->height == 0.0)
|
||||
return Bitmap{};
|
||||
|
||||
if(width == 0 && height == 0)
|
||||
{
|
||||
width = static_cast<std::uint32_t>(std::ceil(root->width));
|
||||
height = static_cast<std::uint32_t>(std::ceil(root->height));
|
||||
}
|
||||
else if(width != 0 && height == 0)
|
||||
{
|
||||
height = static_cast<std::uint32_t>(std::ceil(width * root->height / root->width));
|
||||
}
|
||||
else if(height != 0 && width == 0)
|
||||
{
|
||||
width = static_cast<std::uint32_t>(std::ceil(height * root->width / root->height));
|
||||
}
|
||||
|
||||
Matrix matrix(width / root->width, 0, 0, height / root->height, 0, 0);
|
||||
Bitmap bitmap(width, height);
|
||||
bitmap.clear(backgroundColor);
|
||||
render(bitmap, matrix);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Document::Document()
|
||||
{
|
||||
}
|
||||
|
||||
Document::~Document()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
98
source/markerelement.cpp
Normal file
98
source/markerelement.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "markerelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
MarkerElement::MarkerElement()
|
||||
: StyledElement(ElementId::Marker)
|
||||
{
|
||||
}
|
||||
|
||||
Length MarkerElement::refX() const
|
||||
{
|
||||
auto& value = get(PropertyId::RefX);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length MarkerElement::refY() const
|
||||
{
|
||||
auto& value = get(PropertyId::RefY);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length MarkerElement::markerWidth() const
|
||||
{
|
||||
auto& value = get(PropertyId::MarkerWidth);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent);
|
||||
}
|
||||
|
||||
Length MarkerElement::markerHeight() const
|
||||
{
|
||||
auto& value = get(PropertyId::MarkerHeight);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent);
|
||||
}
|
||||
|
||||
Angle MarkerElement::orient() const
|
||||
{
|
||||
auto& value = get(PropertyId::Orient);
|
||||
return Parser::parseAngle(value);
|
||||
}
|
||||
|
||||
MarkerUnits MarkerElement::markerUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::MarkerUnits);
|
||||
return Parser::parseMarkerUnits(value);
|
||||
}
|
||||
|
||||
Rect MarkerElement::viewBox() const
|
||||
{
|
||||
auto& value = get(PropertyId::ViewBox);
|
||||
return Parser::parseViewBox(value);
|
||||
}
|
||||
|
||||
PreserveAspectRatio MarkerElement::preserveAspectRatio() const
|
||||
{
|
||||
auto& value = get(PropertyId::PreserveAspectRatio);
|
||||
return Parser::parsePreserveAspectRatio(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutMarker> MarkerElement::getMarker(LayoutContext* context) const
|
||||
{
|
||||
auto markerWidth = this->markerWidth();
|
||||
auto markerHeight = this->markerHeight();
|
||||
if(markerWidth.isZero() || markerHeight.isZero() || context->hasReference(this))
|
||||
return nullptr;
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _refX = lengthContext.valueForLength(refX(), LengthMode::Width);
|
||||
auto _refY = lengthContext.valueForLength(refY(), LengthMode::Height);
|
||||
auto _markerWidth = lengthContext.valueForLength(markerWidth, LengthMode::Width);
|
||||
auto _markerHeight = lengthContext.valueForLength(markerHeight, LengthMode::Height);
|
||||
|
||||
auto viewBox = this->viewBox();
|
||||
auto preserveAspectRatio = this->preserveAspectRatio();
|
||||
auto viewTransform = preserveAspectRatio.getMatrix(_markerWidth, _markerHeight, viewBox);
|
||||
viewTransform.map(_refX, _refY, &_refX, &_refY);
|
||||
|
||||
LayoutBreaker layoutBreaker(context, this);
|
||||
auto marker = std::make_unique<LayoutMarker>();
|
||||
marker->refX = _refX;
|
||||
marker->refY = _refY;
|
||||
marker->transform = viewTransform;
|
||||
marker->orient = orient();
|
||||
marker->units = markerUnits();
|
||||
marker->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_markerWidth, _markerHeight, viewBox) : Rect::Invalid;
|
||||
marker->opacity = opacity();
|
||||
marker->masker = context->getMasker(mask());
|
||||
marker->clipper = context->getClipper(clip_path());
|
||||
layoutChildren(context, marker.get());
|
||||
return marker;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> MarkerElement::clone() const
|
||||
{
|
||||
return cloneElement<MarkerElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
31
source/markerelement.h
Normal file
31
source/markerelement.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef MARKERELEMENT_H
|
||||
#define MARKERELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class LayoutMarker;
|
||||
|
||||
class MarkerElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
MarkerElement();
|
||||
|
||||
Length refX() const;
|
||||
Length refY() const;
|
||||
Length markerWidth() const;
|
||||
Length markerHeight() const;
|
||||
Angle orient() const;
|
||||
MarkerUnits markerUnits() const;
|
||||
|
||||
Rect viewBox() const;
|
||||
PreserveAspectRatio preserveAspectRatio() const;
|
||||
std::unique_ptr<LayoutMarker> getMarker(LayoutContext* context) const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // MARKERELEMENT_H
|
77
source/maskelement.cpp
Normal file
77
source/maskelement.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "maskelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
MaskElement::MaskElement()
|
||||
: StyledElement(ElementId::Mask)
|
||||
{
|
||||
}
|
||||
|
||||
Length MaskElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent);
|
||||
}
|
||||
|
||||
Length MaskElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent);
|
||||
}
|
||||
|
||||
Length MaskElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent);
|
||||
}
|
||||
|
||||
Length MaskElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent);
|
||||
}
|
||||
|
||||
Units MaskElement::maskUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::MaskUnits);
|
||||
return Parser::parseUnits(value, Units::ObjectBoundingBox);
|
||||
}
|
||||
|
||||
Units MaskElement::maskContentUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::MaskContentUnits);
|
||||
return Parser::parseUnits(value, Units::UserSpaceOnUse);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutMask> MaskElement::getMasker(LayoutContext* context) const
|
||||
{
|
||||
auto w = this->width();
|
||||
auto h = this->height();
|
||||
if(w.isZero() || h.isZero() || context->hasReference(this))
|
||||
return nullptr;
|
||||
|
||||
LayoutBreaker layoutBreaker(context, this);
|
||||
auto masker = std::make_unique<LayoutMask>();
|
||||
masker->units = maskUnits();
|
||||
masker->contentUnits = maskContentUnits();
|
||||
masker->opacity = opacity();
|
||||
masker->clipper = context->getClipper(clip_path());
|
||||
masker->masker = context->getMasker(mask());
|
||||
|
||||
LengthContext lengthContext(this, maskUnits());
|
||||
masker->x = lengthContext.valueForLength(x(), LengthMode::Width);
|
||||
masker->y = lengthContext.valueForLength(y(), LengthMode::Height);
|
||||
masker->width = lengthContext.valueForLength(w, LengthMode::Width);
|
||||
masker->height = lengthContext.valueForLength(h, LengthMode::Height);
|
||||
layoutChildren(context, masker.get());
|
||||
return masker;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> MaskElement::clone() const
|
||||
{
|
||||
return cloneElement<MaskElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
28
source/maskelement.h
Normal file
28
source/maskelement.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef MASKELEMENT_H
|
||||
#define MASKELEMENT_H
|
||||
|
||||
#include "styledelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class LayoutMask;
|
||||
|
||||
class MaskElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
MaskElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
Units maskUnits() const;
|
||||
Units maskContentUnits() const;
|
||||
std::unique_ptr<LayoutMask> getMasker(LayoutContext* context) const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // MASKELEMENT_H
|
433
source/paintelement.cpp
Normal file
433
source/paintelement.cpp
Normal file
|
@ -0,0 +1,433 @@
|
|||
#include "paintelement.h"
|
||||
#include "stopelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
PaintElement::PaintElement(ElementId id)
|
||||
: StyledElement(id)
|
||||
{
|
||||
}
|
||||
|
||||
GradientElement::GradientElement(ElementId id)
|
||||
: PaintElement(id)
|
||||
{
|
||||
}
|
||||
|
||||
Transform GradientElement::gradientTransform() const
|
||||
{
|
||||
auto& value = get(PropertyId::GradientTransform);
|
||||
return Parser::parseTransform(value);
|
||||
}
|
||||
|
||||
SpreadMethod GradientElement::spreadMethod() const
|
||||
{
|
||||
auto& value = get(PropertyId::SpreadMethod);
|
||||
return Parser::parseSpreadMethod(value);
|
||||
}
|
||||
|
||||
Units GradientElement::gradientUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::GradientUnits);
|
||||
return Parser::parseUnits(value, Units::ObjectBoundingBox);
|
||||
}
|
||||
|
||||
std::string GradientElement::href() const
|
||||
{
|
||||
auto& value = get(PropertyId::Href);
|
||||
return Parser::parseHref(value);
|
||||
}
|
||||
|
||||
GradientStops GradientElement::buildGradientStops() const
|
||||
{
|
||||
GradientStops gradientStops;
|
||||
double prevOffset = 0.0;
|
||||
for(auto& child : children)
|
||||
{
|
||||
if(child->isText())
|
||||
continue;
|
||||
auto element = static_cast<Element*>(child.get());
|
||||
if(element->id != ElementId::Stop)
|
||||
continue;
|
||||
auto stop = static_cast<StopElement*>(element);
|
||||
auto offset = std::max(prevOffset, stop->offset());
|
||||
prevOffset = offset;
|
||||
gradientStops.emplace_back(offset, stop->stopColorWithOpacity());
|
||||
}
|
||||
|
||||
return gradientStops;
|
||||
}
|
||||
|
||||
LinearGradientElement::LinearGradientElement()
|
||||
: GradientElement(ElementId::LinearGradient)
|
||||
{
|
||||
}
|
||||
|
||||
Length LinearGradientElement::x1() const
|
||||
{
|
||||
auto& value = get(PropertyId::X1);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length LinearGradientElement::y1() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y1);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length LinearGradientElement::x2() const
|
||||
{
|
||||
auto& value = get(PropertyId::X2);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Length LinearGradientElement::y2() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y2);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutObject> LinearGradientElement::getPainter(LayoutContext* context) const
|
||||
{
|
||||
LinearGradientAttributes attributes;
|
||||
std::set<const GradientElement*> processedGradients;
|
||||
const GradientElement* current = this;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform))
|
||||
attributes.setGradientTransform(current->gradientTransform());
|
||||
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod))
|
||||
attributes.setSpreadMethod(current->spreadMethod());
|
||||
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits))
|
||||
attributes.setGradientUnits(current->gradientUnits());
|
||||
if(!attributes.hasGradientStops())
|
||||
attributes.setGradientStops(current->buildGradientStops());
|
||||
|
||||
if(current->id == ElementId::LinearGradient)
|
||||
{
|
||||
auto element = static_cast<const LinearGradientElement*>(current);
|
||||
if(!attributes.hasX1() && element->has(PropertyId::X1))
|
||||
attributes.setX1(element->x1());
|
||||
if(!attributes.hasY1() && element->has(PropertyId::Y1))
|
||||
attributes.setY1(element->y1());
|
||||
if(!attributes.hasX2() && element->has(PropertyId::X2))
|
||||
attributes.setX2(element->x2());
|
||||
if(!attributes.hasY2() && element->has(PropertyId::Y2))
|
||||
attributes.setY2(element->y2());
|
||||
}
|
||||
|
||||
auto ref = context->getElementById(current->href());
|
||||
if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient))
|
||||
break;
|
||||
|
||||
processedGradients.insert(current);
|
||||
current = static_cast<const GradientElement*>(ref);
|
||||
if(processedGradients.find(current) != processedGradients.end())
|
||||
break;
|
||||
}
|
||||
|
||||
auto& stops = attributes.gradientStops();
|
||||
if(stops.empty())
|
||||
return nullptr;
|
||||
|
||||
LengthContext lengthContext(this, attributes.gradientUnits());
|
||||
auto x1 = lengthContext.valueForLength(attributes.x1(), LengthMode::Width);
|
||||
auto y1 = lengthContext.valueForLength(attributes.y1(), LengthMode::Height);
|
||||
auto x2 = lengthContext.valueForLength(attributes.x2(), LengthMode::Width);
|
||||
auto y2 = lengthContext.valueForLength(attributes.y2(), LengthMode::Height);
|
||||
if((x1 == x2 && y1 == y2) || stops.size() == 1)
|
||||
{
|
||||
auto solid = std::make_unique<LayoutSolidColor>();
|
||||
solid->color = std::get<1>(stops.back());
|
||||
return std::move(solid);
|
||||
}
|
||||
|
||||
auto gradient = std::make_unique<LayoutLinearGradient>();
|
||||
gradient->transform = attributes.gradientTransform();
|
||||
gradient->spreadMethod = attributes.spreadMethod();
|
||||
gradient->units = attributes.gradientUnits();
|
||||
gradient->stops = attributes.gradientStops();
|
||||
gradient->x1 = x1;
|
||||
gradient->y1 = y1;
|
||||
gradient->x2 = x2;
|
||||
gradient->y2 = y2;
|
||||
return std::move(gradient);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> LinearGradientElement::clone() const
|
||||
{
|
||||
return cloneElement<LinearGradientElement>();
|
||||
}
|
||||
|
||||
RadialGradientElement::RadialGradientElement()
|
||||
: GradientElement(ElementId::RadialGradient)
|
||||
{
|
||||
}
|
||||
|
||||
Length RadialGradientElement::cx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cx);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
|
||||
}
|
||||
|
||||
Length RadialGradientElement::cy() const
|
||||
{
|
||||
auto& value = get(PropertyId::Cy);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
|
||||
}
|
||||
|
||||
Length RadialGradientElement::r() const
|
||||
{
|
||||
auto& value = get(PropertyId::R);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent);
|
||||
}
|
||||
|
||||
Length RadialGradientElement::fx() const
|
||||
{
|
||||
auto& value = get(PropertyId::Fx);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length RadialGradientElement::fy() const
|
||||
{
|
||||
auto& value = get(PropertyId::Fy);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutObject> RadialGradientElement::getPainter(LayoutContext* context) const
|
||||
{
|
||||
RadialGradientAttributes attributes;
|
||||
std::set<const GradientElement*> processedGradients;
|
||||
const GradientElement* current = this;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform))
|
||||
attributes.setGradientTransform(current->gradientTransform());
|
||||
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod))
|
||||
attributes.setSpreadMethod(current->spreadMethod());
|
||||
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits))
|
||||
attributes.setGradientUnits(current->gradientUnits());
|
||||
if(!attributes.hasGradientStops())
|
||||
attributes.setGradientStops(current->buildGradientStops());
|
||||
|
||||
if(current->id == ElementId::RadialGradient)
|
||||
{
|
||||
auto element = static_cast<const RadialGradientElement*>(current);
|
||||
if(!attributes.hasCx() && element->has(PropertyId::Cx))
|
||||
attributes.setCx(element->cx());
|
||||
if(!attributes.hasCy() && element->has(PropertyId::Cy))
|
||||
attributes.setCy(element->cy());
|
||||
if(!attributes.hasR() && element->has(PropertyId::R))
|
||||
attributes.setR(element->r());
|
||||
if(!attributes.hasFx() && element->has(PropertyId::Fx))
|
||||
attributes.setFx(element->fx());
|
||||
if(!attributes.hasFy() && element->has(PropertyId::Fy))
|
||||
attributes.setFy(element->fy());
|
||||
}
|
||||
|
||||
auto ref = context->getElementById(current->href());
|
||||
if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient))
|
||||
break;
|
||||
|
||||
processedGradients.insert(current);
|
||||
current = static_cast<const GradientElement*>(ref);
|
||||
if(processedGradients.find(current) != processedGradients.end())
|
||||
break;
|
||||
}
|
||||
|
||||
if(!attributes.hasFx())
|
||||
attributes.setFx(attributes.cx());
|
||||
if(!attributes.hasFy())
|
||||
attributes.setFy(attributes.cy());
|
||||
|
||||
auto& stops = attributes.gradientStops();
|
||||
if(stops.empty())
|
||||
return nullptr;
|
||||
|
||||
auto& r = attributes.r();
|
||||
if(r.isZero() || stops.size() == 1)
|
||||
{
|
||||
auto solid = std::make_unique<LayoutSolidColor>();
|
||||
solid->color = std::get<1>(stops.back());
|
||||
return std::move(solid);
|
||||
}
|
||||
|
||||
auto gradient = std::make_unique<LayoutRadialGradient>();
|
||||
gradient->transform = attributes.gradientTransform();
|
||||
gradient->spreadMethod = attributes.spreadMethod();
|
||||
gradient->units = attributes.gradientUnits();
|
||||
gradient->stops = attributes.gradientStops();
|
||||
|
||||
LengthContext lengthContext(this, attributes.gradientUnits());
|
||||
gradient->cx = lengthContext.valueForLength(attributes.cx(), LengthMode::Width);
|
||||
gradient->cy = lengthContext.valueForLength(attributes.cy(), LengthMode::Height);
|
||||
gradient->r = lengthContext.valueForLength(attributes.r(), LengthMode::Both);
|
||||
gradient->fx = lengthContext.valueForLength(attributes.fx(), LengthMode::Width);
|
||||
gradient->fy = lengthContext.valueForLength(attributes.fy(), LengthMode::Height);
|
||||
return std::move(gradient);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> RadialGradientElement::clone() const
|
||||
{
|
||||
return cloneElement<RadialGradientElement>();
|
||||
}
|
||||
|
||||
PatternElement::PatternElement()
|
||||
: PaintElement(ElementId::Pattern)
|
||||
{
|
||||
}
|
||||
|
||||
Length PatternElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length PatternElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length PatternElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length PatternElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Transform PatternElement::patternTransform() const
|
||||
{
|
||||
auto& value = get(PropertyId::PatternTransform);
|
||||
return Parser::parseTransform(value);
|
||||
}
|
||||
|
||||
Units PatternElement::patternUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::PatternUnits);
|
||||
return Parser::parseUnits(value, Units::ObjectBoundingBox);
|
||||
}
|
||||
|
||||
Units PatternElement::patternContentUnits() const
|
||||
{
|
||||
auto& value = get(PropertyId::PatternContentUnits);
|
||||
return Parser::parseUnits(value, Units::UserSpaceOnUse);
|
||||
}
|
||||
|
||||
Rect PatternElement::viewBox() const
|
||||
{
|
||||
auto& value = get(PropertyId::ViewBox);
|
||||
return Parser::parseViewBox(value);
|
||||
}
|
||||
|
||||
PreserveAspectRatio PatternElement::preserveAspectRatio() const
|
||||
{
|
||||
auto& value = get(PropertyId::PreserveAspectRatio);
|
||||
return Parser::parsePreserveAspectRatio(value);
|
||||
}
|
||||
|
||||
std::string PatternElement::href() const
|
||||
{
|
||||
auto& value = get(PropertyId::Href);
|
||||
return Parser::parseHref(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutObject> PatternElement::getPainter(LayoutContext* context) const
|
||||
{
|
||||
if(context->hasReference(this))
|
||||
return nullptr;
|
||||
|
||||
PatternAttributes attributes;
|
||||
std::set<const PatternElement*> processedPatterns;
|
||||
const PatternElement* current = this;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(!attributes.hasX() && current->has(PropertyId::X))
|
||||
attributes.setX(current->x());
|
||||
if(!attributes.hasY() && current->has(PropertyId::Y))
|
||||
attributes.setY(current->y());
|
||||
if(!attributes.hasWidth() && current->has(PropertyId::Width))
|
||||
attributes.setWidth(current->width());
|
||||
if(!attributes.hasHeight() && current->has(PropertyId::Height))
|
||||
attributes.setHeight(current->height());
|
||||
if(!attributes.hasPatternTransform() && current->has(PropertyId::PatternTransform))
|
||||
attributes.setPatternTransform(current->patternTransform());
|
||||
if(!attributes.hasPatternUnits() && current->has(PropertyId::PatternUnits))
|
||||
attributes.setPatternUnits(current->patternUnits());
|
||||
if(!attributes.hasPatternContentUnits() && current->has(PropertyId::PatternContentUnits))
|
||||
attributes.setPatternContentUnits(current->patternContentUnits());
|
||||
if(!attributes.hasViewBox() && current->has(PropertyId::ViewBox))
|
||||
attributes.setViewBox(current->viewBox());
|
||||
if(!attributes.hasPreserveAspectRatio() && current->has(PropertyId::PreserveAspectRatio))
|
||||
attributes.setPreserveAspectRatio(current->preserveAspectRatio());
|
||||
if(!attributes.hasPatternContentElement() && current->children.size())
|
||||
attributes.setPatternContentElement(current);
|
||||
|
||||
auto ref = context->getElementById(current->href());
|
||||
if(!ref || ref->id != ElementId::Pattern)
|
||||
break;
|
||||
|
||||
processedPatterns.insert(current);
|
||||
current = static_cast<const PatternElement*>(ref);
|
||||
if(processedPatterns.find(current) != processedPatterns.end())
|
||||
break;
|
||||
}
|
||||
|
||||
auto& width = attributes.width();
|
||||
auto& height = attributes.height();
|
||||
auto element = attributes.patternContentElement();
|
||||
if(element == nullptr || width.isZero() || height.isZero())
|
||||
return nullptr;
|
||||
|
||||
LayoutBreaker layoutBreaker(context, this);
|
||||
auto pattern = std::make_unique<LayoutPattern>();
|
||||
pattern->transform = attributes.patternTransform();
|
||||
pattern->units = attributes.patternUnits();
|
||||
pattern->contentUnits = attributes.patternContentUnits();
|
||||
pattern->viewBox = attributes.viewBox();
|
||||
pattern->preserveAspectRatio = attributes.preserveAspectRatio();
|
||||
|
||||
LengthContext lengthContext(this, attributes.patternUnits());
|
||||
pattern->x = lengthContext.valueForLength(attributes.x(), LengthMode::Width);
|
||||
pattern->y = lengthContext.valueForLength(attributes.y(), LengthMode::Height);
|
||||
pattern->width = lengthContext.valueForLength(attributes.width(), LengthMode::Width);
|
||||
pattern->height = lengthContext.valueForLength(attributes.height(), LengthMode::Height);
|
||||
element->layoutChildren(context, pattern.get());
|
||||
return std::move(pattern);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> PatternElement::clone() const
|
||||
{
|
||||
return cloneElement<PatternElement>();
|
||||
}
|
||||
|
||||
SolidColorElement::SolidColorElement()
|
||||
: PaintElement(ElementId::SolidColor)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutObject> SolidColorElement::getPainter(LayoutContext*) const
|
||||
{
|
||||
auto solid = std::make_unique<LayoutSolidColor>();
|
||||
solid->color = solid_color();
|
||||
solid->color.a = solid_opacity();
|
||||
return std::move(solid);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> SolidColorElement::clone() const
|
||||
{
|
||||
return cloneElement<SolidColorElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
368
source/paintelement.h
Normal file
368
source/paintelement.h
Normal file
|
@ -0,0 +1,368 @@
|
|||
#ifndef PAINTELEMENT_H
|
||||
#define PAINTELEMENT_H
|
||||
|
||||
#include "styledelement.h"
|
||||
#include "canvas.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class LayoutObject;
|
||||
|
||||
class PaintElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
PaintElement(ElementId id);
|
||||
|
||||
bool isPaint() const { return true; }
|
||||
virtual std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const = 0;
|
||||
};
|
||||
|
||||
class GradientElement : public PaintElement
|
||||
{
|
||||
public:
|
||||
GradientElement(ElementId id);
|
||||
|
||||
Transform gradientTransform() const;
|
||||
SpreadMethod spreadMethod() const;
|
||||
Units gradientUnits() const;
|
||||
std::string href() const;
|
||||
GradientStops buildGradientStops() const;
|
||||
};
|
||||
|
||||
class LinearGradientElement : public GradientElement
|
||||
{
|
||||
public:
|
||||
LinearGradientElement();
|
||||
|
||||
Length x1() const;
|
||||
Length y1() const;
|
||||
Length x2() const;
|
||||
Length y2() const;
|
||||
|
||||
std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class RadialGradientElement : public GradientElement
|
||||
{
|
||||
public:
|
||||
RadialGradientElement();
|
||||
|
||||
Length cx() const;
|
||||
Length cy() const;
|
||||
Length r() const;
|
||||
Length fx() const;
|
||||
Length fy() const;
|
||||
|
||||
std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class PatternElement : public PaintElement
|
||||
{
|
||||
public:
|
||||
PatternElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
Transform patternTransform() const;
|
||||
Units patternUnits() const;
|
||||
Units patternContentUnits() const;
|
||||
|
||||
Rect viewBox() const;
|
||||
PreserveAspectRatio preserveAspectRatio() const;
|
||||
std::string href() const;
|
||||
|
||||
std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class SolidColorElement : public PaintElement
|
||||
{
|
||||
public:
|
||||
SolidColorElement();
|
||||
|
||||
std::unique_ptr<LayoutObject> getPainter(LayoutContext*) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
class GradientAttributes
|
||||
{
|
||||
public:
|
||||
GradientAttributes() = default;
|
||||
|
||||
const Transform& gradientTransform() const { return m_gradientTransform; }
|
||||
SpreadMethod spreadMethod() const { return m_spreadMethod; }
|
||||
Units gradientUnits() const { return m_gradientUnits; }
|
||||
const GradientStops& gradientStops() const { return m_gradientStops; }
|
||||
|
||||
bool hasGradientTransform() const { return m_hasGradientTransform; }
|
||||
bool hasSpreadMethod() const { return m_hasSpreadMethod; }
|
||||
bool hasGradientUnits() const { return m_hasGradientUnits; }
|
||||
bool hasGradientStops() const { return m_hasGradientStops; }
|
||||
|
||||
void setGradientTransform(const Transform& gradientTransform)
|
||||
{
|
||||
m_gradientTransform = gradientTransform;
|
||||
m_hasGradientTransform = true;
|
||||
}
|
||||
|
||||
void setSpreadMethod(SpreadMethod spreadMethod)
|
||||
{
|
||||
m_spreadMethod = spreadMethod;
|
||||
m_hasSpreadMethod = true;
|
||||
}
|
||||
|
||||
void setGradientUnits(Units gradientUnits)
|
||||
{
|
||||
m_gradientUnits = gradientUnits;
|
||||
m_hasGradientUnits = true;
|
||||
}
|
||||
|
||||
void setGradientStops(const GradientStops& gradientStops)
|
||||
{
|
||||
m_gradientStops = gradientStops;
|
||||
m_hasGradientStops = gradientStops.size();
|
||||
}
|
||||
|
||||
private:
|
||||
Transform m_gradientTransform;
|
||||
SpreadMethod m_spreadMethod{SpreadMethod::Pad};
|
||||
Units m_gradientUnits{Units::ObjectBoundingBox};
|
||||
GradientStops m_gradientStops;
|
||||
|
||||
bool m_hasGradientTransform{false};
|
||||
bool m_hasSpreadMethod{false};
|
||||
bool m_hasGradientUnits{false};
|
||||
bool m_hasGradientStops{false};
|
||||
};
|
||||
|
||||
class LinearGradientAttributes : public GradientAttributes
|
||||
{
|
||||
public:
|
||||
LinearGradientAttributes() = default;
|
||||
|
||||
const Length& x1() const { return m_x1; }
|
||||
const Length& y1() const { return m_y1; }
|
||||
const Length& x2() const { return m_x2; }
|
||||
const Length& y2() const { return m_y2; }
|
||||
|
||||
bool hasX1() const { return m_hasX1; }
|
||||
bool hasY1() const { return m_hasY1; }
|
||||
bool hasX2() const { return m_hasX2; }
|
||||
bool hasY2() const { return m_hasY2; }
|
||||
|
||||
void setX1(const Length& x1)
|
||||
{
|
||||
m_x1 = x1;
|
||||
m_hasX1 = true;
|
||||
}
|
||||
|
||||
void setY1(const Length& y1)
|
||||
{
|
||||
m_y1 = y1;
|
||||
m_hasY1 = true;
|
||||
}
|
||||
|
||||
void setX2(const Length& x2)
|
||||
{
|
||||
m_x2 = x2;
|
||||
m_hasX2 = true;
|
||||
}
|
||||
|
||||
void setY2(const Length& y2)
|
||||
{
|
||||
m_y2 = y2;
|
||||
m_hasY2 = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Length m_x1;
|
||||
Length m_y1;
|
||||
Length m_x2{100, LengthUnits::Percent};
|
||||
Length m_y2;
|
||||
|
||||
bool m_hasX1{false};
|
||||
bool m_hasY1{false};
|
||||
bool m_hasX2{false};
|
||||
bool m_hasY2{false};
|
||||
};
|
||||
|
||||
class RadialGradientAttributes : public GradientAttributes
|
||||
{
|
||||
public:
|
||||
RadialGradientAttributes() = default;
|
||||
|
||||
const Length& cx() const { return m_cx; }
|
||||
const Length& cy() const { return m_cy; }
|
||||
const Length& r() const { return m_r; }
|
||||
const Length& fx() const { return m_fx; }
|
||||
const Length& fy() const { return m_fy; }
|
||||
|
||||
bool hasCx() const { return m_hasCx; }
|
||||
bool hasCy() const { return m_hasCy; }
|
||||
bool hasR() const { return m_hasR; }
|
||||
bool hasFx() const { return m_hasFx; }
|
||||
bool hasFy() const { return m_hasFy; }
|
||||
|
||||
void setCx(const Length& cx)
|
||||
{
|
||||
m_cx = cx;
|
||||
m_hasCx = true;
|
||||
}
|
||||
|
||||
void setCy(const Length& cy)
|
||||
{
|
||||
m_cy = cy;
|
||||
m_hasCy = true;
|
||||
}
|
||||
|
||||
void setR(const Length& r)
|
||||
{
|
||||
m_r = r;
|
||||
m_hasR = true;
|
||||
}
|
||||
|
||||
void setFx(const Length& fx)
|
||||
{
|
||||
m_fx = fx;
|
||||
m_hasFx = true;
|
||||
}
|
||||
|
||||
void setFy(const Length& fy)
|
||||
{
|
||||
m_fy = fy;
|
||||
m_hasFy = true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Length m_cx{50, LengthUnits::Percent};
|
||||
Length m_cy{50, LengthUnits::Percent};
|
||||
Length m_r{50, LengthUnits::Percent};
|
||||
Length m_fx;
|
||||
Length m_fy;
|
||||
|
||||
bool m_hasCx{false};
|
||||
bool m_hasCy{false};
|
||||
bool m_hasR{false};
|
||||
bool m_hasFx{false};
|
||||
bool m_hasFy{false};
|
||||
};
|
||||
|
||||
class PatternAttributes
|
||||
{
|
||||
public:
|
||||
PatternAttributes() = default;
|
||||
|
||||
const Length& x() const { return m_x; }
|
||||
const Length& y() const { return m_y; }
|
||||
const Length& width() const { return m_width; }
|
||||
const Length& height() const { return m_height; }
|
||||
const Transform& patternTransform() const { return m_patternTransform; }
|
||||
Units patternUnits() const { return m_patternUnits; }
|
||||
Units patternContentUnits() const { return m_patternContentUnits; }
|
||||
const Rect& viewBox() const { return m_viewBox; }
|
||||
const PreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio; }
|
||||
const PatternElement* patternContentElement() const { return m_patternContentElement; }
|
||||
|
||||
bool hasX() const { return m_hasX; }
|
||||
bool hasY() const { return m_hasY; }
|
||||
bool hasWidth() const { return m_hasWidth; }
|
||||
bool hasHeight() const { return m_hasHeight; }
|
||||
bool hasPatternTransform() const { return m_hasPatternTransform; }
|
||||
bool hasPatternUnits() const { return m_hasPatternUnits; }
|
||||
bool hasPatternContentUnits() const { return m_hasPatternContentUnits; }
|
||||
bool hasViewBox() const { return m_hasViewBox; }
|
||||
bool hasPreserveAspectRatio() const { return m_hasPreserveAspectRatio; }
|
||||
bool hasPatternContentElement() const { return m_hasPatternContentElement; }
|
||||
|
||||
void setX(const Length& x)
|
||||
{
|
||||
m_x = x;
|
||||
m_hasX = true;
|
||||
}
|
||||
|
||||
void setY(const Length& y)
|
||||
{
|
||||
m_y = y;
|
||||
m_hasY = true;
|
||||
}
|
||||
|
||||
void setWidth(const Length& width)
|
||||
{
|
||||
m_width = width;
|
||||
m_hasWidth = true;
|
||||
}
|
||||
|
||||
void setHeight(const Length& height)
|
||||
{
|
||||
m_height = height;
|
||||
m_hasHeight = true;
|
||||
}
|
||||
|
||||
void setPatternTransform(const Transform& patternTransform)
|
||||
{
|
||||
m_patternTransform = patternTransform;
|
||||
m_hasPatternTransform = true;
|
||||
}
|
||||
|
||||
void setPatternUnits(Units patternUnits)
|
||||
{
|
||||
m_patternUnits = patternUnits;
|
||||
m_hasPatternUnits = true;
|
||||
}
|
||||
|
||||
void setPatternContentUnits(Units patternContentUnits)
|
||||
{
|
||||
m_patternContentUnits = patternContentUnits;
|
||||
m_hasPatternContentUnits = true;
|
||||
}
|
||||
|
||||
void setViewBox(const Rect& viewBox)
|
||||
{
|
||||
m_viewBox = viewBox;
|
||||
m_hasViewBox = true;
|
||||
}
|
||||
|
||||
void setPreserveAspectRatio(const PreserveAspectRatio& preserveAspectRatio)
|
||||
{
|
||||
m_preserveAspectRatio = preserveAspectRatio;
|
||||
m_hasPreserveAspectRatio = true;
|
||||
}
|
||||
|
||||
void setPatternContentElement(const PatternElement* patternContentElement)
|
||||
{
|
||||
m_patternContentElement = patternContentElement;
|
||||
m_hasPatternContentElement = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Length m_x;
|
||||
Length m_y;
|
||||
Length m_width;
|
||||
Length m_height;
|
||||
Transform m_patternTransform;
|
||||
Units m_patternUnits{Units::ObjectBoundingBox};
|
||||
Units m_patternContentUnits{Units::UserSpaceOnUse};
|
||||
Rect m_viewBox{Rect::Invalid};
|
||||
PreserveAspectRatio m_preserveAspectRatio;
|
||||
const PatternElement* m_patternContentElement{nullptr};
|
||||
|
||||
bool m_hasX{false};
|
||||
bool m_hasY{false};
|
||||
bool m_hasWidth{false};
|
||||
bool m_hasHeight{false};
|
||||
bool m_hasPatternTransform{false};
|
||||
bool m_hasPatternUnits{false};
|
||||
bool m_hasPatternContentUnits{false};
|
||||
bool m_hasViewBox{false};
|
||||
bool m_hasPreserveAspectRatio{false};
|
||||
bool m_hasPatternContentElement{false};
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // PAINTELEMENT_H
|
2073
source/parser.cpp
Normal file
2073
source/parser.cpp
Normal file
File diff suppressed because it is too large
Load diff
195
source/parser.h
Normal file
195
source/parser.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "property.h"
|
||||
#include "element.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class SVGElement;
|
||||
class StyledElement;
|
||||
|
||||
enum LengthNegativeValuesMode
|
||||
{
|
||||
AllowNegativeLengths,
|
||||
ForbidNegativeLengths
|
||||
};
|
||||
|
||||
enum class TransformType
|
||||
{
|
||||
Matrix,
|
||||
Rotate,
|
||||
Scale,
|
||||
SkewX,
|
||||
SkewY,
|
||||
Translate
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
static Length parseLength(const std::string& string, LengthNegativeValuesMode mode, const Length& defaultValue);
|
||||
static LengthList parseLengthList(const std::string& string, LengthNegativeValuesMode mode);
|
||||
static double parseNumber(const std::string& string, double defaultValue);
|
||||
static double parseNumberPercentage(const std::string& string, double defaultValue);
|
||||
static PointList parsePointList(const std::string& string);
|
||||
static Transform parseTransform(const std::string& string);
|
||||
static Path parsePath(const std::string& string);
|
||||
static std::string parseUrl(const std::string& string);
|
||||
static std::string parseHref(const std::string& string);
|
||||
static Rect parseViewBox(const std::string& string);
|
||||
static PreserveAspectRatio parsePreserveAspectRatio(const std::string& string);
|
||||
static Angle parseAngle(const std::string& string);
|
||||
static MarkerUnits parseMarkerUnits(const std::string& string);
|
||||
static SpreadMethod parseSpreadMethod(const std::string& string);
|
||||
static Units parseUnits(const std::string& string, Units defaultValue);
|
||||
static Color parseColor(const std::string& string, const StyledElement* element, const Color& defaultValue);
|
||||
static Paint parsePaint(const std::string& string, const StyledElement* element, const Color& defaultValue);
|
||||
static WindRule parseWindRule(const std::string& string);
|
||||
static LineCap parseLineCap(const std::string& string);
|
||||
static LineJoin parseLineJoin(const std::string& string);
|
||||
static Display parseDisplay(const std::string& string);
|
||||
static Visibility parseVisibility(const std::string& string);
|
||||
static Overflow parseOverflow(const std::string& string, Overflow defaultValue);
|
||||
|
||||
private:
|
||||
static bool parseLength(const char*& ptr, const char* end, double& value, LengthUnits& units, LengthNegativeValuesMode mode);
|
||||
static bool parseNumberList(const char*& ptr, const char* end, double* values, int count);
|
||||
static bool parseArcFlag(const char*& ptr, const char* end, bool& flag);
|
||||
static bool parseColorComponent(const char*& ptr, const char* end, double& value);
|
||||
static bool parseUrlFragment(const char*& ptr, const char* end, std::string& ref);
|
||||
static bool parseTransform(const char*& ptr, const char* end, TransformType& type, double* values, int& count);
|
||||
};
|
||||
|
||||
struct Selector;
|
||||
|
||||
struct AttributeSelector
|
||||
{
|
||||
enum class MatchType
|
||||
{
|
||||
None,
|
||||
Equal,
|
||||
Includes,
|
||||
DashMatch,
|
||||
StartsWith,
|
||||
EndsWith,
|
||||
Contains
|
||||
};
|
||||
|
||||
PropertyId id{PropertyId::Unknown};
|
||||
std::string value;
|
||||
MatchType matchType{MatchType::None};
|
||||
};
|
||||
|
||||
using SelectorList = std::vector<Selector>;
|
||||
|
||||
struct PseudoClass
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Unknown,
|
||||
Empty,
|
||||
Root,
|
||||
Not,
|
||||
FirstChild,
|
||||
LastChild,
|
||||
OnlyChild,
|
||||
FirstOfType,
|
||||
LastOfType,
|
||||
OnlyOfType
|
||||
};
|
||||
|
||||
Type type{Type::Unknown};
|
||||
SelectorList notSelectors;
|
||||
};
|
||||
|
||||
struct SimpleSelector
|
||||
{
|
||||
enum class Combinator
|
||||
{
|
||||
Descendant,
|
||||
Child,
|
||||
DirectAdjacent,
|
||||
InDirectAdjacent
|
||||
};
|
||||
|
||||
ElementId id{ElementId::Star};
|
||||
std::vector<AttributeSelector> attributeSelectors;
|
||||
std::vector<PseudoClass> pseudoClasses;
|
||||
Combinator combinator{Combinator::Descendant};
|
||||
};
|
||||
|
||||
struct Selector
|
||||
{
|
||||
std::vector<SimpleSelector> simpleSelectors;
|
||||
int specificity{0};
|
||||
};
|
||||
|
||||
struct Rule
|
||||
{
|
||||
SelectorList selectors;
|
||||
PropertyList declarations;
|
||||
};
|
||||
|
||||
class RuleMatchContext
|
||||
{
|
||||
public:
|
||||
RuleMatchContext(const std::vector<Rule>& rules);
|
||||
|
||||
std::vector<const PropertyList*> match(const Element* element) const;
|
||||
|
||||
private:
|
||||
bool selectorMatch(const Selector* selector, const Element* element) const;
|
||||
bool simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const;
|
||||
bool attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const;
|
||||
bool pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const;
|
||||
|
||||
private:
|
||||
std::multimap<int, std::pair<const Selector*, const PropertyList*>, std::less<int>> m_selectors;
|
||||
};
|
||||
|
||||
class CSSParser
|
||||
{
|
||||
public:
|
||||
CSSParser() = default;
|
||||
|
||||
bool parseMore(const std::string& value);
|
||||
|
||||
const std::vector<Rule>& rules() const { return m_rules; }
|
||||
|
||||
private:
|
||||
bool parseAtRule(const char*& ptr, const char* end) const;
|
||||
bool parseRule(const char*& ptr, const char* end, Rule& rule) const;
|
||||
bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const;
|
||||
bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const;
|
||||
bool parseSelector(const char*& ptr, const char* end, Selector& selector) const;
|
||||
bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const;
|
||||
|
||||
private:
|
||||
std::vector<Rule> m_rules;
|
||||
};
|
||||
|
||||
class LayoutSymbol;
|
||||
|
||||
class ParseDocument
|
||||
{
|
||||
public:
|
||||
ParseDocument();
|
||||
~ParseDocument();
|
||||
|
||||
bool parse(const char* data, std::size_t size);
|
||||
|
||||
SVGElement* rootElement() const { return m_rootElement.get(); }
|
||||
Element* getElementById(const std::string& id) const;
|
||||
std::unique_ptr<LayoutSymbol> layout() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SVGElement> m_rootElement;
|
||||
std::map<std::string, Element*> m_idCache;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // PARSER_H
|
267
source/parserutils.h
Normal file
267
source/parserutils.h
Normal file
|
@ -0,0 +1,267 @@
|
|||
#ifndef PARSERUTILS_H
|
||||
#define PARSERUTILS_H
|
||||
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')
|
||||
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
|
||||
#define IS_WS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
|
||||
|
||||
namespace Utils {
|
||||
|
||||
inline const char* rtrim(const char* start, const char* end)
|
||||
{
|
||||
while(end > start && IS_WS(end[-1]))
|
||||
--end;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
inline const char* ltrim(const char* start, const char* end)
|
||||
{
|
||||
while(start < end && IS_WS(*start))
|
||||
++start;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
inline bool skipDesc(const char*& ptr, const char* end, const char ch)
|
||||
{
|
||||
if(ptr >= end || *ptr != ch)
|
||||
return false;
|
||||
|
||||
++ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool skipDesc(const char*& ptr, const char* end, const char* data)
|
||||
{
|
||||
int read = 0;
|
||||
while(data[read])
|
||||
{
|
||||
if(ptr >= end || *ptr != data[read])
|
||||
{
|
||||
ptr -= read;
|
||||
return false;
|
||||
}
|
||||
|
||||
++read;
|
||||
++ptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool skipUntil(const char*& ptr, const char* end, const char ch)
|
||||
{
|
||||
while(ptr < end && *ptr != ch)
|
||||
++ptr;
|
||||
|
||||
return ptr < end;
|
||||
}
|
||||
|
||||
inline bool skipUntil(const char*& ptr, const char* end, const char* data)
|
||||
{
|
||||
while(ptr < end)
|
||||
{
|
||||
auto start = ptr;
|
||||
if(skipDesc(start, end, data))
|
||||
break;
|
||||
++ptr;
|
||||
}
|
||||
|
||||
return ptr < end;
|
||||
}
|
||||
|
||||
inline bool readUntil(const char*& ptr, const char* end, const char ch, std::string& value)
|
||||
{
|
||||
auto start = ptr;
|
||||
if(!skipUntil(ptr, end, ch))
|
||||
return false;
|
||||
|
||||
value.assign(start, ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool readUntil(const char*& ptr, const char* end, const char* data, std::string& value)
|
||||
{
|
||||
auto start = ptr;
|
||||
if(!skipUntil(ptr, end, data))
|
||||
return false;
|
||||
|
||||
value.assign(start, ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool skipWs(const char*& ptr, const char* end)
|
||||
{
|
||||
while(ptr < end && IS_WS(*ptr))
|
||||
++ptr;
|
||||
|
||||
return ptr < end;
|
||||
}
|
||||
|
||||
inline bool skipWsDelimiter(const char*& ptr, const char* end, const char delimiter)
|
||||
{
|
||||
if(ptr < end && !IS_WS(*ptr) && *ptr != delimiter)
|
||||
return false;
|
||||
|
||||
if(skipWs(ptr, end))
|
||||
{
|
||||
if(ptr < end && *ptr == delimiter)
|
||||
{
|
||||
++ptr;
|
||||
skipWs(ptr, end);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr < end;
|
||||
}
|
||||
|
||||
inline bool skipWsComma(const char*& ptr, const char* end)
|
||||
{
|
||||
return skipWsDelimiter(ptr, end, ',');
|
||||
}
|
||||
|
||||
inline bool isIntegralDigit(char ch, int base)
|
||||
{
|
||||
if(IS_NUM(ch))
|
||||
return ch - '0' < base;
|
||||
|
||||
if(IS_ALPHA(ch))
|
||||
return (ch >= 'a' && ch < 'a' + std::min(base, 36) - 10) || (ch >= 'A' && ch < 'A' + std::min(base, 36) - 10);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool parseInteger(const char*& ptr, const char* end, T& integer, int base = 10)
|
||||
{
|
||||
bool isNegative = 0;
|
||||
T value = 0;
|
||||
|
||||
static const T intMax = std::numeric_limits<T>::max();
|
||||
static const bool isSigned = std::numeric_limits<T>::is_signed;
|
||||
using signed_t = typename std::make_signed<T>::type;
|
||||
const T maxMultiplier = intMax / static_cast<T>(base);
|
||||
|
||||
if(ptr < end && *ptr == '+')
|
||||
++ptr;
|
||||
else if(ptr < end && isSigned && *ptr == '-')
|
||||
{
|
||||
++ptr;
|
||||
isNegative = true;
|
||||
}
|
||||
|
||||
if(ptr >= end || !isIntegralDigit(*ptr, base))
|
||||
return false;
|
||||
|
||||
do {
|
||||
const char ch = *ptr++;
|
||||
int digitValue;
|
||||
if(IS_NUM(ch))
|
||||
digitValue = ch - '0';
|
||||
else if(ch >= 'a')
|
||||
digitValue = ch - 'a' + 10;
|
||||
else
|
||||
digitValue = ch - 'A' + 10;
|
||||
|
||||
if(value > maxMultiplier || (value == maxMultiplier && static_cast<T>(digitValue) > (intMax % static_cast<T>(base)) + isNegative))
|
||||
return false;
|
||||
value = static_cast<T>(base) * value + static_cast<T>(digitValue);
|
||||
} while(ptr < end && isIntegralDigit(*ptr, base));
|
||||
|
||||
if(isNegative)
|
||||
integer = -static_cast<signed_t>(value);
|
||||
else
|
||||
integer = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool parseNumber(const char*& ptr, const char* end, T& number)
|
||||
{
|
||||
T integer, fraction;
|
||||
int sign, expsign, exponent;
|
||||
|
||||
static const T numberMax = std::numeric_limits<T>::max();
|
||||
fraction = 0;
|
||||
integer = 0;
|
||||
exponent = 0;
|
||||
sign = 1;
|
||||
expsign = 1;
|
||||
|
||||
if(ptr < end && *ptr == '+')
|
||||
++ptr;
|
||||
else if(ptr < end && *ptr == '-')
|
||||
{
|
||||
++ptr;
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
if(ptr >= end || !(IS_NUM(*ptr) || *ptr == '.'))
|
||||
return false;
|
||||
|
||||
if(*ptr != '.')
|
||||
{
|
||||
do {
|
||||
integer = static_cast<T>(10) * integer + (*ptr - '0');
|
||||
++ptr;
|
||||
} while(ptr < end && IS_NUM(*ptr));
|
||||
}
|
||||
|
||||
if(ptr < end && *ptr == '.')
|
||||
{
|
||||
++ptr;
|
||||
if(ptr >= end || !IS_NUM(*ptr))
|
||||
return false;
|
||||
|
||||
T divisor = 1;
|
||||
do {
|
||||
fraction = static_cast<T>(10) * fraction + (*ptr - '0');
|
||||
divisor *= static_cast<T>(10);
|
||||
++ptr;
|
||||
} while(ptr < end && IS_NUM(*ptr));
|
||||
fraction /= divisor;
|
||||
}
|
||||
|
||||
if(ptr < end && (*ptr == 'e' || *ptr == 'E')
|
||||
&& (ptr[1] != 'x' && ptr[1] != 'm'))
|
||||
{
|
||||
++ptr;
|
||||
if(ptr < end && *ptr == '+')
|
||||
++ptr;
|
||||
else if(ptr < end && *ptr == '-')
|
||||
{
|
||||
++ptr;
|
||||
expsign = -1;
|
||||
}
|
||||
|
||||
if(ptr >= end || !IS_NUM(*ptr))
|
||||
return false;
|
||||
|
||||
do {
|
||||
exponent = 10 * exponent + (*ptr - '0');
|
||||
++ptr;
|
||||
} while(ptr < end && IS_NUM(*ptr));
|
||||
}
|
||||
|
||||
number = sign * (integer + fraction);
|
||||
if(exponent)
|
||||
number *= static_cast<T>(pow(10.0, expsign*exponent));
|
||||
|
||||
return number >= -numberMax && number <= numberMax;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // PARSERUTILS_H
|
698
source/property.cpp
Normal file
698
source/property.cpp
Normal file
|
@ -0,0 +1,698 @@
|
|||
#include "property.h"
|
||||
#include "styledelement.h"
|
||||
#include "lunasvg.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
const Color Color::Black{0, 0, 0, 1};
|
||||
const Color Color::White{1, 1, 1, 1};
|
||||
const Color Color::Red{1, 0, 0, 1};
|
||||
const Color Color::Green{0, 1, 0, 1};
|
||||
const Color Color::Blue{0, 0, 1, 1};
|
||||
const Color Color::Yellow{1, 1, 0, 1};
|
||||
const Color Color::Transparent{0, 0, 0, 0};
|
||||
|
||||
Color::Color(double r, double g, double b, double a)
|
||||
: r(r), g(g), b(b), a(a)
|
||||
{
|
||||
}
|
||||
|
||||
Paint::Paint(const Color& color)
|
||||
: m_color(color)
|
||||
{
|
||||
}
|
||||
|
||||
Paint::Paint(const std::string& ref, const Color& color)
|
||||
: m_ref(ref), m_color(color)
|
||||
{
|
||||
}
|
||||
|
||||
Point::Point(double x, double y)
|
||||
: x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
const Rect Rect::Empty{0, 0, 0, 0};
|
||||
const Rect Rect::Invalid{0, 0, -1, -1};
|
||||
|
||||
Rect::Rect(double x, double y, double w, double h)
|
||||
: x(x), y(y), w(w), h(h)
|
||||
{
|
||||
}
|
||||
|
||||
Rect::Rect(const Box& box)
|
||||
: x(box.x), y(box.y), w(box.w), h(box.h)
|
||||
{
|
||||
}
|
||||
|
||||
Rect Rect::operator&(const Rect& rect) const
|
||||
{
|
||||
if(!rect.valid())
|
||||
return *this;
|
||||
|
||||
if(!valid())
|
||||
return rect;
|
||||
|
||||
auto l = std::max(x, rect.x);
|
||||
auto t = std::max(y, rect.y);
|
||||
auto r = std::min(x + w, rect.x + rect.w);
|
||||
auto b = std::min(y + h, rect.y + rect.h);
|
||||
|
||||
return Rect{l, t, r-l, b-t};
|
||||
}
|
||||
|
||||
Rect Rect::operator|(const Rect& rect) const
|
||||
{
|
||||
if(!rect.valid())
|
||||
return *this;
|
||||
|
||||
if(!valid())
|
||||
return rect;
|
||||
|
||||
auto l = std::min(x, rect.x);
|
||||
auto t = std::min(y, rect.y);
|
||||
auto r = std::max(x + w, rect.x + rect.w);
|
||||
auto b = std::max(y + h, rect.y + rect.h);
|
||||
|
||||
return Rect{l, t, r-l, b-t};
|
||||
}
|
||||
|
||||
Rect& Rect::intersect(const Rect& rect)
|
||||
{
|
||||
*this = *this & rect;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rect& Rect::unite(const Rect& rect)
|
||||
{
|
||||
*this = *this | rect;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform::Transform(double m00, double m10, double m01, double m11, double m02, double m12)
|
||||
: m00(m00), m10(m10), m01(m01), m11(m11), m02(m02), m12(m12)
|
||||
{
|
||||
}
|
||||
|
||||
Transform::Transform(const Matrix& matrix)
|
||||
: m00(matrix.a), m10(matrix.b), m01(matrix.c), m11(matrix.d), m02(matrix.e), m12(matrix.f)
|
||||
{
|
||||
}
|
||||
|
||||
Transform Transform::inverted() const
|
||||
{
|
||||
double det = (this->m00 * this->m11 - this->m10 * this->m01);
|
||||
if(det == 0.0)
|
||||
return Transform{};
|
||||
|
||||
double inv_det = 1.0 / det;
|
||||
double m00 = this->m00 * inv_det;
|
||||
double m10 = this->m10 * inv_det;
|
||||
double m01 = this->m01 * inv_det;
|
||||
double m11 = this->m11 * inv_det;
|
||||
double m02 = (this->m01 * this->m12 - this->m11 * this->m02) * inv_det;
|
||||
double m12 = (this->m10 * this->m02 - this->m00 * this->m12) * inv_det;
|
||||
|
||||
return Transform{m11, -m10, -m01, m00, m02, m12};
|
||||
}
|
||||
|
||||
Transform Transform::operator*(const Transform& transform) const
|
||||
{
|
||||
double m00 = this->m00 * transform.m00 + this->m10 * transform.m01;
|
||||
double m10 = this->m00 * transform.m10 + this->m10 * transform.m11;
|
||||
double m01 = this->m01 * transform.m00 + this->m11 * transform.m01;
|
||||
double m11 = this->m01 * transform.m10 + this->m11 * transform.m11;
|
||||
double m02 = this->m02 * transform.m00 + this->m12 * transform.m01 + transform.m02;
|
||||
double m12 = this->m02 * transform.m10 + this->m12 * transform.m11 + transform.m12;
|
||||
|
||||
return Transform{m00, m10, m01, m11, m02, m12};
|
||||
}
|
||||
|
||||
Transform& Transform::operator*=(const Transform& transform)
|
||||
{
|
||||
*this = *this * transform;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::premultiply(const Transform& transform)
|
||||
{
|
||||
*this = transform * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::postmultiply(const Transform& transform)
|
||||
{
|
||||
*this = *this * transform;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::rotate(double angle)
|
||||
{
|
||||
*this = rotated(angle) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::rotate(double angle, double cx, double cy)
|
||||
{
|
||||
*this = rotated(angle, cx, cy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::scale(double sx, double sy)
|
||||
{
|
||||
*this = scaled(sx, sy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::shear(double shx, double shy)
|
||||
{
|
||||
*this = sheared(shx, shy) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::translate(double tx, double ty)
|
||||
{
|
||||
*this = translated(tx, ty) * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::transform(double m00, double m10, double m01, double m11, double m02, double m12)
|
||||
{
|
||||
*this = Transform{m00, m10, m01, m11, m02, m12} * *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::identity()
|
||||
{
|
||||
*this = Transform{1, 0, 0, 1, 0, 0};
|
||||
return *this;
|
||||
}
|
||||
|
||||
Transform& Transform::invert()
|
||||
{
|
||||
*this = inverted();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Transform::map(double x, double y, double* _x, double* _y) const
|
||||
{
|
||||
*_x = x * m00 + y * m01 + m02;
|
||||
*_y = x * m10 + y * m11 + m12;
|
||||
}
|
||||
|
||||
Point Transform::map(double x, double y) const
|
||||
{
|
||||
map(x, y, &x, &y);
|
||||
return Point{x, y};
|
||||
}
|
||||
|
||||
Point Transform::map(const Point& point) const
|
||||
{
|
||||
return map(point.x, point.y);
|
||||
}
|
||||
|
||||
Rect Transform::map(const Rect& rect) const
|
||||
{
|
||||
if(!rect.valid())
|
||||
return Rect::Invalid;
|
||||
|
||||
auto x1 = rect.x;
|
||||
auto y1 = rect.y;
|
||||
auto x2 = rect.x + rect.w;
|
||||
auto y2 = rect.y + rect.h;
|
||||
|
||||
const Point p[] = {
|
||||
map(x1, y1), map(x2, y1),
|
||||
map(x2, y2), map(x1, y2)
|
||||
};
|
||||
|
||||
auto l = p[0].x;
|
||||
auto t = p[0].y;
|
||||
auto r = p[0].x;
|
||||
auto b = p[0].y;
|
||||
|
||||
for(int i = 1;i < 4;i++)
|
||||
{
|
||||
if(p[i].x < l) l = p[i].x;
|
||||
if(p[i].x > r) r = p[i].x;
|
||||
if(p[i].y < t) t = p[i].y;
|
||||
if(p[i].y > b) b = p[i].y;
|
||||
}
|
||||
|
||||
return Rect{l, t, r-l, b-t};
|
||||
}
|
||||
|
||||
static const double pi = 3.14159265358979323846;
|
||||
|
||||
Transform Transform::rotated(double angle)
|
||||
{
|
||||
auto c = std::cos(angle * pi / 180.0);
|
||||
auto s = std::sin(angle * pi / 180.0);
|
||||
|
||||
return Transform{c, s, -s, c, 0, 0};
|
||||
}
|
||||
|
||||
Transform Transform::rotated(double angle, double cx, double cy)
|
||||
{
|
||||
auto c = std::cos(angle * pi / 180.0);
|
||||
auto s = std::sin(angle * pi / 180.0);
|
||||
|
||||
auto x = cx * (1 - c) + cy * s;
|
||||
auto y = cy * (1 - c) - cx * s;
|
||||
|
||||
return Transform{c, s, -s, c, x, y};
|
||||
}
|
||||
|
||||
Transform Transform::scaled(double sx, double sy)
|
||||
{
|
||||
return Transform{sx, 0, 0, sy, 0, 0};
|
||||
}
|
||||
|
||||
Transform Transform::sheared(double shx, double shy)
|
||||
{
|
||||
auto x = std::tan(shx * pi / 180.0);
|
||||
auto y = std::tan(shy * pi / 180.0);
|
||||
|
||||
return Transform{1, y, x, 1, 0, 0};
|
||||
}
|
||||
|
||||
Transform Transform::translated(double tx, double ty)
|
||||
{
|
||||
return Transform{1, 0, 0, 1, tx, ty};
|
||||
}
|
||||
|
||||
void Path::moveTo(double x, double y)
|
||||
{
|
||||
m_commands.push_back(PathCommand::MoveTo);
|
||||
m_points.emplace_back(x, y);
|
||||
}
|
||||
|
||||
void Path::lineTo(double x, double y)
|
||||
{
|
||||
m_commands.push_back(PathCommand::LineTo);
|
||||
m_points.emplace_back(x, y);
|
||||
}
|
||||
|
||||
void Path::cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)
|
||||
{
|
||||
m_commands.push_back(PathCommand::CubicTo);
|
||||
m_points.emplace_back(x1, y1);
|
||||
m_points.emplace_back(x2, y2);
|
||||
m_points.emplace_back(x3, y3);
|
||||
}
|
||||
|
||||
void Path::close()
|
||||
{
|
||||
if(m_commands.empty())
|
||||
return;
|
||||
|
||||
if(m_commands.back() == PathCommand::Close)
|
||||
return;
|
||||
|
||||
m_commands.push_back(PathCommand::Close);
|
||||
}
|
||||
|
||||
void Path::reset()
|
||||
{
|
||||
m_commands.clear();
|
||||
m_points.clear();
|
||||
}
|
||||
|
||||
bool Path::empty() const
|
||||
{
|
||||
return m_commands.empty();
|
||||
}
|
||||
|
||||
void Path::quadTo(double cx, double cy, double x1, double y1, double x2, double y2)
|
||||
{
|
||||
auto cx1 = 2.0 / 3.0 * x1 + 1.0 / 3.0 * cx;
|
||||
auto cy1 = 2.0 / 3.0 * y1 + 1.0 / 3.0 * cy;
|
||||
auto cx2 = 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2;
|
||||
auto cy2 = 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2;
|
||||
cubicTo(cx1, cy1, cx2, cy2, x2, y2);
|
||||
}
|
||||
|
||||
void Path::arcTo(double cx, double cy, double rx, double ry, double xAxisRotation, bool largeArcFlag, bool sweepFlag, double x, double y)
|
||||
{
|
||||
rx = std::fabs(rx);
|
||||
ry = std::fabs(ry);
|
||||
|
||||
auto sin_th = std::sin(xAxisRotation * pi / 180.0);
|
||||
auto cos_th = std::cos(xAxisRotation * pi / 180.0);
|
||||
|
||||
auto dx = (cx - x) / 2.0;
|
||||
auto dy = (cy - y) / 2.0;
|
||||
auto dx1 = cos_th * dx + sin_th * dy;
|
||||
auto dy1 = -sin_th * dx + cos_th * dy;
|
||||
auto Pr1 = rx * rx;
|
||||
auto Pr2 = ry * ry;
|
||||
auto Px = dx1 * dx1;
|
||||
auto Py = dy1 * dy1;
|
||||
auto check = Px / Pr1 + Py / Pr2;
|
||||
if(check > 1)
|
||||
{
|
||||
rx = rx * std::sqrt(check);
|
||||
ry = ry * std::sqrt(check);
|
||||
}
|
||||
|
||||
auto a00 = cos_th / rx;
|
||||
auto a01 = sin_th / rx;
|
||||
auto a10 = -sin_th / ry;
|
||||
auto a11 = cos_th / ry;
|
||||
auto x0 = a00 * cx + a01 * cy;
|
||||
auto y0 = a10 * cx + a11 * cy;
|
||||
auto x1 = a00 * x + a01 * y;
|
||||
auto y1 = a10 * x + a11 * y;
|
||||
auto d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
|
||||
auto sfactor_sq = 1.0 / d - 0.25;
|
||||
if(sfactor_sq < 0) sfactor_sq = 0;
|
||||
auto sfactor = std::sqrt(sfactor_sq);
|
||||
if(sweepFlag == largeArcFlag) sfactor = -sfactor;
|
||||
auto xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
|
||||
auto yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
|
||||
|
||||
auto th0 = std::atan2(y0 - yc, x0 - xc);
|
||||
auto th1 = std::atan2(y1 - yc, x1 - xc);
|
||||
|
||||
double th_arc = th1 - th0;
|
||||
if(th_arc < 0.0 && sweepFlag)
|
||||
th_arc += 2.0 * pi;
|
||||
else if(th_arc > 0.0 && !sweepFlag)
|
||||
th_arc -= 2.0 * pi;
|
||||
|
||||
auto n_segs = static_cast<int>(std::ceil(std::fabs(th_arc / (pi * 0.5 + 0.001))));
|
||||
for(int i = 0;i < n_segs;i++)
|
||||
{
|
||||
auto th2 = th0 + i * th_arc / n_segs;
|
||||
auto th3 = th0 + (i + 1) * th_arc / n_segs;
|
||||
|
||||
auto a00 = cos_th * rx;
|
||||
auto a01 = -sin_th * ry;
|
||||
auto a10 = sin_th * rx;
|
||||
auto a11 = cos_th * ry;
|
||||
|
||||
auto thHalf = 0.5 * (th3 - th2);
|
||||
auto t = (8.0 / 3.0) * std::sin(thHalf * 0.5) * std::sin(thHalf * 0.5) / std::sin(thHalf);
|
||||
auto x1 = xc + std::cos(th2) - t * std::sin(th2);
|
||||
auto y1 = yc + std::sin(th2) + t * std::cos(th2);
|
||||
auto x3 = xc + std::cos(th3);
|
||||
auto y3 = yc + std::sin(th3);
|
||||
auto x2 = x3 + t * std::sin(th3);
|
||||
auto y2 = y3 - t * std::cos(th3);
|
||||
|
||||
auto cx1 = a00 * x1 + a01 * y1;
|
||||
auto cy1 = a10 * x1 + a11 * y1;
|
||||
auto cx2 = a00 * x2 + a01 * y2;
|
||||
auto cy2 = a10 * x2 + a11 * y2;
|
||||
auto cx3 = a00 * x3 + a01 * y3;
|
||||
auto cy3 = a10 * x3 + a11 * y3;
|
||||
cubicTo(cx1, cy1, cx2, cy2, cx3, cy3);
|
||||
}
|
||||
}
|
||||
|
||||
static const double kappa = 0.55228474983079339840;
|
||||
|
||||
void Path::ellipse(double cx, double cy, double rx, double ry)
|
||||
{
|
||||
auto left = cx - rx;
|
||||
auto top = cy - ry;
|
||||
auto right = cx + rx;
|
||||
auto bottom = cy + ry;
|
||||
|
||||
auto cpx = rx * kappa;
|
||||
auto cpy = ry * kappa;
|
||||
|
||||
moveTo(cx, top);
|
||||
cubicTo(cx+cpx, top, right, cy-cpy, right, cy);
|
||||
cubicTo(right, cy+cpy, cx+cpx, bottom, cx, bottom);
|
||||
cubicTo(cx-cpx, bottom, left, cy+cpy, left, cy);
|
||||
cubicTo(left, cy-cpy, cx-cpx, top, cx, top);
|
||||
close();
|
||||
}
|
||||
|
||||
void Path::rect(double x, double y, double w, double h, double rx, double ry)
|
||||
{
|
||||
rx = std::min(rx, w * 0.5);
|
||||
ry = std::min(ry, h * 0.5);
|
||||
|
||||
auto right = x + w;
|
||||
auto bottom = y + h;
|
||||
|
||||
if(rx == 0.0 && ry == 0.0)
|
||||
{
|
||||
moveTo(x, y);
|
||||
lineTo(right, y);
|
||||
lineTo(right, bottom);
|
||||
lineTo(x, bottom);
|
||||
lineTo(x, y);
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
double cpx = rx * kappa;
|
||||
double cpy = ry * kappa;
|
||||
moveTo(x, y+ry);
|
||||
cubicTo(x, y+ry-cpy, x+rx-cpx, y, x+rx, y);
|
||||
lineTo(right-rx, y);
|
||||
cubicTo(right-rx+cpx, y, right, y+ry-cpy, right, y+ry);
|
||||
lineTo(right, bottom-ry);
|
||||
cubicTo(right, bottom-ry+cpy, right-rx+cpx, bottom, right-rx, bottom);
|
||||
lineTo(x+rx, bottom);
|
||||
cubicTo(x+rx-cpx, bottom, x, bottom-ry+cpy, x, bottom-ry);
|
||||
lineTo(x, y+ry);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
Rect Path::box() const
|
||||
{
|
||||
if(m_points.empty())
|
||||
return Rect{};
|
||||
|
||||
auto l = m_points[0].x;
|
||||
auto t = m_points[0].y;
|
||||
auto r = m_points[0].x;
|
||||
auto b = m_points[0].y;
|
||||
|
||||
for(std::size_t i = 1;i < m_points.size();i++)
|
||||
{
|
||||
if(m_points[i].x < l) l = m_points[i].x;
|
||||
if(m_points[i].x > r) r = m_points[i].x;
|
||||
if(m_points[i].y < t) t = m_points[i].y;
|
||||
if(m_points[i].y > b) b = m_points[i].y;
|
||||
}
|
||||
|
||||
return Rect{l, t, r-l, b-t};
|
||||
}
|
||||
|
||||
PathIterator::PathIterator(const Path& path)
|
||||
: m_commands(path.commands()),
|
||||
m_points(path.points().data())
|
||||
{
|
||||
}
|
||||
|
||||
PathCommand PathIterator::currentSegment(std::array<Point, 3>& points) const
|
||||
{
|
||||
auto command = m_commands[m_index];
|
||||
switch(command) {
|
||||
case PathCommand::MoveTo:
|
||||
points[0] = m_points[0];
|
||||
m_startPoint = points[0];
|
||||
break;
|
||||
case PathCommand::LineTo:
|
||||
points[0] = m_points[0];
|
||||
break;
|
||||
case PathCommand::CubicTo:
|
||||
points[0] = m_points[0];
|
||||
points[1] = m_points[1];
|
||||
points[2] = m_points[2];
|
||||
break;
|
||||
case PathCommand::Close:
|
||||
points[0] = m_startPoint;
|
||||
break;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
bool PathIterator::isDone() const
|
||||
{
|
||||
return (m_index >= m_commands.size());
|
||||
}
|
||||
|
||||
void PathIterator::next()
|
||||
{
|
||||
switch(m_commands[m_index]) {
|
||||
case PathCommand::MoveTo:
|
||||
case PathCommand::LineTo:
|
||||
m_points += 1;
|
||||
break;
|
||||
case PathCommand::CubicTo:
|
||||
m_points += 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_index += 1;
|
||||
}
|
||||
|
||||
const Length Length::Unknown{0, LengthUnits::Unknown};
|
||||
const Length Length::Zero{0, LengthUnits::Number};
|
||||
const Length Length::One{1, LengthUnits::Number};
|
||||
const Length Length::ThreePercent{3, LengthUnits::Percent};
|
||||
const Length Length::HundredPercent{100, LengthUnits::Percent};
|
||||
const Length Length::FiftyPercent{50, LengthUnits::Percent};
|
||||
const Length Length::OneTwentyPercent{120, LengthUnits::Percent};
|
||||
const Length Length::MinusTenPercent{-10, LengthUnits::Percent};
|
||||
|
||||
Length::Length(double value)
|
||||
: m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
Length::Length(double value, LengthUnits units)
|
||||
: m_value(value), m_units(units)
|
||||
{
|
||||
}
|
||||
|
||||
static const double dpi = 96.0;
|
||||
|
||||
double Length::value(double max) const
|
||||
{
|
||||
switch(m_units) {
|
||||
case LengthUnits::Number:
|
||||
case LengthUnits::Px:
|
||||
return m_value;
|
||||
case LengthUnits::In:
|
||||
return m_value * dpi;
|
||||
case LengthUnits::Cm:
|
||||
return m_value * dpi / 2.54;
|
||||
case LengthUnits::Mm:
|
||||
return m_value * dpi / 25.4;
|
||||
case LengthUnits::Pt:
|
||||
return m_value * dpi / 72.0;
|
||||
case LengthUnits::Pc:
|
||||
return m_value * dpi / 6.0;
|
||||
case LengthUnits::Percent:
|
||||
return m_value * max / 100.0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static const double sqrt2 = 1.41421356237309504880;
|
||||
|
||||
double Length::value(const Element* element, LengthMode mode) const
|
||||
{
|
||||
if(m_units == LengthUnits::Percent)
|
||||
{
|
||||
auto viewport = element->currentViewport();
|
||||
auto w = viewport.w;
|
||||
auto h = viewport.h;
|
||||
auto max = (mode == LengthMode::Width) ? w : (mode == LengthMode::Height) ? h : std::sqrt(w*w+h*h) / sqrt2;
|
||||
return m_value * max / 100.0;
|
||||
}
|
||||
|
||||
return value(1.0);
|
||||
}
|
||||
|
||||
LengthContext::LengthContext(const Element* element)
|
||||
: m_element(element)
|
||||
{
|
||||
}
|
||||
|
||||
LengthContext::LengthContext(const Element* element, Units units)
|
||||
: m_element(element), m_units(units)
|
||||
{
|
||||
}
|
||||
|
||||
double LengthContext::valueForLength(const Length& length, LengthMode mode) const
|
||||
{
|
||||
if(m_units == Units::ObjectBoundingBox)
|
||||
return length.value(1.0);
|
||||
return length.value(m_element, mode);
|
||||
}
|
||||
|
||||
PreserveAspectRatio::PreserveAspectRatio(Align align, MeetOrSlice scale)
|
||||
: m_align(align), m_scale(scale)
|
||||
{
|
||||
}
|
||||
|
||||
Transform PreserveAspectRatio::getMatrix(double width, double height, const Rect& viewBox) const
|
||||
{
|
||||
if(viewBox.empty())
|
||||
return Transform{};
|
||||
|
||||
auto xscale = width / viewBox.w;
|
||||
auto yscale = height / viewBox.h;
|
||||
if(m_align == Align::None)
|
||||
{
|
||||
auto xoffset = -viewBox.x * xscale;
|
||||
auto yoffset = -viewBox.y * yscale;
|
||||
return Transform{xscale, 0, 0, yscale, xoffset, yoffset};
|
||||
}
|
||||
|
||||
auto scale = (m_scale == MeetOrSlice::Meet) ? std::min(xscale, yscale) : std::max(xscale, yscale);
|
||||
auto viewWidth = viewBox.w * scale;
|
||||
auto viewHeight = viewBox.h * scale;
|
||||
|
||||
auto xoffset = -viewBox.x * scale;
|
||||
auto yoffset = -viewBox.y * scale;
|
||||
|
||||
switch(m_align) {
|
||||
case Align::xMidYMin:
|
||||
case Align::xMidYMid:
|
||||
case Align::xMidYMax:
|
||||
xoffset += (width - viewWidth) * 0.5;
|
||||
break;
|
||||
case Align::xMaxYMin:
|
||||
case Align::xMaxYMid:
|
||||
case Align::xMaxYMax:
|
||||
xoffset += (width - viewWidth);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(m_align) {
|
||||
case Align::xMinYMid:
|
||||
case Align::xMidYMid:
|
||||
case Align::xMaxYMid:
|
||||
yoffset += (height - viewHeight) * 0.5;
|
||||
break;
|
||||
case Align::xMinYMax:
|
||||
case Align::xMidYMax:
|
||||
case Align::xMaxYMax:
|
||||
yoffset += (height - viewHeight);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Transform{scale, 0, 0, scale, xoffset, yoffset};
|
||||
}
|
||||
|
||||
Rect PreserveAspectRatio::getClip(double width, double height, const Rect& viewBox) const
|
||||
{
|
||||
if(viewBox.empty())
|
||||
return Rect{0, 0, width, height};
|
||||
|
||||
return viewBox;
|
||||
}
|
||||
|
||||
Angle::Angle(MarkerOrient type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
Angle::Angle(double value, MarkerOrient type)
|
||||
: m_value(value), m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
370
source/property.h
Normal file
370
source/property.h
Normal file
|
@ -0,0 +1,370 @@
|
|||
#ifndef PROPERTY_H
|
||||
#define PROPERTY_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
enum class Display
|
||||
{
|
||||
Inline,
|
||||
None
|
||||
};
|
||||
|
||||
enum class Visibility
|
||||
{
|
||||
Visible,
|
||||
Hidden
|
||||
};
|
||||
|
||||
enum class Overflow
|
||||
{
|
||||
Visible,
|
||||
Hidden
|
||||
};
|
||||
|
||||
enum class LineCap
|
||||
{
|
||||
Butt,
|
||||
Round,
|
||||
Square
|
||||
};
|
||||
|
||||
enum class LineJoin
|
||||
{
|
||||
Miter,
|
||||
Round,
|
||||
Bevel
|
||||
};
|
||||
|
||||
enum class WindRule
|
||||
{
|
||||
NonZero,
|
||||
EvenOdd
|
||||
};
|
||||
|
||||
enum class Units
|
||||
{
|
||||
UserSpaceOnUse,
|
||||
ObjectBoundingBox
|
||||
};
|
||||
|
||||
enum class SpreadMethod
|
||||
{
|
||||
Pad,
|
||||
Reflect,
|
||||
Repeat
|
||||
};
|
||||
|
||||
enum class MarkerUnits
|
||||
{
|
||||
StrokeWidth,
|
||||
UserSpaceOnUse
|
||||
};
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
Color() = default;
|
||||
Color(double r, double g, double b, double a = 1);
|
||||
|
||||
bool isNone() const { return a == 0.0; }
|
||||
|
||||
static const Color Black;
|
||||
static const Color White;
|
||||
static const Color Red;
|
||||
static const Color Green;
|
||||
static const Color Blue;
|
||||
static const Color Yellow;
|
||||
static const Color Transparent;
|
||||
|
||||
public:
|
||||
double r{0};
|
||||
double g{0};
|
||||
double b{0};
|
||||
double a{1};
|
||||
};
|
||||
|
||||
class Paint
|
||||
{
|
||||
public:
|
||||
Paint() = default;
|
||||
Paint(const Color& color);
|
||||
Paint(const std::string& ref, const Color& color);
|
||||
|
||||
const Color& color() const { return m_color; }
|
||||
const std::string& ref() const { return m_ref; }
|
||||
bool isNone() const { return m_ref.empty() && m_color.isNone(); }
|
||||
|
||||
public:
|
||||
std::string m_ref;
|
||||
Color m_color{Color::Transparent};
|
||||
};
|
||||
|
||||
class Point
|
||||
{
|
||||
public:
|
||||
Point() = default;
|
||||
Point(double x, double y);
|
||||
|
||||
public:
|
||||
double x{0};
|
||||
double y{0};
|
||||
};
|
||||
|
||||
using PointList = std::vector<Point>;
|
||||
|
||||
class Box;
|
||||
|
||||
class Rect
|
||||
{
|
||||
public:
|
||||
Rect() = default;
|
||||
Rect(double x, double y, double w, double h);
|
||||
Rect(const Box& box);
|
||||
|
||||
Rect operator&(const Rect& rect) const;
|
||||
Rect operator|(const Rect& rect) const;
|
||||
|
||||
Rect& intersect(const Rect& rect);
|
||||
Rect& unite(const Rect& rect);
|
||||
|
||||
bool empty() const { return w <= 0.0 || h <= 0.0; }
|
||||
bool valid() const { return w >= 0.0 && h >= 0.0; }
|
||||
|
||||
static const Rect Empty;
|
||||
static const Rect Invalid;
|
||||
|
||||
public:
|
||||
double x{0};
|
||||
double y{0};
|
||||
double w{0};
|
||||
double h{0};
|
||||
};
|
||||
|
||||
class Matrix;
|
||||
|
||||
class Transform
|
||||
{
|
||||
public:
|
||||
Transform() = default;
|
||||
Transform(double m00, double m10, double m01, double m11, double m02, double m12);
|
||||
Transform(const Matrix& matrix);
|
||||
|
||||
Transform inverted() const;
|
||||
Transform operator*(const Transform& transform) const;
|
||||
Transform& operator*=(const Transform& transform);
|
||||
|
||||
Transform& premultiply(const Transform& transform);
|
||||
Transform& postmultiply(const Transform& transform);
|
||||
Transform& rotate(double angle);
|
||||
Transform& rotate(double angle, double cx, double cy);
|
||||
Transform& scale(double sx, double sy);
|
||||
Transform& shear(double shx, double shy);
|
||||
Transform& translate(double tx, double ty);
|
||||
Transform& transform(double m00, double m10, double m01, double m11, double m02, double m12);
|
||||
Transform& identity();
|
||||
Transform& invert();
|
||||
|
||||
void map(double x, double y, double* _x, double* _y) const;
|
||||
Point map(double x, double y) const;
|
||||
Point map(const Point& point) const;
|
||||
Rect map(const Rect& rect) const;
|
||||
|
||||
static Transform rotated(double angle);
|
||||
static Transform rotated(double angle, double cx, double cy);
|
||||
static Transform scaled(double sx, double sy);
|
||||
static Transform sheared(double shx, double shy);
|
||||
static Transform translated(double tx, double ty);
|
||||
|
||||
public:
|
||||
double m00{1};
|
||||
double m10{0};
|
||||
double m01{0};
|
||||
double m11{1};
|
||||
double m02{0};
|
||||
double m12{0};
|
||||
};
|
||||
|
||||
enum class PathCommand
|
||||
{
|
||||
MoveTo,
|
||||
LineTo,
|
||||
CubicTo,
|
||||
Close
|
||||
};
|
||||
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
Path() = default;
|
||||
|
||||
void moveTo(double x, double y);
|
||||
void lineTo(double x, double y);
|
||||
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3);
|
||||
void close();
|
||||
void reset();
|
||||
bool empty() const;
|
||||
|
||||
void quadTo(double cx, double cy, double x1, double y1, double x2, double y2);
|
||||
void arcTo(double cx, double cy, double rx, double ry, double xAxisRotation, bool largeArcFlag, bool sweepFlag, double x, double y);
|
||||
|
||||
void ellipse(double cx, double cy, double rx, double ry);
|
||||
void rect(double x, double y, double w, double h, double rx, double ry);
|
||||
|
||||
Rect box() const;
|
||||
|
||||
const std::vector<PathCommand>& commands() const { return m_commands; }
|
||||
const std::vector<Point>& points() const { return m_points; }
|
||||
|
||||
private:
|
||||
std::vector<PathCommand> m_commands;
|
||||
std::vector<Point> m_points;
|
||||
};
|
||||
|
||||
class PathIterator
|
||||
{
|
||||
public:
|
||||
PathIterator(const Path& path);
|
||||
|
||||
PathCommand currentSegment(std::array<Point, 3>& points) const;
|
||||
bool isDone() const;
|
||||
void next();
|
||||
|
||||
private:
|
||||
mutable Point m_startPoint;
|
||||
const std::vector<PathCommand>& m_commands;
|
||||
const Point* m_points{nullptr};
|
||||
unsigned int m_index{0};
|
||||
};
|
||||
|
||||
enum class LengthUnits
|
||||
{
|
||||
Unknown,
|
||||
Number,
|
||||
Px,
|
||||
Pt,
|
||||
Pc,
|
||||
In,
|
||||
Cm,
|
||||
Mm,
|
||||
Ex,
|
||||
Em,
|
||||
Percent
|
||||
};
|
||||
|
||||
enum LengthMode
|
||||
{
|
||||
Width,
|
||||
Height,
|
||||
Both
|
||||
};
|
||||
|
||||
class Element;
|
||||
|
||||
class Length
|
||||
{
|
||||
public:
|
||||
Length() = default;
|
||||
Length(double value);
|
||||
Length(double value, LengthUnits units);
|
||||
|
||||
double value(double max) const;
|
||||
double value(const Element* element, LengthMode mode) const;
|
||||
|
||||
bool isValid() const { return m_units != LengthUnits::Unknown; }
|
||||
bool isZero() const { return m_value == 0.0; }
|
||||
bool isRelative() const { return m_units == LengthUnits::Percent || m_units == LengthUnits::Em || m_units == LengthUnits::Ex; }
|
||||
|
||||
static const Length Unknown;
|
||||
static const Length Zero;
|
||||
static const Length One;
|
||||
static const Length ThreePercent;
|
||||
static const Length HundredPercent;
|
||||
static const Length FiftyPercent;
|
||||
static const Length OneTwentyPercent;
|
||||
static const Length MinusTenPercent;
|
||||
|
||||
private:
|
||||
double m_value{0};
|
||||
LengthUnits m_units{LengthUnits::Px};
|
||||
};
|
||||
|
||||
using LengthList = std::vector<Length>;
|
||||
|
||||
class LengthContext
|
||||
{
|
||||
public:
|
||||
LengthContext(const Element* element);
|
||||
LengthContext(const Element* element, Units units);
|
||||
|
||||
double valueForLength(const Length& length, LengthMode mode) const;
|
||||
|
||||
private:
|
||||
const Element* m_element{nullptr};
|
||||
Units m_units{Units::UserSpaceOnUse};
|
||||
};
|
||||
|
||||
enum class Align
|
||||
{
|
||||
None,
|
||||
xMinYMin,
|
||||
xMidYMin,
|
||||
xMaxYMin,
|
||||
xMinYMid,
|
||||
xMidYMid,
|
||||
xMaxYMid,
|
||||
xMinYMax,
|
||||
xMidYMax,
|
||||
xMaxYMax
|
||||
};
|
||||
|
||||
enum class MeetOrSlice
|
||||
{
|
||||
Meet,
|
||||
Slice
|
||||
};
|
||||
|
||||
class PreserveAspectRatio
|
||||
{
|
||||
public:
|
||||
PreserveAspectRatio() = default;
|
||||
PreserveAspectRatio(Align align, MeetOrSlice scale);
|
||||
|
||||
Transform getMatrix(double width, double height, const Rect& viewBox) const;
|
||||
Rect getClip(double width, double height, const Rect& viewBox) const;
|
||||
|
||||
Align align() const { return m_align; }
|
||||
MeetOrSlice scale() const { return m_scale; }
|
||||
|
||||
private:
|
||||
Align m_align{Align::xMidYMid};
|
||||
MeetOrSlice m_scale{MeetOrSlice::Meet};
|
||||
};
|
||||
|
||||
enum class MarkerOrient
|
||||
{
|
||||
Auto,
|
||||
Angle
|
||||
};
|
||||
|
||||
class Angle
|
||||
{
|
||||
public:
|
||||
Angle() = default;
|
||||
Angle(MarkerOrient type);
|
||||
Angle(double value, MarkerOrient type);
|
||||
|
||||
double value() const { return m_value; }
|
||||
MarkerOrient type() const { return m_type; }
|
||||
|
||||
private:
|
||||
double m_value{0};
|
||||
MarkerOrient m_type{MarkerOrient::Angle};
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // PROPERTY_H
|
29
source/stopelement.cpp
Normal file
29
source/stopelement.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "stopelement.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
StopElement::StopElement()
|
||||
: StyledElement(ElementId::Stop)
|
||||
{
|
||||
}
|
||||
|
||||
double StopElement::offset() const
|
||||
{
|
||||
auto& value = get(PropertyId::Offset);
|
||||
return Parser::parseNumberPercentage(value, 0.0);
|
||||
}
|
||||
|
||||
Color StopElement::stopColorWithOpacity() const
|
||||
{
|
||||
auto color = stop_color();
|
||||
color.a = stop_opacity();
|
||||
return color;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> StopElement::clone() const
|
||||
{
|
||||
return cloneElement<StopElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
21
source/stopelement.h
Normal file
21
source/stopelement.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef STOPELEMENT_H
|
||||
#define STOPELEMENT_H
|
||||
|
||||
#include "styledelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class StopElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
StopElement();
|
||||
|
||||
double offset() const;
|
||||
Color stopColorWithOpacity() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // STOPELEMENT_H
|
177
source/styledelement.cpp
Normal file
177
source/styledelement.cpp
Normal file
|
@ -0,0 +1,177 @@
|
|||
#include "styledelement.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
StyledElement::StyledElement(ElementId id)
|
||||
: Element(id)
|
||||
{
|
||||
}
|
||||
|
||||
Paint StyledElement::fill() const
|
||||
{
|
||||
auto& value = find(PropertyId::Fill);
|
||||
return Parser::parsePaint(value, this, Color::Black);
|
||||
}
|
||||
|
||||
Paint StyledElement::stroke() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke);
|
||||
return Parser::parsePaint(value, this, Color::Transparent);
|
||||
}
|
||||
|
||||
Color StyledElement::color() const
|
||||
{
|
||||
auto& value = find(PropertyId::Color);
|
||||
return Parser::parseColor(value, this, Color::Black);
|
||||
}
|
||||
|
||||
Color StyledElement::stop_color() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stop_Color);
|
||||
return Parser::parseColor(value, this, Color::Black);
|
||||
}
|
||||
|
||||
Color StyledElement::solid_color() const
|
||||
{
|
||||
auto& value = find(PropertyId::Solid_Color);
|
||||
return Parser::parseColor(value, this, Color::Black);
|
||||
}
|
||||
|
||||
double StyledElement::opacity() const
|
||||
{
|
||||
auto& value = get(PropertyId::Opacity);
|
||||
return Parser::parseNumberPercentage(value, 1.0);
|
||||
}
|
||||
|
||||
double StyledElement::fill_opacity() const
|
||||
{
|
||||
auto& value = find(PropertyId::Fill_Opacity);
|
||||
return Parser::parseNumberPercentage(value, 1.0);
|
||||
}
|
||||
|
||||
double StyledElement::stroke_opacity() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Opacity);
|
||||
return Parser::parseNumberPercentage(value, 1.0);
|
||||
}
|
||||
|
||||
double StyledElement::stop_opacity() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stop_Opacity);
|
||||
return Parser::parseNumberPercentage(value, 1.0);
|
||||
}
|
||||
|
||||
double StyledElement::solid_opacity() const
|
||||
{
|
||||
auto& value = find(PropertyId::Solid_Opacity);
|
||||
return Parser::parseNumberPercentage(value, 1.0);
|
||||
}
|
||||
|
||||
double StyledElement::stroke_miterlimit() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Miterlimit);
|
||||
return Parser::parseNumber(value, 4.0);
|
||||
}
|
||||
|
||||
Length StyledElement::stroke_width() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::One);
|
||||
}
|
||||
|
||||
Length StyledElement::stroke_dashoffset() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Dashoffset);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
LengthList StyledElement::stroke_dasharray() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Dasharray);
|
||||
return Parser::parseLengthList(value, ForbidNegativeLengths);
|
||||
}
|
||||
|
||||
WindRule StyledElement::fill_rule() const
|
||||
{
|
||||
auto& value = find(PropertyId::Fill_Rule);
|
||||
return Parser::parseWindRule(value);
|
||||
}
|
||||
|
||||
WindRule StyledElement::clip_rule() const
|
||||
{
|
||||
auto& value = find(PropertyId::Clip_Rule);
|
||||
return Parser::parseWindRule(value);
|
||||
}
|
||||
|
||||
LineCap StyledElement::stroke_linecap() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Linecap);
|
||||
return Parser::parseLineCap(value);
|
||||
}
|
||||
|
||||
LineJoin StyledElement::stroke_linejoin() const
|
||||
{
|
||||
auto& value = find(PropertyId::Stroke_Linejoin);
|
||||
return Parser::parseLineJoin(value);
|
||||
}
|
||||
|
||||
Display StyledElement::display() const
|
||||
{
|
||||
auto& value = get(PropertyId::Display);
|
||||
return Parser::parseDisplay(value);
|
||||
}
|
||||
|
||||
Visibility StyledElement::visibility() const
|
||||
{
|
||||
auto& value = find(PropertyId::Visibility);
|
||||
return Parser::parseVisibility(value);
|
||||
}
|
||||
|
||||
Overflow StyledElement::overflow() const
|
||||
{
|
||||
auto& value = get(PropertyId::Overflow);
|
||||
return Parser::parseOverflow(value, parent == nullptr ? Overflow::Visible : Overflow::Hidden);
|
||||
}
|
||||
|
||||
std::string StyledElement::clip_path() const
|
||||
{
|
||||
auto& value = get(PropertyId::Clip_Path);
|
||||
return Parser::parseUrl(value);
|
||||
}
|
||||
|
||||
std::string StyledElement::mask() const
|
||||
{
|
||||
auto& value = get(PropertyId::Mask);
|
||||
return Parser::parseUrl(value);
|
||||
}
|
||||
|
||||
std::string StyledElement::marker_start() const
|
||||
{
|
||||
auto& value = find(PropertyId::Marker_Start);
|
||||
return Parser::parseUrl(value);
|
||||
}
|
||||
|
||||
std::string StyledElement::marker_mid() const
|
||||
{
|
||||
auto& value = find(PropertyId::Marker_Mid);
|
||||
return Parser::parseUrl(value);
|
||||
}
|
||||
|
||||
std::string StyledElement::marker_end() const
|
||||
{
|
||||
auto& value = find(PropertyId::Marker_End);
|
||||
return Parser::parseUrl(value);
|
||||
}
|
||||
|
||||
bool StyledElement::isDisplayNone() const
|
||||
{
|
||||
return display() == Display::None;
|
||||
}
|
||||
|
||||
bool StyledElement::isOverflowHidden() const
|
||||
{
|
||||
return overflow() == Overflow::Hidden;
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
53
source/styledelement.h
Normal file
53
source/styledelement.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef STYLEDELEMENT_H
|
||||
#define STYLEDELEMENT_H
|
||||
|
||||
#include "element.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class StyledElement : public Element
|
||||
{
|
||||
public:
|
||||
StyledElement(ElementId id);
|
||||
|
||||
Paint fill() const;
|
||||
Paint stroke() const;
|
||||
|
||||
Color color() const;
|
||||
Color stop_color() const;
|
||||
Color solid_color() const;
|
||||
|
||||
double opacity() const;
|
||||
double fill_opacity() const;
|
||||
double stroke_opacity() const;
|
||||
double stop_opacity() const;
|
||||
double solid_opacity() const;
|
||||
double stroke_miterlimit() const;
|
||||
|
||||
Length stroke_width() const;
|
||||
Length stroke_dashoffset() const;
|
||||
LengthList stroke_dasharray() const;
|
||||
|
||||
WindRule fill_rule() const;
|
||||
WindRule clip_rule() const;
|
||||
|
||||
LineCap stroke_linecap() const;
|
||||
LineJoin stroke_linejoin() const;
|
||||
|
||||
Display display() const;
|
||||
Visibility visibility() const;
|
||||
Overflow overflow() const;
|
||||
|
||||
std::string clip_path() const;
|
||||
std::string mask() const;
|
||||
std::string marker_start() const;
|
||||
std::string marker_mid() const;
|
||||
std::string marker_end() const;
|
||||
|
||||
bool isDisplayNone() const;
|
||||
bool isOverflowHidden() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // STYLEDELEMENT_H
|
15
source/styleelement.cpp
Normal file
15
source/styleelement.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "styleelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
StyleElement::StyleElement()
|
||||
: Element(ElementId::Style)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> StyleElement::clone() const
|
||||
{
|
||||
return cloneElement<StyleElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
18
source/styleelement.h
Normal file
18
source/styleelement.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef STYLEELEMENT_H
|
||||
#define STYLEELEMENT_H
|
||||
|
||||
#include "element.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class StyleElement : public Element
|
||||
{
|
||||
public:
|
||||
StyleElement();
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // STYLEELEMENT_H
|
121
source/svgelement.cpp
Normal file
121
source/svgelement.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
#include "svgelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
SVGElement::SVGElement()
|
||||
: GraphicsElement(ElementId::Svg)
|
||||
{
|
||||
}
|
||||
|
||||
Length SVGElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length SVGElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length SVGElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Length SVGElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Rect SVGElement::viewBox() const
|
||||
{
|
||||
auto& value = get(PropertyId::ViewBox);
|
||||
return Parser::parseViewBox(value);
|
||||
}
|
||||
|
||||
PreserveAspectRatio SVGElement::preserveAspectRatio() const
|
||||
{
|
||||
auto& value = get(PropertyId::PreserveAspectRatio);
|
||||
return Parser::parsePreserveAspectRatio(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutSymbol> SVGElement::layoutDocument(const ParseDocument* document) const
|
||||
{
|
||||
if(isDisplayNone())
|
||||
return nullptr;
|
||||
|
||||
auto w = this->width();
|
||||
auto h = this->height();
|
||||
if(w.isZero() || h.isZero())
|
||||
return nullptr;
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _x = lengthContext.valueForLength(x(), LengthMode::Width);
|
||||
auto _y = lengthContext.valueForLength(y(), LengthMode::Height);
|
||||
auto _w = lengthContext.valueForLength(w, LengthMode::Width);
|
||||
auto _h = lengthContext.valueForLength(h, LengthMode::Height);
|
||||
|
||||
auto viewBox = this->viewBox();
|
||||
auto preserveAspectRatio = this->preserveAspectRatio();
|
||||
auto viewTranslation = Transform::translated(_x, _y);
|
||||
auto viewTransform = preserveAspectRatio.getMatrix(_w, _h, viewBox);
|
||||
|
||||
auto root = std::make_unique<LayoutSymbol>();
|
||||
root->width = _w;
|
||||
root->height = _h;
|
||||
root->transform = (viewTransform * viewTranslation) * transform();
|
||||
root->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid;
|
||||
root->opacity = opacity();
|
||||
|
||||
LayoutContext context(document, root.get());
|
||||
root->masker = context.getMasker(mask());
|
||||
root->clipper = context.getClipper(clip_path());
|
||||
layoutChildren(&context, root.get());
|
||||
return root;
|
||||
}
|
||||
|
||||
void SVGElement::layout(LayoutContext* context, LayoutContainer* current) const
|
||||
{
|
||||
if(isDisplayNone())
|
||||
return;
|
||||
|
||||
auto w = this->width();
|
||||
auto h = this->height();
|
||||
if(w.isZero() || h.isZero())
|
||||
return;
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _x = lengthContext.valueForLength(x(), LengthMode::Width);
|
||||
auto _y = lengthContext.valueForLength(y(), LengthMode::Height);
|
||||
auto _w = lengthContext.valueForLength(w, LengthMode::Width);
|
||||
auto _h = lengthContext.valueForLength(h, LengthMode::Height);
|
||||
|
||||
auto viewBox = this->viewBox();
|
||||
auto preserveAspectRatio = this->preserveAspectRatio();
|
||||
auto viewTranslation = Transform::translated(_x, _y);
|
||||
auto viewTransform = preserveAspectRatio.getMatrix(_w, _h, viewBox);
|
||||
|
||||
auto symbol = std::make_unique<LayoutSymbol>();
|
||||
symbol->width = _w;
|
||||
symbol->height = _h;
|
||||
symbol->transform = (viewTransform * viewTranslation) * transform();
|
||||
symbol->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid;
|
||||
symbol->opacity = opacity();
|
||||
symbol->masker = context->getMasker(mask());
|
||||
symbol->clipper = context->getClipper(clip_path());
|
||||
layoutChildren(context, symbol.get());
|
||||
current->addChildIfNotEmpty(std::move(symbol));
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> SVGElement::clone() const
|
||||
{
|
||||
return cloneElement<SVGElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
31
source/svgelement.h
Normal file
31
source/svgelement.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef SVGELEMENT_H
|
||||
#define SVGELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class ParseDocument;
|
||||
class LayoutSymbol;
|
||||
|
||||
class SVGElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
SVGElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
|
||||
Rect viewBox() const;
|
||||
PreserveAspectRatio preserveAspectRatio() const;
|
||||
std::unique_ptr<LayoutSymbol> layoutDocument(const ParseDocument* document) const;
|
||||
|
||||
void layout(LayoutContext* context, LayoutContainer* current) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // SVGELEMENT_H
|
52
source/symbolelement.cpp
Normal file
52
source/symbolelement.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include "symbolelement.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
SymbolElement::SymbolElement()
|
||||
: StyledElement(ElementId::Symbol)
|
||||
{
|
||||
}
|
||||
|
||||
Length SymbolElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length SymbolElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length SymbolElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Length SymbolElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Rect SymbolElement::viewBox() const
|
||||
{
|
||||
auto& value = get(PropertyId::ViewBox);
|
||||
return Parser::parseViewBox(value);
|
||||
}
|
||||
|
||||
PreserveAspectRatio SymbolElement::preserveAspectRatio() const
|
||||
{
|
||||
auto& value = get(PropertyId::PreserveAspectRatio);
|
||||
return Parser::parsePreserveAspectRatio(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> SymbolElement::clone() const
|
||||
{
|
||||
return cloneElement<SymbolElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
25
source/symbolelement.h
Normal file
25
source/symbolelement.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef SYMBOLELEMENT_H
|
||||
#define SYMBOLELEMENT_H
|
||||
|
||||
#include "styledelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class SymbolElement : public StyledElement
|
||||
{
|
||||
public:
|
||||
SymbolElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
Rect viewBox() const;
|
||||
PreserveAspectRatio preserveAspectRatio() const;
|
||||
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // SYMBOLELEMENT_H
|
99
source/useelement.cpp
Normal file
99
source/useelement.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include "useelement.h"
|
||||
#include "parser.h"
|
||||
#include "layoutcontext.h"
|
||||
|
||||
#include "gelement.h"
|
||||
#include "svgelement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
UseElement::UseElement()
|
||||
: GraphicsElement(ElementId::Use)
|
||||
{
|
||||
}
|
||||
|
||||
Length UseElement::x() const
|
||||
{
|
||||
auto& value = get(PropertyId::X);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length UseElement::y() const
|
||||
{
|
||||
auto& value = get(PropertyId::Y);
|
||||
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
|
||||
}
|
||||
|
||||
Length UseElement::width() const
|
||||
{
|
||||
auto& value = get(PropertyId::Width);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
Length UseElement::height() const
|
||||
{
|
||||
auto& value = get(PropertyId::Height);
|
||||
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
|
||||
}
|
||||
|
||||
std::string UseElement::href() const
|
||||
{
|
||||
auto& value = get(PropertyId::Href);
|
||||
return Parser::parseHref(value);
|
||||
}
|
||||
|
||||
void UseElement::transferWidthAndHeight(Element* element) const
|
||||
{
|
||||
auto& width = get(PropertyId::Width);
|
||||
auto& height = get(PropertyId::Height);
|
||||
|
||||
element->set(PropertyId::Width, width, 0x0);
|
||||
element->set(PropertyId::Height, height, 0x0);
|
||||
}
|
||||
|
||||
void UseElement::layout(LayoutContext* context, LayoutContainer* current) const
|
||||
{
|
||||
if(isDisplayNone())
|
||||
return;
|
||||
|
||||
auto ref = context->getElementById(href());
|
||||
if(ref == nullptr || context->hasReference(ref) || (current->id == LayoutId::ClipPath && !ref->isGeometry()))
|
||||
return;
|
||||
|
||||
LayoutBreaker layoutBreaker(context, ref);
|
||||
auto group = std::make_unique<GElement>();
|
||||
group->parent = parent;
|
||||
group->properties = properties;
|
||||
|
||||
LengthContext lengthContext(this);
|
||||
auto _x = lengthContext.valueForLength(x(), LengthMode::Width);
|
||||
auto _y = lengthContext.valueForLength(y(), LengthMode::Height);
|
||||
|
||||
auto transform = get(PropertyId::Transform);
|
||||
transform += "translate(";
|
||||
transform += std::to_string(_x);
|
||||
transform += ' ';
|
||||
transform += std::to_string(_y);
|
||||
transform += ')';
|
||||
group->set(PropertyId::Transform, transform, 0x10);
|
||||
|
||||
if(ref->id == ElementId::Svg || ref->id == ElementId::Symbol)
|
||||
{
|
||||
auto element = ref->cloneElement<SVGElement>();
|
||||
transferWidthAndHeight(element.get());
|
||||
group->addChild(std::move(element));
|
||||
}
|
||||
else
|
||||
{
|
||||
group->addChild(ref->clone());
|
||||
}
|
||||
|
||||
group->layout(context, current);
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> UseElement::clone() const
|
||||
{
|
||||
return cloneElement<UseElement>();
|
||||
}
|
||||
|
||||
} // namespace lunasvg
|
26
source/useelement.h
Normal file
26
source/useelement.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef USEELEMENT_H
|
||||
#define USEELEMENT_H
|
||||
|
||||
#include "graphicselement.h"
|
||||
|
||||
namespace lunasvg {
|
||||
|
||||
class UseElement : public GraphicsElement
|
||||
{
|
||||
public:
|
||||
UseElement();
|
||||
|
||||
Length x() const;
|
||||
Length y() const;
|
||||
Length width() const;
|
||||
Length height() const;
|
||||
std::string href() const;
|
||||
void transferWidthAndHeight(Element* element) const;
|
||||
|
||||
void layout(LayoutContext* context, LayoutContainer* current) const;
|
||||
std::unique_ptr<Node> clone() const;
|
||||
};
|
||||
|
||||
} // namespace lunasvg
|
||||
|
||||
#endif // USEELEMENT_H
|
Loading…
Reference in a new issue