Squashed 'external/lunasvg/' content from commit 7417baa0a

git-subtree-dir: external/lunasvg
git-subtree-split: 7417baa0aff477f361e44e2aa793fdb0c7aae352
This commit is contained in:
Leon Styhre 2022-10-03 18:25:42 +02:00
commit 32546b5874
70 changed files with 18135 additions and 0 deletions

4
.github/FUNDING.yml vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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, &params);
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, &params);
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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

607
3rdparty/software/sw_ft_raster.h vendored Normal file
View 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

File diff suppressed because it is too large Load diff

319
3rdparty/software/sw_ft_stroker.h vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
target_include_directories(lunasvg
PUBLIC
"${CMAKE_CURRENT_LIST_DIR}"
)

270
include/lunasvg.h Normal file
View 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

BIN
luna.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

28
source/CMakeLists.txt Executable file
View 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
View 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
View 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

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

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

File diff suppressed because it is too large Load diff

195
source/parser.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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