improve svg

This commit is contained in:
lixianjing 2024-03-06 17:33:52 +08:00
parent 144490cee2
commit 66f34a4640
5 changed files with 407 additions and 7 deletions

View File

@ -99,7 +99,7 @@ static ret_t arc_info_init(arc_info_t* info, pointf_t from, pointf_t to, pointf_
return RET_OK;
}
bool_t arc_info_next(arc_info_t* info, pointf_t* cp1, pointf_t* cp2, pointf_t* to) {
static bool_t arc_info_next(arc_info_t* info, pointf_t* cp1, pointf_t* cp2, pointf_t* to) {
if (info->seg_index == info->num_segs) {
return FALSE;
}

View File

@ -1,5 +1,8 @@
# 最新动态
2024/03/06
* 完善svg支持awtk-widget-shape(感谢陈聪提供补丁)
2024/03/04
* 增加zip_file。
* 修改edit控件支持只读下数据可以拷贝到剪切板上面(感谢智明提供补丁)

View File

@ -20,9 +20,294 @@
*/
#include "tkc/utils.h"
#include "tkc/mem.h"
#include "svg/svg_path.h"
#include "svg/svg_path_parser.h"
#include "svgtiny/include/svgtiny_arc.h"
/*
* svg改为用svgtiny解析svg/svg_path_parser.c|.h中的代码
*/
typedef struct _svg_path_parser_t {
char* path;
void* ctx;
tk_visit_t on_path;
} svg_path_parser_t;
static ret_t svg_path_parser_parse(svg_path_parser_t* parser) {
char* s = parser->path;
float first_x = 0, first_y = 0;
float last_x = 0, last_y = 0;
float last_cubic_x = 0, last_cubic_y = 0;
float last_quad_x = 0, last_quad_y = 0;
/* parse d and build path */
s = parser->path;
while (*s) {
if (*s == ',') {
*s = ' ';
}
s++;
}
s = parser->path;
while (*s) {
int n;
char command[2];
float x, y, x1, y1, x2, y2, rx, ry, rotation, large_arc, sweep;
/* moveto (M, m) (2 arguments) */
if (sscanf(s, " %1[Mm] %f %f %n", command, &x, &y, &n) == 3) {
svg_path_move_t path;
do {
if (*command == 'm') {
x += last_x;
y += last_y;
}
first_x = last_cubic_x = last_quad_x = last_x = x;
first_y = last_cubic_y = last_quad_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_move_init(&path, x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %n", &x, &y, &n) == 2);
/* lineto (L, l) (2 arguments) */
} else if (sscanf(s, " %1[Ll] %f %f %n", command, &x, &y, &n) == 3) {
svg_path_line_t path;
do {
if (*command == 'l') {
x += last_x;
y += last_y;
}
last_cubic_x = last_quad_x = last_x = x;
last_cubic_y = last_quad_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_line_init(&path, x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %n", &x, &y, &n) == 2);
/* closepath (Z, z) (no arguments) */
} else if (sscanf(s, " %1[Zz] %n", command, &n) == 1) {
svg_path_t path;
last_x = first_x;
last_y = first_y;
s += n;
memset(&path, 0, sizeof(path));
path.type = SVG_PATH_Z;
parser->on_path(parser->ctx, (svg_path_t*)&path);
/* horizontal lineto (H, h) (1 argument) */
} else if (sscanf(s, " %1[Hh] %f %n", command, &x, &n) == 2) {
svg_path_line_t path;
do {
if (*command == 'h') {
x += last_x;
}
last_cubic_x = last_quad_x = last_x = x;
last_cubic_y = last_quad_y = last_y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_line_init(&path, x, last_y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %n", &x, &n) == 1);
/* vertical lineto (V, v) (1 argument) */
} else if (sscanf(s, " %1[Vv] %f %n", command, &y, &n) == 2) {
svg_path_line_t path;
do {
if (*command == 'v') {
y += last_y;
}
last_cubic_x = last_quad_x = last_x;
last_cubic_y = last_quad_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_line_init(&path, last_x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %n", &y, &n) == 1);
/* curveto (C, c) (6 arguments) */
} else if (sscanf(s, " %1[Cc] %f %f %f %f %f %f %n", command, &x1, &y1, &x2, &y2, &x, &y, &n) ==
7) {
svg_path_curve_to_t path;
do {
if (*command == 'c') {
x1 += last_x;
y1 += last_y;
x2 += last_x;
y2 += last_y;
x += last_x;
y += last_y;
}
last_cubic_x = x2;
last_cubic_y = y2;
last_quad_x = last_x = x;
last_quad_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_curve_to_init(&path, x1, y1, x2, y2, x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %f %f %f %f %n", &x1, &y1, &x2, &y2, &x, &y, &n) == 6);
/* shorthand/smooth curveto (S, s) (4 arguments) */
} else if (sscanf(s, " %1[Ss] %f %f %f %f %n", command, &x2, &y2, &x, &y, &n) == 5) {
svg_path_curve_to_t path;
do {
x1 = last_x + (last_x - last_cubic_x);
y1 = last_y + (last_y - last_cubic_y);
if (*command == 's') {
x2 += last_x;
y2 += last_y;
x += last_x;
y += last_y;
}
last_cubic_x = x2;
last_cubic_y = y2;
last_quad_x = last_x = x;
last_quad_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_curve_to_init(&path, x1, y1, x2, y2, x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %f %f %n", &x2, &y2, &x, &y, &n) == 4);
/* quadratic Bezier curveto (Q, q) (4 arguments) */
} else if (sscanf(s, " %1[Qq] %f %f %f %f %n", command, &x1, &y1, &x, &y, &n) == 5) {
svg_path_curve_to_t path;
float p[4];
do {
if (*command == 'q') {
x1 += last_x;
y1 += last_y;
x += last_x;
y += last_y;
}
last_quad_x = x1;
last_quad_y = y1;
p[0] = 1. / 3 * last_x + 2. / 3 * x1;
p[1] = 1. / 3 * last_y + 2. / 3 * y1;
p[2] = 2. / 3 * x1 + 1. / 3 * x;
p[3] = 2. / 3 * y1 + 1. / 3 * y;
last_cubic_x = last_x = x;
last_cubic_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_curve_to_init(&path, p[0], p[1], p[2], p[3], x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %f %f %n", &x1, &y1, &x, &y, &n) == 4);
/* shorthand/smooth quadratic Bezier curveto (T, t)
(2 arguments) */
} else if (sscanf(s, " %1[Tt] %f %f %n", command, &x, &y, &n) == 3) {
svg_path_curve_to_t path;
float p[4];
do {
x1 = last_x + (last_x - last_quad_x);
y1 = last_y + (last_y - last_quad_y);
last_quad_x = x1;
last_quad_y = y1;
if (*command == 't') {
x += last_x;
y += last_y;
}
p[0] = 1. / 3 * last_x + 2. / 3 * x1;
p[1] = 1. / 3 * last_y + 2. / 3 * y1;
p[2] = 2. / 3 * x1 + 1. / 3 * x;
p[3] = 2. / 3 * y1 + 1. / 3 * y;
last_cubic_x = last_x = x;
last_cubic_y = last_y = y;
s += n;
memset(&path, 0, sizeof(path));
svg_path_curve_to_init(&path, p[0], p[1], p[2], p[3], x, y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
} while (sscanf(s, "%f %f %n", &x, &y, &n) == 2);
/* elliptical arc (A, a) (7 arguments) */
} else if (sscanf(s, " %1[Aa] %f %f %f %f %f %f %f %n", command, &rx, &ry, &rotation,
&large_arc, &sweep, &x, &y, &n) == 8) {
svg_path_curve_to_t path;
do {
arc_info_t info;
pointf_t cp1 = {0, 0};
pointf_t cp2 = {0, 0};
pointf_t end = {0, 0};
pointf_t r = {rx, ry};
pointf_t from = {last_x, last_y};
pointf_t to = {x, y};
if (*command == 'a') {
to.x += last_x;
to.y += last_y;
}
arc_info_init(&info, from, to, r, rotation, large_arc, sweep);
while (arc_info_next(&info, &cp1, &cp2, &end)) {
memset(&path, 0, sizeof(path));
svg_path_curve_to_init(&path, cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
parser->on_path(parser->ctx, (svg_path_t*)&path);
}
last_x = to.x;
last_y = to.y;
s += n;
} while (sscanf(s, "%f %f %f %f %f %f %f %n", &rx, &ry, &rotation, &large_arc, &sweep, &x, &y,
&n) == 7);
} else {
log_error("parse failed at \"%s\"\n", s);
return RET_FAIL;
}
}
return RET_OK;
}
static ret_t svg_path_parser_init(svg_path_parser_t* parser, const char* path, void* ctx,
tk_visit_t on_path) {
memset(parser, 0x00, sizeof(*parser));
parser->ctx = ctx;
parser->path = (char*)path;
parser->on_path = on_path;
return RET_OK;
}
ret_t svg_path_parse(const char* path, void* ctx, tk_visit_t on_path) {
ret_t ret;
char* p = NULL;
svg_path_parser_t parser;
return_value_if_fail(path != NULL && on_path != NULL, RET_BAD_PARAMS);
p = tk_strdup(path);
return_value_if_fail(p != NULL, RET_OOM);
svg_path_parser_init(&parser, p, ctx, on_path);
ret = svg_path_parser_parse(&parser);
TKMEM_FREE(p);
return ret;
}

View File

@ -24,8 +24,26 @@
#include "tkc/types_def.h"
/*
* svg改为用svgtiny解析svg/svg_path_parser.c|.h中的代码
*/
BEGIN_C_DECLS
/**
* @class svg_path_t
* @annotation ["fake"]
*/
/**
* @method svg_path_parse
*
*
*
* @param {const char*} path
* @param {void*} ctx
* @param {tk_visit_t} on_path
*
* @return {ret_t} RET_OK表示成功
*/
ret_t svg_path_parse(const char* path, void* ctx, tk_visit_t on_path);
END_C_DECLS
#endif /*TK_SVG_PATH_BUILDER_H*/

View File

@ -1,5 +1,6 @@
#include "svg/bsvg.h"
#include "svg/bsvg_builder.h"
#include "svg/svg_path_parser.h"
#include "tkc/utils.h"
#include "gtest/gtest.h"
#include "svg/bsvg_to_svg.h"
@ -84,6 +85,13 @@ static ret_t on_path(void* ctx, const void* data) {
return RET_OK;
}
static ret_t on_path_parse(void* ctx, const void* data) {
cmp_path(((const svg_path_t**)ctx)[s_on_path_count], (const svg_path_t*)data);
s_on_path_count++;
return RET_OK;
}
static ret_t on_shape_null(void* ctx, const void* data) {
(void)ctx;
(void)data;
@ -135,3 +143,89 @@ TEST(SvgPath, curve) {
svg_path_t* p = svg_path_curve_to_init(&path, 1, 2, 3, 4, 5, 6);
test_one_path(p, " C1.0 2.0 3.0 4.0 5.0 6.0");
}
static void test_one_path_parse(const char* str, svg_path_t** ctx, uint32_t c) {
s_on_path_count = 0;
svg_path_parse(str, ctx, on_path_parse);
ASSERT_EQ(s_on_path_count, c);
}
TEST(SVGPathParser, move) {
svg_path_t* ctx[2];
svg_path_move_t path[2];
memset(&path, 0, sizeof(path));
ctx[0] = svg_path_move_init(&path[0], 409.6, 281.6);
ctx[1] = svg_path_move_init(&path[1], 409.6, 281.6);
test_one_path_parse("M409.6 281.6", (svg_path_t**)ctx, 1);
test_one_path_parse("M409.6 281.6 409.6 281.6", (svg_path_t**)ctx, 2);
ctx[0] = svg_path_move_init(&path[0], 409.6, 281.6);
ctx[1] = svg_path_move_init(&path[1], 409.6 * 2, 281.6 * 2);
test_one_path_parse("m409.6 281.6", (svg_path_t**)ctx, 1);
test_one_path_parse("m409.6 281.6 409.6 281.6", (svg_path_t**)ctx, 2);
}
TEST(SVGPathParser, line) {
svg_path_t* ctx[2];
svg_path_line_t path[2] = {0};
memset(&path, 0, sizeof(path));
ctx[0] = svg_path_line_init(&path[0], 128, -128.5);
ctx[1] = svg_path_line_init(&path[1], 128, -128.5);
test_one_path_parse("L128-128.5", (svg_path_t**)ctx, 1);
test_one_path_parse("L128-128.5 128-128.5", (svg_path_t**)ctx, 2);
ctx[0] = svg_path_line_init(&path[0], 128, -128.5);
ctx[1] = svg_path_line_init(&path[1], 128 * 2, -128.5 * 2);
test_one_path_parse("l128-128.5", (svg_path_t**)ctx, 1);
test_one_path_parse("l128-128.5 128-128.5", (svg_path_t**)ctx, 2);
}
TEST(SVGPathParser, hline) {
svg_path_t* ctx[2];
svg_path_line_t path[2] = {0};
memset(&path, 0, sizeof(path));
ctx[0] = svg_path_line_init(&path[0], 179.2, 0);
ctx[1] = svg_path_line_init(&path[1], 179.2, 0);
test_one_path_parse("H179.2", (svg_path_t**)ctx, 1);
test_one_path_parse("H179.2 179.2", (svg_path_t**)ctx, 2);
ctx[0] = svg_path_line_init(&path[0], 179.2, 0);
ctx[1] = svg_path_line_init(&path[1], 179.2 * 2, 0);
test_one_path_parse("h179.2", (svg_path_t**)ctx, 1);
test_one_path_parse("h179.2 179.2", (svg_path_t**)ctx, 2);
}
TEST(SVGPathParser, vline) {
svg_path_t* ctx[2];
svg_path_line_t path[2];
memset(&path, 0, sizeof(path));
ctx[0] = svg_path_line_init(&path[0], 0, -179.2);
ctx[1] = svg_path_line_init(&path[1], 0, -179.2);
test_one_path_parse("V-179.2", (svg_path_t**)ctx, 1);
test_one_path_parse("V-179.2-179.2", (svg_path_t**)ctx, 2);
ctx[0] = svg_path_line_init(&path[0], 0, -179.2);
ctx[1] = svg_path_line_init(&path[1], 0, -179.2 * 2);
test_one_path_parse("v-179.2", (svg_path_t**)ctx, 1);
test_one_path_parse("v-179.2-179.2", (svg_path_t**)ctx, 2);
}
TEST(SVGPathParser, curve) {
svg_path_t* ctx[2];
svg_path_curve_to_t path[2];
memset(&path, 0, sizeof(path));
ctx[0] = svg_path_curve_to_init(&path[0], 1, 2, 3, 4, 5, 6);
ctx[1] = svg_path_curve_to_init(&path[1], 1, 2, 3, 4, 5, 6);
test_one_path_parse("C1 2 3 4 5 6", (svg_path_t**)ctx, 1);
test_one_path_parse("C1 2 3 4 5 6 1 2 3 4 5 6 ", (svg_path_t**)ctx, 2);
ctx[0] = svg_path_curve_to_init(&path[0], 1, 2, 3, 4, 5, 6);
ctx[1] = svg_path_curve_to_init(&path[1], 1 + 5, 2 + 6, 3 + 5, 4 + 6, 5 * 2, 6 * 2);
test_one_path_parse("c1 2 3 4 5 6", (svg_path_t**)ctx, 1);
test_one_path_parse("c1 2 3 4 5 6 1 2 3 4 5 6 ", (svg_path_t**)ctx, 2);
}