WIP: lunasvg implementation, things stopped working
This commit is contained in:
BIN
.cache/clangd/index/audio.h.EFD04539128CB066.idx
Normal file
BIN
.cache/clangd/index/audio.h.EFD04539128CB066.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/audio_coreaudio.cpp.C30D9855B63A408E.idx
Normal file
BIN
.cache/clangd/index/audio_coreaudio.cpp.C30D9855B63A408E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_arena.cpp.164995F241F3F880.idx
Normal file
BIN
.cache/clangd/index/base_arena.cpp.164995F241F3F880.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_arena.h.CBCA6374A6808DE1.idx
Normal file
BIN
.cache/clangd/index/base_arena.h.CBCA6374A6808DE1.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_core.h.7D5D690A43734B5E.idx
Normal file
BIN
.cache/clangd/index/base_core.h.7D5D690A43734B5E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_inc.cpp.DA11457DD4910E5E.idx
Normal file
BIN
.cache/clangd/index/base_inc.cpp.DA11457DD4910E5E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_inc.h.61DD50EE532D598A.idx
Normal file
BIN
.cache/clangd/index/base_inc.h.61DD50EE532D598A.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_math.h.22213D7142116A00.idx
Normal file
BIN
.cache/clangd/index/base_math.h.22213D7142116A00.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_strings.cpp.04AD22E410EE497F.idx
Normal file
BIN
.cache/clangd/index/base_strings.cpp.04AD22E410EE497F.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/base_strings.h.1662805A1C0C0BA6.idx
Normal file
BIN
.cache/clangd/index/base_strings.h.1662805A1C0C0BA6.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/clay.h.FBF47F0B8BF4850E.idx
Normal file
BIN
.cache/clangd/index/clay.h.FBF47F0B8BF4850E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/lunasvg.h.F0A538DDD7F84C21.idx
Normal file
BIN
.cache/clangd/index/lunasvg.h.F0A538DDD7F84C21.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/main.cpp.A6515BFFE4CD0D5B.idx
Normal file
BIN
.cache/clangd/index/main.cpp.A6515BFFE4CD0D5B.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/menus.cpp.9A9C873D20897FB5.idx
Normal file
BIN
.cache/clangd/index/menus.cpp.9A9C873D20897FB5.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/midi.h.FE65C080BD6B4278.idx
Normal file
BIN
.cache/clangd/index/midi.h.FE65C080BD6B4278.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/midi_coremidi.cpp.1405C3B7583C7F77.idx
Normal file
BIN
.cache/clangd/index/midi_coremidi.cpp.1405C3B7583C7F77.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/platform.h.0F95EF7BB057FB07.idx
Normal file
BIN
.cache/clangd/index/platform.h.0F95EF7BB057FB07.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/platform_macos.mm.592FED4BC31D10F6.idx
Normal file
BIN
.cache/clangd/index/platform_macos.mm.592FED4BC31D10F6.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/renderer.h.72DA7B7C6BAF6E31.idx
Normal file
BIN
.cache/clangd/index/renderer.h.72DA7B7C6BAF6E31.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/renderer_metal.mm.4A5862252690E2BA.idx
Normal file
BIN
.cache/clangd/index/renderer_metal.mm.4A5862252690E2BA.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_core.cpp.700D288EBBE4E64E.idx
Normal file
BIN
.cache/clangd/index/ui_core.cpp.700D288EBBE4E64E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_core.h.08A48D201816FC6A.idx
Normal file
BIN
.cache/clangd/index/ui_core.h.08A48D201816FC6A.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_icons.cpp.E120C1FAB06B49CD.idx
Normal file
BIN
.cache/clangd/index/ui_icons.cpp.E120C1FAB06B49CD.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_icons.h.C42E196812AFE48E.idx
Normal file
BIN
.cache/clangd/index/ui_icons.h.C42E196812AFE48E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_widgets.cpp.95BDB5FDC10F055B.idx
Normal file
BIN
.cache/clangd/index/ui_widgets.cpp.95BDB5FDC10F055B.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_widgets.h.FA28EA93ABE86503.idx
Normal file
BIN
.cache/clangd/index/ui_widgets.h.FA28EA93ABE86503.idx
Normal file
Binary file not shown.
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"lldb-dap.executable-path": "/opt/homebrew/opt/llvm/bin/lldb-dap"
|
||||||
|
}
|
||||||
254
build.c
254
build.c
@@ -6,6 +6,36 @@
|
|||||||
#define NOB_IMPLEMENTATION
|
#define NOB_IMPLEMENTATION
|
||||||
#include "vendor/nob/nob.h"
|
#include "vendor/nob/nob.h"
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// lunasvg/plutovg static library build helpers
|
||||||
|
|
||||||
|
static const char *plutovg_sources[] = {
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-blend.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-canvas.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-font.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-ft-math.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-ft-raster.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-matrix.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-paint.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-path.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-rasterize.c",
|
||||||
|
"vendor/lunasvg/plutovg/source/plutovg-surface.c",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *lunasvg_sources[] = {
|
||||||
|
"vendor/lunasvg/source/graphics.cpp",
|
||||||
|
"vendor/lunasvg/source/lunasvg.cpp",
|
||||||
|
"vendor/lunasvg/source/svgelement.cpp",
|
||||||
|
"vendor/lunasvg/source/svggeometryelement.cpp",
|
||||||
|
"vendor/lunasvg/source/svglayoutstate.cpp",
|
||||||
|
"vendor/lunasvg/source/svgpaintelement.cpp",
|
||||||
|
"vendor/lunasvg/source/svgparser.cpp",
|
||||||
|
"vendor/lunasvg/source/svgproperty.cpp",
|
||||||
|
"vendor/lunasvg/source/svgrenderstate.cpp",
|
||||||
|
"vendor/lunasvg/source/svgtextelement.cpp",
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// macOS build (clang++ with Objective-C++)
|
// macOS build (clang++ with Objective-C++)
|
||||||
@@ -22,6 +52,128 @@ static const char *frameworks[] = {
|
|||||||
"-framework", "CoreGraphics",
|
"-framework", "CoreGraphics",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||||
|
const char *obj_dir = nob_temp_sprintf("%s/lunasvg_obj", build_dir);
|
||||||
|
const char *lib_path = nob_temp_sprintf("%s/liblunasvg.a", build_dir);
|
||||||
|
|
||||||
|
// Collect all source paths to check if rebuild is needed
|
||||||
|
{
|
||||||
|
const char *all_sources[NOB_ARRAY_LEN(plutovg_sources) + NOB_ARRAY_LEN(lunasvg_sources)];
|
||||||
|
size_t n = 0;
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(plutovg_sources); i++)
|
||||||
|
all_sources[n++] = plutovg_sources[i];
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(lunasvg_sources); i++)
|
||||||
|
all_sources[n++] = lunasvg_sources[i];
|
||||||
|
|
||||||
|
if (!nob_needs_rebuild(lib_path, all_sources, n)) {
|
||||||
|
nob_log(NOB_INFO, "lunasvg is up to date");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nob_mkdir_if_not_exists(obj_dir)) return false;
|
||||||
|
|
||||||
|
// Compile plutovg C sources
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(plutovg_sources); i++) {
|
||||||
|
const char *src = plutovg_sources[i];
|
||||||
|
// Extract filename for .o
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char obj_name[256];
|
||||||
|
snprintf(obj_name, sizeof(obj_name), "%s", base);
|
||||||
|
char *dot = strrchr(obj_name, '.');
|
||||||
|
if (dot) { dot[1] = 'o'; dot[2] = '\0'; }
|
||||||
|
const char *obj_path = nob_temp_sprintf("%s/%s", obj_dir, obj_name);
|
||||||
|
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "clang");
|
||||||
|
nob_cmd_append(&cmd, "-std=c11", "-c");
|
||||||
|
nob_cmd_append(&cmd, "-DPLUTVOG_BUILD", "-DPLUTVOG_BUILD_STATIC");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/source");
|
||||||
|
nob_cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter");
|
||||||
|
if (debug) {
|
||||||
|
nob_cmd_append(&cmd, "-g", "-O0");
|
||||||
|
} else {
|
||||||
|
nob_cmd_append(&cmd, "-O2");
|
||||||
|
}
|
||||||
|
nob_cmd_append(&cmd, "-o", obj_path);
|
||||||
|
nob_cmd_append(&cmd, src);
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile lunasvg C++ sources
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(lunasvg_sources); i++) {
|
||||||
|
const char *src = lunasvg_sources[i];
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char obj_name[256];
|
||||||
|
snprintf(obj_name, sizeof(obj_name), "%s", base);
|
||||||
|
char *dot = strrchr(obj_name, '.');
|
||||||
|
if (dot) { dot[1] = 'o'; dot[2] = '\0'; }
|
||||||
|
const char *obj_path = nob_temp_sprintf("%s/%s", obj_dir, obj_name);
|
||||||
|
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "clang++");
|
||||||
|
nob_cmd_append(&cmd, "-std=c++17", "-c");
|
||||||
|
nob_cmd_append(&cmd, "-fno-exceptions", "-fno-rtti");
|
||||||
|
nob_cmd_append(&cmd, "-DLUNASVG_BUILD", "-DLUNASVG_BUILD_STATIC");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/include");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/source");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include");
|
||||||
|
nob_cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter");
|
||||||
|
if (debug) {
|
||||||
|
nob_cmd_append(&cmd, "-g", "-O0");
|
||||||
|
} else {
|
||||||
|
nob_cmd_append(&cmd, "-O2");
|
||||||
|
}
|
||||||
|
nob_cmd_append(&cmd, "-o", obj_path);
|
||||||
|
nob_cmd_append(&cmd, src);
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive into static library
|
||||||
|
{
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "ar", "rcs", lib_path);
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(plutovg_sources); i++) {
|
||||||
|
const char *src = plutovg_sources[i];
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char obj_name[256];
|
||||||
|
snprintf(obj_name, sizeof(obj_name), "%s", base);
|
||||||
|
char *dot = strrchr(obj_name, '.');
|
||||||
|
if (dot) { dot[1] = 'o'; dot[2] = '\0'; }
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("%s/%s", obj_dir, obj_name));
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(lunasvg_sources); i++) {
|
||||||
|
const char *src = lunasvg_sources[i];
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char obj_name[256];
|
||||||
|
snprintf(obj_name, sizeof(obj_name), "%s", base);
|
||||||
|
char *dot = strrchr(obj_name, '.');
|
||||||
|
if (dot) { dot[1] = 'o'; dot[2] = '\0'; }
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("%s/%s", obj_dir, obj_name));
|
||||||
|
}
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up obj dir — only the .a is needed
|
||||||
|
{
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "rm", "-rf", obj_dir);
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
nob_cmd_run_opt(&cmd, copt);
|
||||||
|
}
|
||||||
|
|
||||||
|
nob_log(NOB_INFO, "Built %s", lib_path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
NOB_GO_REBUILD_URSELF(argc, argv);
|
NOB_GO_REBUILD_URSELF(argc, argv);
|
||||||
|
|
||||||
@@ -55,6 +207,9 @@ int main(int argc, char **argv) {
|
|||||||
if (!nob_mkdir_if_not_exists(macos_dir)) return 1;
|
if (!nob_mkdir_if_not_exists(macos_dir)) return 1;
|
||||||
if (!nob_mkdir_if_not_exists(res_dir)) return 1;
|
if (!nob_mkdir_if_not_exists(res_dir)) return 1;
|
||||||
|
|
||||||
|
// Build lunasvg static library
|
||||||
|
if (!build_lunasvg_lib(build_dir, debug)) return 1;
|
||||||
|
|
||||||
// Unity build: single clang++ invocation compiles main.cpp (which #includes everything)
|
// Unity build: single clang++ invocation compiles main.cpp (which #includes everything)
|
||||||
{
|
{
|
||||||
Nob_Cmd cmd = {0};
|
Nob_Cmd cmd = {0};
|
||||||
@@ -64,6 +219,8 @@ int main(int argc, char **argv) {
|
|||||||
nob_cmd_append(&cmd, "-Wall", "-Wextra", "-Wno-missing-field-initializers");
|
nob_cmd_append(&cmd, "-Wall", "-Wextra", "-Wno-missing-field-initializers");
|
||||||
nob_cmd_append(&cmd, "-Wno-deprecated-declarations");
|
nob_cmd_append(&cmd, "-Wno-deprecated-declarations");
|
||||||
nob_cmd_append(&cmd, "-Isrc", "-Ivendor/clay");
|
nob_cmd_append(&cmd, "-Isrc", "-Ivendor/clay");
|
||||||
|
nob_cmd_append(&cmd, "-Ivendor/lunasvg/include");
|
||||||
|
nob_cmd_append(&cmd, "-DLUNASVG_BUILD_STATIC");
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
nob_cmd_append(&cmd, "-g", "-O0", "-D_DEBUG");
|
nob_cmd_append(&cmd, "-g", "-O0", "-D_DEBUG");
|
||||||
@@ -74,11 +231,16 @@ int main(int argc, char **argv) {
|
|||||||
nob_cmd_append(&cmd, "-o", binary_path);
|
nob_cmd_append(&cmd, "-o", binary_path);
|
||||||
nob_cmd_append(&cmd, "src/main.cpp");
|
nob_cmd_append(&cmd, "src/main.cpp");
|
||||||
|
|
||||||
|
// Reset language mode so .a is treated as a library, not source
|
||||||
|
nob_cmd_append(&cmd, "-x", "none");
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("%s/liblunasvg.a", build_dir));
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < NOB_ARRAY_LEN(frameworks); i++)
|
for (i = 0; i < NOB_ARRAY_LEN(frameworks); i++)
|
||||||
nob_cmd_append(&cmd, frameworks[i]);
|
nob_cmd_append(&cmd, frameworks[i]);
|
||||||
}
|
}
|
||||||
|
nob_cmd_append(&cmd, "-lstdc++");
|
||||||
{ Nob_Cmd_Opt opt = {0}; if (!nob_cmd_run_opt(&cmd, opt)) return 1; }
|
{ Nob_Cmd_Opt opt = {0}; if (!nob_cmd_run_opt(&cmd, opt)) return 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +319,92 @@ static const char *link_libs[] = {
|
|||||||
"winmm.lib",
|
"winmm.lib",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||||
|
const char *obj_dir = nob_temp_sprintf("%s\\lunasvg_obj", build_dir);
|
||||||
|
const char *lib_path = nob_temp_sprintf("%s\\lunasvg.lib", build_dir);
|
||||||
|
|
||||||
|
// Check if rebuild is needed
|
||||||
|
{
|
||||||
|
const char *all_sources[NOB_ARRAY_LEN(plutovg_sources) + NOB_ARRAY_LEN(lunasvg_sources)];
|
||||||
|
size_t n = 0;
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(plutovg_sources); i++)
|
||||||
|
all_sources[n++] = plutovg_sources[i];
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(lunasvg_sources); i++)
|
||||||
|
all_sources[n++] = lunasvg_sources[i];
|
||||||
|
|
||||||
|
if (!nob_needs_rebuild(lib_path, all_sources, n)) {
|
||||||
|
nob_log(NOB_INFO, "lunasvg is up to date");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nob_mkdir_if_not_exists(obj_dir)) return false;
|
||||||
|
|
||||||
|
// Compile plutovg C sources
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(plutovg_sources); i++) {
|
||||||
|
const char *src = plutovg_sources[i];
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "cl.exe", "/nologo", "/c");
|
||||||
|
nob_cmd_append(&cmd, "/std:c11");
|
||||||
|
nob_cmd_append(&cmd, "/DPLUTVOG_BUILD", "/DPLUTVOG_BUILD_STATIC");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/source");
|
||||||
|
nob_cmd_append(&cmd, "/W3");
|
||||||
|
if (debug) {
|
||||||
|
nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od");
|
||||||
|
} else {
|
||||||
|
nob_cmd_append(&cmd, "/MT", "/O2");
|
||||||
|
}
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("/Fo:%s/", obj_dir));
|
||||||
|
nob_cmd_append(&cmd, src);
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile lunasvg C++ sources
|
||||||
|
for (size_t i = 0; i < NOB_ARRAY_LEN(lunasvg_sources); i++) {
|
||||||
|
const char *src = lunasvg_sources[i];
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "cl.exe", "/nologo", "/c");
|
||||||
|
nob_cmd_append(&cmd, "/std:c++17", "/EHsc");
|
||||||
|
nob_cmd_append(&cmd, "/DLUNASVG_BUILD", "/DLUNASVG_BUILD_STATIC");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/include");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/source");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include");
|
||||||
|
nob_cmd_append(&cmd, "/W3");
|
||||||
|
if (debug) {
|
||||||
|
nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od");
|
||||||
|
} else {
|
||||||
|
nob_cmd_append(&cmd, "/MT", "/O2");
|
||||||
|
}
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("/Fo:%s/", obj_dir));
|
||||||
|
nob_cmd_append(&cmd, src);
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Archive into static library
|
||||||
|
{
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "lib.exe", "/nologo", nob_temp_sprintf("/OUT:%s", lib_path));
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("%s/*.obj", obj_dir));
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
if (!nob_cmd_run_opt(&cmd, copt)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up obj dir — only the .lib is needed
|
||||||
|
{
|
||||||
|
Nob_Cmd cmd = {0};
|
||||||
|
nob_cmd_append(&cmd, "cmd.exe", "/c",
|
||||||
|
nob_temp_sprintf("if exist %s rmdir /s /q %s", obj_dir, obj_dir));
|
||||||
|
Nob_Cmd_Opt copt = {0};
|
||||||
|
nob_cmd_run_opt(&cmd, copt);
|
||||||
|
}
|
||||||
|
|
||||||
|
nob_log(NOB_INFO, "Built %s", lib_path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
NOB_GO_REBUILD_URSELF(argc, argv);
|
NOB_GO_REBUILD_URSELF(argc, argv);
|
||||||
|
|
||||||
@@ -180,12 +428,17 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (!nob_mkdir_if_not_exists(build_dir)) return 1;
|
if (!nob_mkdir_if_not_exists(build_dir)) return 1;
|
||||||
|
|
||||||
|
// Build lunasvg static library
|
||||||
|
if (!build_lunasvg_lib(build_dir, debug)) return 1;
|
||||||
|
|
||||||
// Unity build: single cl.exe invocation compiles main.cpp (which #includes everything)
|
// Unity build: single cl.exe invocation compiles main.cpp (which #includes everything)
|
||||||
{
|
{
|
||||||
Nob_Cmd cmd = {0};
|
Nob_Cmd cmd = {0};
|
||||||
nob_cmd_append(&cmd, "cl.exe");
|
nob_cmd_append(&cmd, "cl.exe");
|
||||||
nob_cmd_append(&cmd, "/nologo", "/std:c++20", "/EHsc", "/W3");
|
nob_cmd_append(&cmd, "/nologo", "/std:c++20", "/EHsc", "/W3");
|
||||||
nob_cmd_append(&cmd, "/Isrc", "/Ivendor/clay");
|
nob_cmd_append(&cmd, "/Isrc", "/Ivendor/clay");
|
||||||
|
nob_cmd_append(&cmd, "/Ivendor/lunasvg/include");
|
||||||
|
nob_cmd_append(&cmd, "/DLUNASVG_BUILD_STATIC");
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od", "/D_DEBUG");
|
nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od", "/D_DEBUG");
|
||||||
@@ -204,6 +457,7 @@ int main(int argc, char **argv) {
|
|||||||
nob_cmd_append(&cmd, "/SUBSYSTEM:CONSOLE");
|
nob_cmd_append(&cmd, "/SUBSYSTEM:CONSOLE");
|
||||||
nob_cmd_append(&cmd, nob_temp_sprintf("/PDB:%s/autosample.pdb", build_dir));
|
nob_cmd_append(&cmd, nob_temp_sprintf("/PDB:%s/autosample.pdb", build_dir));
|
||||||
nob_cmd_append(&cmd, "/DEBUG");
|
nob_cmd_append(&cmd, "/DEBUG");
|
||||||
|
nob_cmd_append(&cmd, nob_temp_sprintf("%s/lunasvg.lib", build_dir));
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < NOB_ARRAY_LEN(link_libs); i++)
|
for (i = 0; i < NOB_ARRAY_LEN(link_libs); i++)
|
||||||
|
|||||||
24
compile_commands.json
Normal file
24
compile_commands.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"directory": "/Users/mta/projects/autosample",
|
||||||
|
"file": "src/main.cpp",
|
||||||
|
"arguments": [
|
||||||
|
"clang++",
|
||||||
|
"-std=c++20",
|
||||||
|
"-x", "objective-c++",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-Isrc",
|
||||||
|
"-Ivendor/clay",
|
||||||
|
"-Ivendor/lunasvg/include",
|
||||||
|
"-DLUNASVG_BUILD_STATIC",
|
||||||
|
"-D_DEBUG",
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
"-Wno-missing-field-initializers",
|
||||||
|
"-Wno-deprecated-declarations",
|
||||||
|
"-c",
|
||||||
|
"src/main.cpp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
12
src/main.cpp
12
src/main.cpp
@@ -10,11 +10,13 @@
|
|||||||
#include "midi/midi.h"
|
#include "midi/midi.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_icons.h"
|
||||||
#include "ui/ui_widgets.h"
|
#include "ui/ui_widgets.h"
|
||||||
|
|
||||||
// [cpp]
|
// [cpp]
|
||||||
#include "base/base_inc.cpp"
|
#include "base/base_inc.cpp"
|
||||||
#include "ui/ui_core.cpp"
|
#include "ui/ui_core.cpp"
|
||||||
|
#include "ui/ui_icons.cpp"
|
||||||
#include "ui/ui_widgets.cpp"
|
#include "ui/ui_widgets.cpp"
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include "platform/platform_macos.mm"
|
#include "platform/platform_macos.mm"
|
||||||
@@ -808,6 +810,16 @@ int main(int argc, char **argv) {
|
|||||||
setup_menus(window);
|
setup_menus(window);
|
||||||
ui_widgets_init();
|
ui_widgets_init();
|
||||||
|
|
||||||
|
// Rasterize icon atlas and upload to GPU
|
||||||
|
{
|
||||||
|
S32 iw, ih;
|
||||||
|
U8 *icon_atlas = ui_icons_rasterize_atlas(&iw, &ih, 48);
|
||||||
|
if (icon_atlas) {
|
||||||
|
renderer_create_icon_atlas(renderer, icon_atlas, iw, ih);
|
||||||
|
free(icon_atlas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AppState app = {};
|
AppState app = {};
|
||||||
app.window = window;
|
app.window = window;
|
||||||
app.renderer = renderer;
|
app.renderer = renderer;
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ void renderer_set_clear_color(Renderer *renderer, float r, float g, float b
|
|||||||
// user_data should be the Renderer pointer.
|
// user_data should be the Renderer pointer.
|
||||||
struct Vec2F32;
|
struct Vec2F32;
|
||||||
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data);
|
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data);
|
||||||
|
|
||||||
|
// Upload an R8 icon atlas texture for icon rendering
|
||||||
|
void renderer_create_icon_atlas(Renderer *renderer, const uint8_t *data, int32_t w, int32_t h);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "renderer/renderer.h"
|
#include "renderer/renderer.h"
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_icons.h"
|
||||||
|
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_5.h>
|
||||||
@@ -201,6 +202,10 @@ struct Renderer {
|
|||||||
F32 font_atlas_size; // font size the atlas was built at
|
F32 font_atlas_size; // font size the atlas was built at
|
||||||
F32 font_line_height;
|
F32 font_line_height;
|
||||||
|
|
||||||
|
// Icon atlas
|
||||||
|
ID3D12Resource *icon_texture;
|
||||||
|
ID3D12DescriptorHeap *icon_srv_heap;
|
||||||
|
|
||||||
// GDI text measurement
|
// GDI text measurement
|
||||||
HDC measure_dc;
|
HDC measure_dc;
|
||||||
HFONT measure_font;
|
HFONT measure_font;
|
||||||
@@ -976,7 +981,7 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// Flush helper: issues a draw call for accumulated vertices, then resets batch
|
// Flush helper: issues a draw call for accumulated vertices, then resets batch
|
||||||
|
|
||||||
static void flush_batch(Renderer *r, DrawBatch *batch, UINT buf_idx) {
|
static void flush_batch(Renderer *r, DrawBatch *batch, UINT buf_idx, ID3D12DescriptorHeap *tex_heap = nullptr) {
|
||||||
if (batch->index_count == 0) return;
|
if (batch->index_count == 0) return;
|
||||||
|
|
||||||
r->command_list->SetPipelineState(r->pipeline_state);
|
r->command_list->SetPipelineState(r->pipeline_state);
|
||||||
@@ -985,10 +990,11 @@ static void flush_batch(Renderer *r, DrawBatch *batch, UINT buf_idx) {
|
|||||||
float constants[4] = { (float)r->width, (float)r->height, 0, 0 };
|
float constants[4] = { (float)r->width, (float)r->height, 0, 0 };
|
||||||
r->command_list->SetGraphicsRoot32BitConstants(0, 4, constants, 0);
|
r->command_list->SetGraphicsRoot32BitConstants(0, 4, constants, 0);
|
||||||
|
|
||||||
// Bind font texture
|
// Bind texture (font or icon)
|
||||||
r->command_list->SetDescriptorHeaps(1, &r->srv_heap);
|
ID3D12DescriptorHeap *heap = tex_heap ? tex_heap : r->srv_heap;
|
||||||
|
r->command_list->SetDescriptorHeaps(1, &heap);
|
||||||
r->command_list->SetGraphicsRootDescriptorTable(1,
|
r->command_list->SetGraphicsRootDescriptorTable(1,
|
||||||
r->srv_heap->GetGPUDescriptorHandleForHeapStart());
|
heap->GetGPUDescriptorHandleForHeapStart());
|
||||||
|
|
||||||
r->command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
r->command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
|
|
||||||
@@ -1053,6 +1059,8 @@ void renderer_destroy(Renderer *r) {
|
|||||||
if (r->index_buffers[i]) r->index_buffers[i]->Release();
|
if (r->index_buffers[i]) r->index_buffers[i]->Release();
|
||||||
}
|
}
|
||||||
if (r->font_texture) r->font_texture->Release();
|
if (r->font_texture) r->font_texture->Release();
|
||||||
|
if (r->icon_texture) r->icon_texture->Release();
|
||||||
|
if (r->icon_srv_heap) r->icon_srv_heap->Release();
|
||||||
if (r->pipeline_state) r->pipeline_state->Release();
|
if (r->pipeline_state) r->pipeline_state->Release();
|
||||||
if (r->root_signature) r->root_signature->Release();
|
if (r->root_signature) r->root_signature->Release();
|
||||||
|
|
||||||
@@ -1133,6 +1141,23 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
batch.vertex_count = 0;
|
batch.vertex_count = 0;
|
||||||
batch.index_count = 0;
|
batch.index_count = 0;
|
||||||
|
|
||||||
|
// Track which texture is currently bound (0 = font, 1 = icon)
|
||||||
|
int bound_texture = 0;
|
||||||
|
|
||||||
|
auto bind_font = [&]() {
|
||||||
|
if (bound_texture != 0) {
|
||||||
|
flush_batch(r, &batch, buf_idx, r->srv_heap);
|
||||||
|
bound_texture = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto bind_icon = [&]() {
|
||||||
|
if (bound_texture != 1 && r->icon_srv_heap) {
|
||||||
|
flush_batch(r, &batch, buf_idx);
|
||||||
|
bound_texture = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (int32_t i = 0; i < render_commands.length; i++) {
|
for (int32_t i = 0; i < render_commands.length; i++) {
|
||||||
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
|
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
|
||||||
Clay_BoundingBox bb = cmd->boundingBox;
|
Clay_BoundingBox bb = cmd->boundingBox;
|
||||||
@@ -1177,6 +1202,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||||
|
bind_font();
|
||||||
Clay_TextRenderData *text = &cmd->renderData.text;
|
Clay_TextRenderData *text = &cmd->renderData.text;
|
||||||
emit_text_glyphs(&batch, r, bb, text->textColor,
|
emit_text_glyphs(&batch, r, bb, text->textColor,
|
||||||
text->stringContents.chars, text->stringContents.length,
|
text->stringContents.chars, text->stringContents.length,
|
||||||
@@ -1185,7 +1211,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
||||||
// Flush before changing scissor
|
// Flush before changing scissor
|
||||||
flush_batch(r, &batch, buf_idx);
|
ID3D12DescriptorHeap *heap = bound_texture == 1 ? r->icon_srv_heap : r->srv_heap;
|
||||||
|
flush_batch(r, &batch, buf_idx, heap);
|
||||||
D3D12_RECT clip = {};
|
D3D12_RECT clip = {};
|
||||||
clip.left = (LONG)Max(bb.x, 0.f);
|
clip.left = (LONG)Max(bb.x, 0.f);
|
||||||
clip.top = (LONG)Max(bb.y, 0.f);
|
clip.top = (LONG)Max(bb.y, 0.f);
|
||||||
@@ -1197,7 +1224,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
|
||||||
flush_batch(r, &batch, buf_idx);
|
ID3D12DescriptorHeap *heap = bound_texture == 1 ? r->icon_srv_heap : r->srv_heap;
|
||||||
|
flush_batch(r, &batch, buf_idx, heap);
|
||||||
D3D12_RECT full_scissor = { 0, 0, (LONG)r->width, (LONG)r->height };
|
D3D12_RECT full_scissor = { 0, 0, (LONG)r->width, (LONG)r->height };
|
||||||
r->command_list->RSSetScissorRects(1, &full_scissor);
|
r->command_list->RSSetScissorRects(1, &full_scissor);
|
||||||
} break;
|
} break;
|
||||||
@@ -1207,6 +1235,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
if (custom->customData) {
|
if (custom->customData) {
|
||||||
CustomRenderType type = *(CustomRenderType *)custom->customData;
|
CustomRenderType type = *(CustomRenderType *)custom->customData;
|
||||||
if (type == CUSTOM_RENDER_VGRADIENT) {
|
if (type == CUSTOM_RENDER_VGRADIENT) {
|
||||||
|
bind_font();
|
||||||
CustomGradientData *grad = (CustomGradientData *)custom->customData;
|
CustomGradientData *grad = (CustomGradientData *)custom->customData;
|
||||||
Clay_Color tc = grad->top_color;
|
Clay_Color tc = grad->top_color;
|
||||||
Clay_Color bc = grad->bottom_color;
|
Clay_Color bc = grad->bottom_color;
|
||||||
@@ -1217,6 +1246,20 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
custom->cornerRadius.topLeft, custom->cornerRadius.topRight,
|
custom->cornerRadius.topLeft, custom->cornerRadius.topRight,
|
||||||
custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft,
|
custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft,
|
||||||
1.0f);
|
1.0f);
|
||||||
|
} else if (type == CUSTOM_RENDER_ICON) {
|
||||||
|
bind_icon();
|
||||||
|
CustomIconData *icon = (CustomIconData *)custom->customData;
|
||||||
|
Clay_Color c = icon->color;
|
||||||
|
float cr = c.r / 255.f, cg = c.g / 255.f;
|
||||||
|
float cb = c.b / 255.f, ca = c.a / 255.f;
|
||||||
|
UI_IconInfo *info = &g_icons[icon->icon_id];
|
||||||
|
emit_quad(&batch,
|
||||||
|
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
|
||||||
|
info->u0, info->v0, info->u1, info->v1,
|
||||||
|
cr, cg, cb, ca,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1228,7 +1271,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Flush remaining
|
// Flush remaining
|
||||||
flush_batch(r, &batch, buf_idx);
|
ID3D12DescriptorHeap *heap = bound_texture == 1 ? r->icon_srv_heap : r->srv_heap;
|
||||||
|
flush_batch(r, &batch, buf_idx, heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||||
@@ -1245,6 +1289,101 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
r->frame_index++;
|
r->frame_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int32_t h) {
|
||||||
|
// Create texture resource
|
||||||
|
D3D12_HEAP_PROPERTIES heap_props = {};
|
||||||
|
heap_props.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC tex_desc = {};
|
||||||
|
tex_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||||
|
tex_desc.Width = w;
|
||||||
|
tex_desc.Height = h;
|
||||||
|
tex_desc.DepthOrArraySize = 1;
|
||||||
|
tex_desc.MipLevels = 1;
|
||||||
|
tex_desc.Format = DXGI_FORMAT_R8_UNORM;
|
||||||
|
tex_desc.SampleDesc.Count = 1;
|
||||||
|
tex_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||||
|
|
||||||
|
r->device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE,
|
||||||
|
&tex_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
|
||||||
|
IID_PPV_ARGS(&r->icon_texture));
|
||||||
|
|
||||||
|
// Upload via staging buffer
|
||||||
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
|
||||||
|
UINT64 total_bytes = 0;
|
||||||
|
r->device->GetCopyableFootprints(&tex_desc, 0, 1, 0, &footprint, nullptr, nullptr, &total_bytes);
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES upload_heap = {};
|
||||||
|
upload_heap.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||||
|
|
||||||
|
D3D12_RESOURCE_DESC upload_desc = {};
|
||||||
|
upload_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||||
|
upload_desc.Width = total_bytes;
|
||||||
|
upload_desc.Height = 1;
|
||||||
|
upload_desc.DepthOrArraySize = 1;
|
||||||
|
upload_desc.MipLevels = 1;
|
||||||
|
upload_desc.SampleDesc.Count = 1;
|
||||||
|
upload_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||||
|
|
||||||
|
ID3D12Resource *upload_buf = nullptr;
|
||||||
|
r->device->CreateCommittedResource(&upload_heap, D3D12_HEAP_FLAG_NONE,
|
||||||
|
&upload_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
|
||||||
|
IID_PPV_ARGS(&upload_buf));
|
||||||
|
|
||||||
|
void *mapped = nullptr;
|
||||||
|
D3D12_RANGE read_range = {0, 0};
|
||||||
|
upload_buf->Map(0, &read_range, &mapped);
|
||||||
|
U8 *dst = (U8 *)mapped;
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
memcpy(dst + y * footprint.Footprint.RowPitch, data + y * w, w);
|
||||||
|
}
|
||||||
|
upload_buf->Unmap(0, nullptr);
|
||||||
|
|
||||||
|
r->frames[0].command_allocator->Reset();
|
||||||
|
r->command_list->Reset(r->frames[0].command_allocator, nullptr);
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION src_loc = {};
|
||||||
|
src_loc.pResource = upload_buf;
|
||||||
|
src_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||||
|
src_loc.PlacedFootprint = footprint;
|
||||||
|
|
||||||
|
D3D12_TEXTURE_COPY_LOCATION dst_loc = {};
|
||||||
|
dst_loc.pResource = r->icon_texture;
|
||||||
|
dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||||
|
dst_loc.SubresourceIndex = 0;
|
||||||
|
|
||||||
|
r->command_list->CopyTextureRegion(&dst_loc, 0, 0, 0, &src_loc, nullptr);
|
||||||
|
|
||||||
|
D3D12_RESOURCE_BARRIER barrier = {};
|
||||||
|
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||||
|
barrier.Transition.pResource = r->icon_texture;
|
||||||
|
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||||
|
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||||
|
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||||
|
r->command_list->ResourceBarrier(1, &barrier);
|
||||||
|
|
||||||
|
r->command_list->Close();
|
||||||
|
r->command_queue->ExecuteCommandLists(1, (ID3D12CommandList *const *)&r->command_list);
|
||||||
|
wait_for_pending(r);
|
||||||
|
upload_buf->Release();
|
||||||
|
|
||||||
|
// Create separate SRV heap for icon texture
|
||||||
|
D3D12_DESCRIPTOR_HEAP_DESC srv_desc = {};
|
||||||
|
srv_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||||
|
srv_desc.NumDescriptors = 1;
|
||||||
|
srv_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||||
|
r->device->CreateDescriptorHeap(&srv_desc, IID_PPV_ARGS(&r->icon_srv_heap));
|
||||||
|
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC srv_view = {};
|
||||||
|
srv_view.Format = DXGI_FORMAT_R8_UNORM;
|
||||||
|
srv_view.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
srv_view.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
|
srv_view.Texture2D.MipLevels = 1;
|
||||||
|
|
||||||
|
r->device->CreateShaderResourceView(r->icon_texture,
|
||||||
|
&srv_view, r->icon_srv_heap->GetCPUDescriptorHandleForHeapStart());
|
||||||
|
}
|
||||||
|
|
||||||
void renderer_resize(Renderer *r, int32_t width, int32_t height) {
|
void renderer_resize(Renderer *r, int32_t width, int32_t height) {
|
||||||
if (width <= 0 || height <= 0) return;
|
if (width <= 0 || height <= 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "renderer/renderer.h"
|
#include "renderer/renderer.h"
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_icons.h"
|
||||||
|
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
@@ -162,6 +163,9 @@ struct Renderer {
|
|||||||
F32 font_atlas_size;
|
F32 font_atlas_size;
|
||||||
F32 font_line_height;
|
F32 font_line_height;
|
||||||
|
|
||||||
|
// Icon atlas
|
||||||
|
id<MTLTexture> icon_texture;
|
||||||
|
|
||||||
// Text measurement (Core Text)
|
// Text measurement (Core Text)
|
||||||
CTFontRef measure_font;
|
CTFontRef measure_font;
|
||||||
F32 measure_font_size;
|
F32 measure_font_size;
|
||||||
@@ -694,6 +698,9 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
batch.vertex_count = 0;
|
batch.vertex_count = 0;
|
||||||
batch.index_count = 0;
|
batch.index_count = 0;
|
||||||
|
|
||||||
|
// Track which texture is currently bound (0 = font, 1 = icon)
|
||||||
|
int bound_texture = 0;
|
||||||
|
|
||||||
auto flush_batch = [&]() {
|
auto flush_batch = [&]() {
|
||||||
if (batch.index_count == 0) return;
|
if (batch.index_count == 0) return;
|
||||||
|
|
||||||
@@ -708,6 +715,22 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
batch.index_count = 0;
|
batch.index_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto bind_font_texture = [&]() {
|
||||||
|
if (bound_texture != 0) {
|
||||||
|
flush_batch();
|
||||||
|
[encoder setFragmentTexture:r->font_texture atIndex:0];
|
||||||
|
bound_texture = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto bind_icon_texture = [&]() {
|
||||||
|
if (bound_texture != 1 && r->icon_texture) {
|
||||||
|
flush_batch();
|
||||||
|
[encoder setFragmentTexture:r->icon_texture atIndex:0];
|
||||||
|
bound_texture = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (int32_t i = 0; i < render_commands.length; i++) {
|
for (int32_t i = 0; i < render_commands.length; i++) {
|
||||||
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
|
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
|
||||||
Clay_BoundingBox bb = cmd->boundingBox;
|
Clay_BoundingBox bb = cmd->boundingBox;
|
||||||
@@ -751,6 +774,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||||
|
bind_font_texture();
|
||||||
Clay_TextRenderData *text = &cmd->renderData.text;
|
Clay_TextRenderData *text = &cmd->renderData.text;
|
||||||
emit_text_glyphs(&batch, r, bb, text->textColor,
|
emit_text_glyphs(&batch, r, bb, text->textColor,
|
||||||
text->stringContents.chars, text->stringContents.length,
|
text->stringContents.chars, text->stringContents.length,
|
||||||
@@ -779,6 +803,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
if (custom->customData) {
|
if (custom->customData) {
|
||||||
CustomRenderType type = *(CustomRenderType *)custom->customData;
|
CustomRenderType type = *(CustomRenderType *)custom->customData;
|
||||||
if (type == CUSTOM_RENDER_VGRADIENT) {
|
if (type == CUSTOM_RENDER_VGRADIENT) {
|
||||||
|
bind_font_texture();
|
||||||
CustomGradientData *grad = (CustomGradientData *)custom->customData;
|
CustomGradientData *grad = (CustomGradientData *)custom->customData;
|
||||||
Clay_Color tc = grad->top_color;
|
Clay_Color tc = grad->top_color;
|
||||||
Clay_Color bc = grad->bottom_color;
|
Clay_Color bc = grad->bottom_color;
|
||||||
@@ -789,6 +814,20 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
custom->cornerRadius.topLeft, custom->cornerRadius.topRight,
|
custom->cornerRadius.topLeft, custom->cornerRadius.topRight,
|
||||||
custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft,
|
custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft,
|
||||||
1.0f);
|
1.0f);
|
||||||
|
} else if (type == CUSTOM_RENDER_ICON) {
|
||||||
|
bind_icon_texture();
|
||||||
|
CustomIconData *icon = (CustomIconData *)custom->customData;
|
||||||
|
Clay_Color c = icon->color;
|
||||||
|
float cr = c.r / 255.f, cg = c.g / 255.f;
|
||||||
|
float cb = c.b / 255.f, ca = c.a / 255.f;
|
||||||
|
UI_IconInfo *info = &g_icons[icon->icon_id];
|
||||||
|
emit_quad(&batch,
|
||||||
|
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
|
||||||
|
info->u0, info->v0, info->u1, info->v1,
|
||||||
|
cr, cg, cb, ca,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -816,6 +855,20 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
|||||||
r->frame_index++;
|
r->frame_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int32_t h) {
|
||||||
|
MTLTextureDescriptor *tex_desc = [[MTLTextureDescriptor alloc] init];
|
||||||
|
tex_desc.pixelFormat = MTLPixelFormatR8Unorm;
|
||||||
|
tex_desc.width = w;
|
||||||
|
tex_desc.height = h;
|
||||||
|
tex_desc.usage = MTLTextureUsageShaderRead;
|
||||||
|
|
||||||
|
r->icon_texture = [r->device newTextureWithDescriptor:tex_desc];
|
||||||
|
[r->icon_texture replaceRegion:MTLRegionMake2D(0, 0, w, h)
|
||||||
|
mipmapLevel:0
|
||||||
|
withBytes:data
|
||||||
|
bytesPerRow:w];
|
||||||
|
}
|
||||||
|
|
||||||
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
|
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
|
||||||
r->clear_r = cr;
|
r->clear_r = cr;
|
||||||
r->clear_g = cg;
|
r->clear_g = cg;
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ static inline uint16_t uifs(float x) { return (uint16_t)(x * g_ui_scale + 0.5f);
|
|||||||
|
|
||||||
enum CustomRenderType {
|
enum CustomRenderType {
|
||||||
CUSTOM_RENDER_VGRADIENT = 1,
|
CUSTOM_RENDER_VGRADIENT = 1,
|
||||||
|
CUSTOM_RENDER_ICON = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CustomGradientData {
|
struct CustomGradientData {
|
||||||
@@ -121,6 +122,12 @@ struct CustomGradientData {
|
|||||||
Clay_Color bottom_color;
|
Clay_Color bottom_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CustomIconData {
|
||||||
|
CustomRenderType type; // CUSTOM_RENDER_ICON
|
||||||
|
S32 icon_id;
|
||||||
|
Clay_Color color;
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// Font sizes
|
// Font sizes
|
||||||
|
|
||||||
|
|||||||
76
src/ui/ui_icons.cpp
Normal file
76
src/ui/ui_icons.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// ui_icons.cpp - SVG icon rasterization via lunasvg
|
||||||
|
|
||||||
|
#include "ui/ui_icons.h"
|
||||||
|
#include <lunasvg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
UI_IconInfo g_icons[UI_ICON_COUNT] = {};
|
||||||
|
|
||||||
|
// Simple SVG icon sources (24x24 viewBox)
|
||||||
|
static const char *g_icon_svgs[UI_ICON_COUNT] = {
|
||||||
|
// UI_ICON_CLOSE - X mark
|
||||||
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M6 6 L18 18 M18 6 L6 18" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
|
||||||
|
</svg>)",
|
||||||
|
|
||||||
|
// UI_ICON_CHECK - checkmark
|
||||||
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M5 12 L10 17 L19 7" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
</svg>)",
|
||||||
|
|
||||||
|
// UI_ICON_CHEVRON_DOWN - downward arrow
|
||||||
|
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M7 10 L12 15 L17 10" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||||
|
</svg>)",
|
||||||
|
};
|
||||||
|
|
||||||
|
U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
||||||
|
// Pack icons in a row
|
||||||
|
S32 atlas_w = icon_size * UI_ICON_COUNT;
|
||||||
|
S32 atlas_h = icon_size;
|
||||||
|
|
||||||
|
// Pad to power of 2 isn't necessary for correctness, but ensure minimum size
|
||||||
|
if (atlas_w < 64) atlas_w = 64;
|
||||||
|
if (atlas_h < 64) atlas_h = 64;
|
||||||
|
|
||||||
|
U8 *atlas = (U8 *)calloc(atlas_w * atlas_h, 1);
|
||||||
|
if (!atlas) return nullptr;
|
||||||
|
|
||||||
|
S32 pen_x = 0;
|
||||||
|
for (S32 i = 0; i < UI_ICON_COUNT; i++) {
|
||||||
|
auto doc = lunasvg::Document::loadFromData(g_icon_svgs[i]);
|
||||||
|
if (!doc) continue;
|
||||||
|
|
||||||
|
lunasvg::Bitmap bmp = doc->renderToBitmap(icon_size, icon_size);
|
||||||
|
if (bmp.isNull()) continue;
|
||||||
|
|
||||||
|
// Extract alpha channel from ARGB32 premultiplied into R8
|
||||||
|
U8 *src = bmp.data();
|
||||||
|
S32 bmp_w = bmp.width();
|
||||||
|
S32 bmp_h = bmp.height();
|
||||||
|
S32 stride = bmp.stride();
|
||||||
|
|
||||||
|
for (S32 y = 0; y < bmp_h && y < atlas_h; y++) {
|
||||||
|
for (S32 x = 0; x < bmp_w && (pen_x + x) < atlas_w; x++) {
|
||||||
|
// ARGB32 premultiplied: bytes are B, G, R, A (little-endian)
|
||||||
|
U8 a = src[y * stride + x * 4 + 3];
|
||||||
|
atlas[y * atlas_w + pen_x + x] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store UV and pixel info
|
||||||
|
g_icons[i].u0 = (F32)pen_x / (F32)atlas_w;
|
||||||
|
g_icons[i].v0 = 0.0f;
|
||||||
|
g_icons[i].u1 = (F32)(pen_x + bmp_w) / (F32)atlas_w;
|
||||||
|
g_icons[i].v1 = (F32)bmp_h / (F32)atlas_h;
|
||||||
|
g_icons[i].w = (F32)bmp_w;
|
||||||
|
g_icons[i].h = (F32)bmp_h;
|
||||||
|
|
||||||
|
pen_x += icon_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_w = atlas_w;
|
||||||
|
*out_h = atlas_h;
|
||||||
|
return atlas;
|
||||||
|
}
|
||||||
23
src/ui/ui_icons.h
Normal file
23
src/ui/ui_icons.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
// ui_icons.h - SVG icon definitions and atlas rasterization via lunasvg
|
||||||
|
|
||||||
|
#include "base/base_inc.h"
|
||||||
|
|
||||||
|
enum UI_IconID {
|
||||||
|
UI_ICON_CLOSE,
|
||||||
|
UI_ICON_CHECK,
|
||||||
|
UI_ICON_CHEVRON_DOWN,
|
||||||
|
UI_ICON_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UI_IconInfo {
|
||||||
|
F32 u0, v0, u1, v1; // UV coordinates in icon atlas
|
||||||
|
F32 w, h; // pixel dimensions at rasterized size
|
||||||
|
};
|
||||||
|
|
||||||
|
extern UI_IconInfo g_icons[UI_ICON_COUNT];
|
||||||
|
|
||||||
|
// Rasterizes all icons into an R8 atlas bitmap.
|
||||||
|
// Returns malloc'd data (caller frees). Sets *out_w, *out_h to atlas dimensions.
|
||||||
|
// icon_size is the pixel height to rasterize each icon at.
|
||||||
|
U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size);
|
||||||
@@ -10,6 +10,11 @@
|
|||||||
|
|
||||||
UI_WidgetState g_wstate = {};
|
UI_WidgetState g_wstate = {};
|
||||||
|
|
||||||
|
// Icon per-frame pool (forward declaration for begin_frame)
|
||||||
|
#define UI_MAX_ICONS_PER_FRAME 32
|
||||||
|
static CustomIconData g_icon_pool[UI_MAX_ICONS_PER_FRAME];
|
||||||
|
static S32 g_icon_pool_count = 0;
|
||||||
|
|
||||||
void ui_widgets_init() {
|
void ui_widgets_init() {
|
||||||
g_wstate = {};
|
g_wstate = {};
|
||||||
}
|
}
|
||||||
@@ -17,6 +22,7 @@ void ui_widgets_init() {
|
|||||||
void ui_widgets_begin_frame(PlatformInput input) {
|
void ui_widgets_begin_frame(PlatformInput input) {
|
||||||
g_wstate.input = input;
|
g_wstate.input = input;
|
||||||
g_wstate.mouse_clicked = (input.mouse_down && !input.was_mouse_down);
|
g_wstate.mouse_clicked = (input.mouse_down && !input.was_mouse_down);
|
||||||
|
g_icon_pool_count = 0;
|
||||||
g_wstate.cursor_blink += 1.0f / 60.0f;
|
g_wstate.cursor_blink += 1.0f / 60.0f;
|
||||||
g_wstate.text_input_count = 0;
|
g_wstate.text_input_count = 0;
|
||||||
g_wstate.tab_pressed = 0;
|
g_wstate.tab_pressed = 0;
|
||||||
@@ -96,6 +102,26 @@ static Clay_String clay_str(const char *s) {
|
|||||||
#define WID(s) CLAY_SID(clay_str(s))
|
#define WID(s) CLAY_SID(clay_str(s))
|
||||||
#define WIDI(s, i) CLAY_SIDI(clay_str(s), i)
|
#define WIDI(s, i) CLAY_SIDI(clay_str(s), i)
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Icon
|
||||||
|
|
||||||
|
void ui_icon(UI_IconID icon, F32 size, Clay_Color color) {
|
||||||
|
if (g_icon_pool_count >= UI_MAX_ICONS_PER_FRAME) return;
|
||||||
|
|
||||||
|
S32 idx = g_icon_pool_count;
|
||||||
|
CustomIconData *data = &g_icon_pool[g_icon_pool_count++];
|
||||||
|
data->type = CUSTOM_RENDER_ICON;
|
||||||
|
data->icon_id = (S32)icon;
|
||||||
|
data->color = color;
|
||||||
|
|
||||||
|
CLAY(CLAY_IDI("UIIcon", idx),
|
||||||
|
.layout = {
|
||||||
|
.sizing = { .width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) },
|
||||||
|
},
|
||||||
|
.custom = { .customData = data }
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// Label
|
// Label
|
||||||
|
|
||||||
@@ -168,7 +194,7 @@ B32 ui_checkbox(const char *id, const char *label, B32 *value) {
|
|||||||
.border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }
|
.border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } }
|
||||||
) {
|
) {
|
||||||
if (*value) {
|
if (*value) {
|
||||||
CLAY_TEXT(CLAY_STRING("x"), &g_widget_text_config);
|
ui_icon(UI_ICON_CHECK, WIDGET_CHECKBOX_SIZE * 0.75f, g_theme.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,11 +733,11 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
|||||||
|
|
||||||
CLAY(WIDI(id, 501),
|
CLAY(WIDI(id, 501),
|
||||||
.layout = {
|
.layout = {
|
||||||
.sizing = { .width = CLAY_SIZING_FIXED(uis(20)), .height = CLAY_SIZING_FIT() },
|
.sizing = { .width = CLAY_SIZING_FIXED(uis(20)), .height = CLAY_SIZING_FIXED(uis(20)) },
|
||||||
.childAlignment = { .x = CLAY_ALIGN_X_CENTER },
|
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER },
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
CLAY_TEXT(CLAY_STRING("v"), &g_widget_text_config_dim);
|
ui_icon(UI_ICON_CHEVRON_DOWN, uis(12), g_theme.text_dim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,7 +1104,7 @@ B32 ui_window(const char *id, const char *title, B32 *open,
|
|||||||
.backgroundColor = close_bg,
|
.backgroundColor = close_bg,
|
||||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS_SM)
|
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS_SM)
|
||||||
) {
|
) {
|
||||||
CLAY_TEXT(CLAY_STRING("x"), &g_widget_text_config_btn);
|
ui_icon(UI_ICON_CLOSE, uis(12), g_theme.button_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// like which text field is focused or which dropdown is open.
|
// like which text field is focused or which dropdown is open.
|
||||||
|
|
||||||
#include "ui/ui_core.h"
|
#include "ui/ui_core.h"
|
||||||
|
#include "ui/ui_icons.h"
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -89,6 +90,9 @@ void ui_text_input_reset_display_bufs();
|
|||||||
// Widgets
|
// Widgets
|
||||||
// All IDs must be unique string literals (passed to CLAY_ID internally).
|
// All IDs must be unique string literals (passed to CLAY_ID internally).
|
||||||
|
|
||||||
|
// Icon element (rendered via icon atlas)
|
||||||
|
void ui_icon(UI_IconID icon, F32 size, Clay_Color color);
|
||||||
|
|
||||||
// Simple label
|
// Simple label
|
||||||
void ui_label(const char *id, const char *text);
|
void ui_label(const char *id, const char *text);
|
||||||
|
|
||||||
|
|||||||
21
vendor/lunasvg/LICENSE
vendored
Normal file
21
vendor/lunasvg/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020-2026 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.
|
||||||
791
vendor/lunasvg/include/lunasvg.h
vendored
Normal file
791
vendor/lunasvg/include/lunasvg.h
vendored
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2026 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 <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if defined(LUNASVG_BUILD_STATIC)
|
||||||
|
#define LUNASVG_EXPORT
|
||||||
|
#define LUNASVG_IMPORT
|
||||||
|
#elif (defined(_WIN32) || defined(__CYGWIN__))
|
||||||
|
#define LUNASVG_EXPORT __declspec(dllexport)
|
||||||
|
#define LUNASVG_IMPORT __declspec(dllimport)
|
||||||
|
#elif defined(__GNUC__) && (__GNUC__ >= 4)
|
||||||
|
#define LUNASVG_EXPORT __attribute__((__visibility__("default")))
|
||||||
|
#define LUNASVG_IMPORT
|
||||||
|
#else
|
||||||
|
#define LUNASVG_EXPORT
|
||||||
|
#define LUNASVG_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LUNASVG_BUILD
|
||||||
|
#define LUNASVG_API LUNASVG_EXPORT
|
||||||
|
#else
|
||||||
|
#define LUNASVG_API LUNASVG_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LUNASVG_VERSION_MAJOR 3
|
||||||
|
#define LUNASVG_VERSION_MINOR 5
|
||||||
|
#define LUNASVG_VERSION_MICRO 0
|
||||||
|
|
||||||
|
#define LUNASVG_VERSION_ENCODE(major, minor, micro) (((major) * 10000) + ((minor) * 100) + ((micro) * 1))
|
||||||
|
#define LUNASVG_VERSION LUNASVG_VERSION_ENCODE(LUNASVG_VERSION_MAJOR, LUNASVG_VERSION_MINOR, LUNASVG_VERSION_MICRO)
|
||||||
|
|
||||||
|
#define LUNASVG_VERSION_XSTRINGIZE(major, minor, micro) #major"."#minor"."#micro
|
||||||
|
#define LUNASVG_VERSION_STRINGIZE(major, minor, micro) LUNASVG_VERSION_XSTRINGIZE(major, minor, micro)
|
||||||
|
#define LUNASVG_VERSION_STRING LUNASVG_VERSION_STRINGIZE(LUNASVG_VERSION_MAJOR, LUNASVG_VERSION_MINOR, LUNASVG_VERSION_MICRO)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct plutovg_surface plutovg_surface_t;
|
||||||
|
typedef struct plutovg_matrix plutovg_matrix_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for cleaning up resources.
|
||||||
|
*
|
||||||
|
* This function is called to release resources associated with a specific operation.
|
||||||
|
*
|
||||||
|
* @param closure A user-defined pointer to the resource or context to be freed.
|
||||||
|
*/
|
||||||
|
typedef void (*lunasvg_destroy_func_t)(void* closure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A function pointer type for a write callback.
|
||||||
|
* @param closure A pointer to user-defined data or context.
|
||||||
|
* @param data A pointer to the data to be written.
|
||||||
|
* @param size The size of the data in bytes.
|
||||||
|
*/
|
||||||
|
typedef void (*lunasvg_write_func_t)(void* closure, void* data, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the version of the lunasvg library encoded in a single integer.
|
||||||
|
*
|
||||||
|
* Encodes the version of the lunasvg library into a single integer for easier comparison.
|
||||||
|
* The version is typically represented by combining major, minor, and patch numbers into one integer.
|
||||||
|
*
|
||||||
|
* @return The lunasvg library version as a single integer.
|
||||||
|
*/
|
||||||
|
LUNASVG_API int lunasvg_version(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the lunasvg library version as a human-readable string in "X.Y.Z" format.
|
||||||
|
*
|
||||||
|
* Provides the version of the lunasvg library as a human-readable string in the format "X.Y.Z",
|
||||||
|
* where X represents the major version, Y the minor version, and Z the patch version.
|
||||||
|
*
|
||||||
|
* @return A pointer to a string containing the version in "X.Y.Z" format.
|
||||||
|
*/
|
||||||
|
LUNASVG_API const char* lunasvg_version_string(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a font face from a file to the cache.
|
||||||
|
* @param family The name of the font family. If an empty string is provided, the font will act as a fallback.
|
||||||
|
* @param bold Use `true` for bold, `false` otherwise.
|
||||||
|
* @param italic Use `true` for italic, `false` otherwise.
|
||||||
|
* @param filename The path to the font file.
|
||||||
|
* @return `true` if the font face was successfully added to the cache, `false` otherwise.
|
||||||
|
*/
|
||||||
|
LUNASVG_API bool lunasvg_add_font_face_from_file(const char* family, bool bold, bool italic, const char* filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a font face from memory to the cache.
|
||||||
|
* @param family The name of the font family. If an empty string is provided, the font will act as a fallback.
|
||||||
|
* @param bold Use `true` for bold, `false` otherwise.
|
||||||
|
* @param italic Use `true` for italic, `false` otherwise.
|
||||||
|
* @param data A pointer to the memory buffer containing the font data.
|
||||||
|
* @param length The size of the memory buffer in bytes.
|
||||||
|
* @param destroy_func Callback function to free the memory buffer when it is no longer needed.
|
||||||
|
* @param closure User-defined pointer passed to the `destroy_func` callback.
|
||||||
|
* @return `true` if the font face was successfully added to the cache, `false` otherwise.
|
||||||
|
*/
|
||||||
|
LUNASVG_API bool lunasvg_add_font_face_from_data(const char* family, bool bold, bool italic, const void* data, size_t length, lunasvg_destroy_func_t destroy_func, void* closure);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Bitmap pixel format is ARGB32_Premultiplied.
|
||||||
|
*/
|
||||||
|
class LUNASVG_API Bitmap {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a null bitmap.
|
||||||
|
*/
|
||||||
|
Bitmap() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a bitmap with the specified width and height.
|
||||||
|
* @note A null bitmap will be returned if memory cannot be allocated.
|
||||||
|
* @param width The width of the bitmap in pixels.
|
||||||
|
* @param height The height of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
Bitmap(int width, int height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a bitmap with the provided pixel data, width, height, and stride.
|
||||||
|
*
|
||||||
|
* @param data A pointer to the raw pixel data in ARGB32 Premultiplied format.
|
||||||
|
* @param width The width of the bitmap in pixels.
|
||||||
|
* @param height The height of the bitmap in pixels.
|
||||||
|
* @param stride The number of bytes per row of pixel data (stride).
|
||||||
|
*/
|
||||||
|
Bitmap(uint8_t* data, int width, int height, int stride);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor.
|
||||||
|
* @param bitmap The bitmap to copy.
|
||||||
|
*/
|
||||||
|
Bitmap(const Bitmap& bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
* @param bitmap The bitmap to move.
|
||||||
|
*/
|
||||||
|
Bitmap(Bitmap&& bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
Bitmap(plutovg_surface_t* surface) : m_surface(surface) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Cleans up any resources associated with the bitmap.
|
||||||
|
*/
|
||||||
|
~Bitmap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy assignment operator.
|
||||||
|
* @param bitmap The bitmap to copy.
|
||||||
|
* @return A reference to this bitmap.
|
||||||
|
*/
|
||||||
|
Bitmap& operator=(const Bitmap& bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move assignment operator.
|
||||||
|
* @param bitmap The bitmap to move.
|
||||||
|
* @return A reference to this bitmap.
|
||||||
|
*/
|
||||||
|
Bitmap& operator=(Bitmap&& bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swaps the content of this bitmap with another.
|
||||||
|
* @param bitmap The bitmap to swap with.
|
||||||
|
*/
|
||||||
|
void swap(Bitmap& bitmap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the pointer to the raw pixel data.
|
||||||
|
* @return A pointer to the raw pixel data.
|
||||||
|
*/
|
||||||
|
uint8_t* data() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the width of the bitmap.
|
||||||
|
* @return The width of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
int width() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the height of the bitmap.
|
||||||
|
* @return The height of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
int height() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the stride of the bitmap.
|
||||||
|
* @return The number of bytes per row of pixel data (stride).
|
||||||
|
*/
|
||||||
|
int stride() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clears the bitmap with the specified color.
|
||||||
|
* @param The color value in 0xRRGGBBAA format.
|
||||||
|
*/
|
||||||
|
void clear(uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the bitmap pixel data from ARGB32 Premultiplied to RGBA Plain format in place.
|
||||||
|
*/
|
||||||
|
void convertToRGBA();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the bitmap is null.
|
||||||
|
* @return True if the bitmap is null, false otherwise.
|
||||||
|
*/
|
||||||
|
bool isNull() const { return m_surface == nullptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the bitmap is valid.
|
||||||
|
* @deprecated This function has been deprecated. Use `isNull()` instead to check whether the bitmap is null.
|
||||||
|
* @return True if the bitmap is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
bool valid() const { return !isNull(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes the bitmap to a PNG file.
|
||||||
|
* @param filename The name of the file to write.
|
||||||
|
* @return True if the file was written successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
bool writeToPng(const std::string& filename) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes the bitmap to a PNG stream.
|
||||||
|
* @param callback Callback function for writing data.
|
||||||
|
* @param closure User-defined data passed to the callback.
|
||||||
|
* @return True if successful, false otherwise.
|
||||||
|
*/
|
||||||
|
bool writeToPng(lunasvg_write_func_t callback, void* closure) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
plutovg_surface_t* surface() const { return m_surface; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
plutovg_surface_t* release();
|
||||||
|
plutovg_surface_t* m_surface{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class Rect;
|
||||||
|
class Matrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a 2D axis-aligned bounding box.
|
||||||
|
*/
|
||||||
|
class LUNASVG_API Box {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a box with zero dimensions.
|
||||||
|
*/
|
||||||
|
Box() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a box with the specified position and size.
|
||||||
|
* @param x The x-coordinate of the box's origin.
|
||||||
|
* @param y The y-coordinate of the box's origin.
|
||||||
|
* @param w The width of the box.
|
||||||
|
* @param h The height of the box.
|
||||||
|
*/
|
||||||
|
Box(float x, float y, float w, float h);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
Box(const Rect& rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transforms the box using the specified matrix.
|
||||||
|
* @param matrix The transformation matrix.
|
||||||
|
* @return A reference to this box, modified by the transformation.
|
||||||
|
*/
|
||||||
|
Box& transform(const Matrix& matrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a new box transformed by the specified matrix.
|
||||||
|
* @param matrix The transformation matrix.
|
||||||
|
* @return A new box, transformed by the matrix.
|
||||||
|
*/
|
||||||
|
Box transformed(const Matrix& matrix) const;
|
||||||
|
|
||||||
|
float x{0}; ///< The x-coordinate of the box's origin.
|
||||||
|
float y{0}; ///< The y-coordinate of the box's origin.
|
||||||
|
float w{0}; ///< The width of the box.
|
||||||
|
float h{0}; ///< The height of the box.
|
||||||
|
};
|
||||||
|
|
||||||
|
class Transform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a 2D transformation matrix.
|
||||||
|
*/
|
||||||
|
class LUNASVG_API Matrix {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initializes the matrix to the identity matrix.
|
||||||
|
*/
|
||||||
|
Matrix() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a matrix with the specified values.
|
||||||
|
* @param a The horizontal scaling factor.
|
||||||
|
* @param b The vertical shearing factor.
|
||||||
|
* @param c The horizontal shearing factor.
|
||||||
|
* @param d The vertical scaling factor.
|
||||||
|
* @param e The horizontal translation offset.
|
||||||
|
* @param f The vertical translation offset.
|
||||||
|
*/
|
||||||
|
Matrix(float a, float b, float c, float d, float e, float f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
Matrix(const plutovg_matrix_t& matrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
Matrix(const Transform& transform);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies this matrix with another matrix.
|
||||||
|
* @param matrix The matrix to multiply with.
|
||||||
|
* @return A new matrix that is the result of the multiplication.
|
||||||
|
*/
|
||||||
|
Matrix operator*(const Matrix& matrix) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies this matrix with another matrix in place.
|
||||||
|
* @param matrix The matrix to multiply with.
|
||||||
|
* @return A reference to this matrix after multiplication.
|
||||||
|
*/
|
||||||
|
Matrix& operator*=(const Matrix& matrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Multiplies this matrix with another matrix.
|
||||||
|
* @param matrix The matrix to multiply with.
|
||||||
|
* @return A reference to this matrix after multiplication.
|
||||||
|
*/
|
||||||
|
Matrix& multiply(const Matrix& matrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translates this matrix by the specified offsets.
|
||||||
|
* @param tx The horizontal translation offset.
|
||||||
|
* @param ty The vertical translation offset.
|
||||||
|
* @return A reference to this matrix after translation.
|
||||||
|
*/
|
||||||
|
Matrix& translate(float tx, float ty);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scales this matrix by the specified factors.
|
||||||
|
* @param sx The horizontal scaling factor.
|
||||||
|
* @param sy The vertical scaling factor.
|
||||||
|
* @return A reference to this matrix after scaling.
|
||||||
|
*/
|
||||||
|
Matrix& scale(float sx, float sy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotates this matrix by the specified angle around a point.
|
||||||
|
* @param angle The rotation angle in degrees.
|
||||||
|
* @param cx The x-coordinate of the center of rotation.
|
||||||
|
* @param cy The y-coordinate of the center of rotation.
|
||||||
|
* @return A reference to this matrix after rotation.
|
||||||
|
*/
|
||||||
|
Matrix& rotate(float angle, float cx = 0.f, float cy = 0.f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shears this matrix by the specified factors.
|
||||||
|
* @param shx The horizontal shearing factor.
|
||||||
|
* @param shy The vertical shearing factor.
|
||||||
|
* @return A reference to this matrix after shearing.
|
||||||
|
*/
|
||||||
|
Matrix& shear(float shx, float shy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inverts this matrix.
|
||||||
|
* @return A reference to this matrix after inversion.
|
||||||
|
*/
|
||||||
|
Matrix& invert();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the inverse of this matrix.
|
||||||
|
* @return A new matrix that is the inverse of this matrix.
|
||||||
|
*/
|
||||||
|
Matrix inverse() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resets this matrix to the identity matrix.
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a translation matrix with the specified offsets.
|
||||||
|
* @param tx The horizontal translation offset.
|
||||||
|
* @param ty The vertical translation offset.
|
||||||
|
* @return A new translation matrix.
|
||||||
|
*/
|
||||||
|
static Matrix translated(float tx, float ty);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a scaling matrix with the specified factors.
|
||||||
|
* @param sx The horizontal scaling factor.
|
||||||
|
* @param sy The vertical scaling factor.
|
||||||
|
* @return A new scaling matrix.
|
||||||
|
*/
|
||||||
|
static Matrix scaled(float sx, float sy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a rotation matrix with the specified angle around a point.
|
||||||
|
* @param angle The rotation angle in degrees.
|
||||||
|
* @param cx The x-coordinate of the center of rotation.
|
||||||
|
* @param cy The y-coordinate of the center of rotation.
|
||||||
|
* @return A new rotation matrix.
|
||||||
|
*/
|
||||||
|
static Matrix rotated(float angle, float cx = 0.f, float cy = 0.f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a shearing matrix with the specified factors.
|
||||||
|
* @param shx The horizontal shearing factor.
|
||||||
|
* @param shy The vertical shearing factor.
|
||||||
|
* @return A new shearing matrix.
|
||||||
|
*/
|
||||||
|
static Matrix sheared(float shx, float shy);
|
||||||
|
|
||||||
|
float a{1}; ///< The horizontal scaling factor.
|
||||||
|
float b{0}; ///< The vertical shearing factor.
|
||||||
|
float c{0}; ///< The horizontal shearing factor.
|
||||||
|
float d{1}; ///< The vertical scaling factor.
|
||||||
|
float e{0}; ///< The horizontal translation offset.
|
||||||
|
float f{0}; ///< The vertical translation offset.
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGNode;
|
||||||
|
class SVGTextNode;
|
||||||
|
class SVGElement;
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
class TextNode;
|
||||||
|
|
||||||
|
class LUNASVG_API Node {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a null node.
|
||||||
|
*/
|
||||||
|
Node() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the node is a text node.
|
||||||
|
* @return True if the node is a text node, false otherwise.
|
||||||
|
*/
|
||||||
|
bool isTextNode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the node is an element node.
|
||||||
|
* @return True if the node is an element node, false otherwise.
|
||||||
|
*/
|
||||||
|
bool isElement() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the node to a TextNode.
|
||||||
|
* @return A TextNode or a null node if conversion is not possible.
|
||||||
|
*/
|
||||||
|
TextNode toTextNode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts the node to an Element.
|
||||||
|
* @return An Element or a null node if conversion is not possible.
|
||||||
|
*/
|
||||||
|
Element toElement() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the parent element.
|
||||||
|
* @return The parent element of this node. If this node has no parent, a null `Element` is returned.
|
||||||
|
*/
|
||||||
|
Element parentElement() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the node is null.
|
||||||
|
* @return True if the node is null, false otherwise.
|
||||||
|
*/
|
||||||
|
bool isNull() const { return m_node == nullptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the node is not null.
|
||||||
|
* @return True if the node is not null, false otherwise.
|
||||||
|
*/
|
||||||
|
operator bool() const { return !isNull(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if two nodes are equal.
|
||||||
|
* @param element The node to compare.
|
||||||
|
* @return True if equal, otherwise false.
|
||||||
|
*/
|
||||||
|
bool operator==(const Node& node) const { return m_node == node.m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if two nodes are not equal.
|
||||||
|
* @param element The node to compare.
|
||||||
|
* @return True if not equal, otherwise false.
|
||||||
|
*/
|
||||||
|
bool operator!=(const Node& node) const { return m_node != node.m_node; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Node(SVGNode* node);
|
||||||
|
SVGNode* node() const { return m_node; }
|
||||||
|
SVGNode* m_node{nullptr};
|
||||||
|
friend class Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NodeList = std::vector<Node>;
|
||||||
|
|
||||||
|
class LUNASVG_API TextNode : public Node {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a null text node.
|
||||||
|
*/
|
||||||
|
TextNode() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the text content of the node.
|
||||||
|
* @return A string representing the text content.
|
||||||
|
*/
|
||||||
|
const std::string& data() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the text content of the node.
|
||||||
|
* @param data The new text content to set.
|
||||||
|
*/
|
||||||
|
void setData(const std::string& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextNode(SVGTextNode* text);
|
||||||
|
SVGTextNode* text() const;
|
||||||
|
friend class Node;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LUNASVG_API Element : public Node {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a null element.
|
||||||
|
*/
|
||||||
|
Element() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the element has a specific attribute.
|
||||||
|
* @param name The name of the attribute to check.
|
||||||
|
* @return True if the element has the specified attribute, false otherwise.
|
||||||
|
*/
|
||||||
|
bool hasAttribute(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the value of an attribute.
|
||||||
|
* @param name The name of the attribute to retrieve.
|
||||||
|
* @return The value of the attribute as a string.
|
||||||
|
*/
|
||||||
|
const std::string& getAttribute(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the value of an attribute.
|
||||||
|
* @param name The name of the attribute to set.
|
||||||
|
* @param value The value to assign to the attribute.
|
||||||
|
*/
|
||||||
|
void setAttribute(const std::string& name, const std::string& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renders the element onto a bitmap using a transformation matrix.
|
||||||
|
* @param bitmap The bitmap to render onto.
|
||||||
|
* @param The root transformation matrix.
|
||||||
|
*/
|
||||||
|
void render(Bitmap& bitmap, const Matrix& matrix = Matrix()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renders the element to a bitmap with specified dimensions.
|
||||||
|
* @param width The desired width in pixels, or -1 to auto-scale based on the intrinsic size.
|
||||||
|
* @param height The desired height in pixels, or -1 to auto-scale based on the intrinsic size.
|
||||||
|
* @param backgroundColor The background color in 0xRRGGBBAA format.
|
||||||
|
* @return A Bitmap containing the raster representation of the element.
|
||||||
|
*/
|
||||||
|
Bitmap renderToBitmap(int width = -1, int height = -1, uint32_t backgroundColor = 0x00000000) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the local transformation matrix of the element.
|
||||||
|
* @return The matrix that applies only to the element, relative to its parent.
|
||||||
|
*/
|
||||||
|
Matrix getLocalMatrix() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the global transformation matrix of the element.
|
||||||
|
* @return The matrix combining the element's local and all parent transformations.
|
||||||
|
*/
|
||||||
|
Matrix getGlobalMatrix() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the local bounding box of the element.
|
||||||
|
* @return A Box representing the bounding box after applying local transformations.
|
||||||
|
*/
|
||||||
|
Box getLocalBoundingBox() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the global bounding box of the element.
|
||||||
|
* @return A Box representing the bounding box after applying global transformations.
|
||||||
|
*/
|
||||||
|
Box getGlobalBoundingBox() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the bounding box of the element without any transformations.
|
||||||
|
* @return A Box representing the bounding box of the element without any transformations applied.
|
||||||
|
*/
|
||||||
|
Box getBoundingBox() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the child nodes of this node.
|
||||||
|
* @return A NodeList containing the child nodes.
|
||||||
|
*/
|
||||||
|
NodeList children() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element(SVGElement* element);
|
||||||
|
SVGElement* element(bool layoutIfNeeded = false) const;
|
||||||
|
friend class Node;
|
||||||
|
friend class Document;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ElementList = std::vector<Element>;
|
||||||
|
|
||||||
|
class SVGRootElement;
|
||||||
|
|
||||||
|
class LUNASVG_API Document {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Load an SVG document from a file.
|
||||||
|
* @param filename The path to the SVG file.
|
||||||
|
* @return A pointer to the loaded `Document`, or `nullptr` on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Document> loadFromFile(const std::string& filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load an SVG document from a string.
|
||||||
|
* @param string The SVG data as a string.
|
||||||
|
* @return A pointer to the loaded `Document`, or `nullptr` on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Document> loadFromData(const std::string& string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load an SVG document from a null-terminated string.
|
||||||
|
* @param data The string containing the SVG data.
|
||||||
|
* @return A pointer to the loaded `Document`, or `nullptr` on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Document> loadFromData(const char* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load an SVG document from a string with a specified length.
|
||||||
|
* @param data The string containing the SVG data.
|
||||||
|
* @param length The length of the string in bytes.
|
||||||
|
* @return A pointer to the loaded `Document`, or `nullptr` on failure.
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Document> loadFromData(const char* data, size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Applies a CSS stylesheet to the document.
|
||||||
|
* @param content A string containing the CSS rules to apply, with comments removed.
|
||||||
|
*/
|
||||||
|
void applyStyleSheet(const std::string& content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Selects all elements that match the given CSS selector(s).
|
||||||
|
* @param content A string containing the CSS selector(s) to match elements.
|
||||||
|
* @return A list of elements matching the selector(s).
|
||||||
|
*/
|
||||||
|
ElementList querySelectorAll(const std::string& content) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the intrinsic width of the document in pixels.
|
||||||
|
* @return The width of the document.
|
||||||
|
*/
|
||||||
|
float width() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the intrinsic height of the document in pixels.
|
||||||
|
* @return The height of the document.
|
||||||
|
*/
|
||||||
|
float height() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the smallest rectangle that encloses the document content.
|
||||||
|
* @return A Box representing the bounding box of the document.
|
||||||
|
*/
|
||||||
|
Box boundingBox() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the layout of the document if needed.
|
||||||
|
*/
|
||||||
|
void updateLayout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Forces an immediate layout update.
|
||||||
|
*/
|
||||||
|
void forceLayout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renders the document onto a bitmap using a transformation matrix.
|
||||||
|
* @param bitmap The bitmap to render onto.
|
||||||
|
* @param The root transformation matrix.
|
||||||
|
*/
|
||||||
|
void render(Bitmap& bitmap, const Matrix& matrix = Matrix()) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renders the document to a bitmap with specified dimensions.
|
||||||
|
* @param width The desired width in pixels, or -1 to auto-scale based on the intrinsic size.
|
||||||
|
* @param height The desired height in pixels, or -1 to auto-scale based on the intrinsic size.
|
||||||
|
* @param backgroundColor The background color in 0xRRGGBBAA format.
|
||||||
|
* @return A Bitmap containing the raster representation of the document.
|
||||||
|
*/
|
||||||
|
Bitmap renderToBitmap(int width = -1, int height = -1, uint32_t backgroundColor = 0x00000000) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the topmost element under the specified point.
|
||||||
|
* @param x The x-coordinate in viewport space.
|
||||||
|
* @param y The y-coordinate in viewport space.
|
||||||
|
* @return The topmost Element at the given point, or a null `Element` if no match is found.
|
||||||
|
*/
|
||||||
|
Element elementFromPoint(float x, float y) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves an element by its ID.
|
||||||
|
* @param id The ID of the element to retrieve.
|
||||||
|
* @return The Element with the specified ID, or a null `Element` if not found.
|
||||||
|
*/
|
||||||
|
Element getElementById(const std::string& id) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves the document element.
|
||||||
|
* @return The root Element of the document.
|
||||||
|
*/
|
||||||
|
Element documentElement() const;
|
||||||
|
|
||||||
|
Document(Document&&);
|
||||||
|
Document& operator=(Document&&);
|
||||||
|
~Document();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Document();
|
||||||
|
Document(const Document&) = delete;
|
||||||
|
Document& operator=(const Document&) = delete;
|
||||||
|
SVGRootElement* rootElement(bool layoutIfNeeded = false) const;
|
||||||
|
bool parse(const char* data, size_t length);
|
||||||
|
std::unique_ptr<SVGRootElement> m_rootElement;
|
||||||
|
friend class SVGURIReference;
|
||||||
|
friend class SVGNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_H
|
||||||
2548
vendor/lunasvg/plutovg/include/plutovg.h
vendored
Normal file
2548
vendor/lunasvg/plutovg/include/plutovg.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1144
vendor/lunasvg/plutovg/source/plutovg-blend.c
vendored
Normal file
1144
vendor/lunasvg/plutovg/source/plutovg-blend.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
759
vendor/lunasvg/plutovg/source/plutovg-canvas.c
vendored
Normal file
759
vendor/lunasvg/plutovg/source/plutovg-canvas.c
vendored
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
#include "plutovg-private.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
int plutovg_version(void)
|
||||||
|
{
|
||||||
|
return PLUTOVG_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* plutovg_version_string(void)
|
||||||
|
{
|
||||||
|
return PLUTOVG_VERSION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PLUTOVG_DEFAULT_STROKE_STYLE ((plutovg_stroke_style_t){1.f, PLUTOVG_LINE_CAP_BUTT, PLUTOVG_LINE_JOIN_MITER, 10.f})
|
||||||
|
|
||||||
|
static plutovg_state_t* plutovg_state_create(void)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = malloc(sizeof(plutovg_state_t));
|
||||||
|
state->paint = NULL;
|
||||||
|
state->font_face = NULL;
|
||||||
|
state->color = PLUTOVG_BLACK_COLOR;
|
||||||
|
state->matrix = PLUTOVG_IDENTITY_MATRIX;
|
||||||
|
state->stroke.style = PLUTOVG_DEFAULT_STROKE_STYLE;
|
||||||
|
state->stroke.dash.offset = 0.f;
|
||||||
|
plutovg_array_init(state->stroke.dash.array);
|
||||||
|
plutovg_span_buffer_init(&state->clip_spans);
|
||||||
|
state->winding = PLUTOVG_FILL_RULE_NON_ZERO;
|
||||||
|
state->op = PLUTOVG_OPERATOR_SRC_OVER;
|
||||||
|
state->font_size = 12.f;
|
||||||
|
state->opacity = 1.f;
|
||||||
|
state->clipping = false;
|
||||||
|
state->next = NULL;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_state_reset(plutovg_state_t* state)
|
||||||
|
{
|
||||||
|
plutovg_paint_destroy(state->paint);
|
||||||
|
plutovg_font_face_destroy(state->font_face);
|
||||||
|
state->paint = NULL;
|
||||||
|
state->font_face = NULL;
|
||||||
|
state->color = PLUTOVG_BLACK_COLOR;
|
||||||
|
state->matrix = PLUTOVG_IDENTITY_MATRIX;
|
||||||
|
state->stroke.style = PLUTOVG_DEFAULT_STROKE_STYLE;
|
||||||
|
state->stroke.dash.offset = 0.f;
|
||||||
|
plutovg_array_clear(state->stroke.dash.array);
|
||||||
|
plutovg_span_buffer_reset(&state->clip_spans);
|
||||||
|
state->winding = PLUTOVG_FILL_RULE_NON_ZERO;
|
||||||
|
state->op = PLUTOVG_OPERATOR_SRC_OVER;
|
||||||
|
state->font_size = 12.f;
|
||||||
|
state->opacity = 1.f;
|
||||||
|
state->clipping = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_state_copy(plutovg_state_t* state, const plutovg_state_t* source)
|
||||||
|
{
|
||||||
|
state->paint = plutovg_paint_reference(source->paint);
|
||||||
|
state->font_face = plutovg_font_face_reference(source->font_face);
|
||||||
|
state->color = source->color;
|
||||||
|
state->matrix = source->matrix;
|
||||||
|
state->stroke.style = source->stroke.style;
|
||||||
|
state->stroke.dash.offset = source->stroke.dash.offset;
|
||||||
|
plutovg_array_clear(state->stroke.dash.array);
|
||||||
|
plutovg_array_append(state->stroke.dash.array, source->stroke.dash.array);
|
||||||
|
plutovg_span_buffer_copy(&state->clip_spans, &source->clip_spans);
|
||||||
|
state->winding = source->winding;
|
||||||
|
state->op = source->op;
|
||||||
|
state->font_size = source->font_size;
|
||||||
|
state->opacity = source->opacity;
|
||||||
|
state->clipping = source->clipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_state_destroy(plutovg_state_t* state)
|
||||||
|
{
|
||||||
|
plutovg_paint_destroy(state->paint);
|
||||||
|
plutovg_font_face_destroy(state->font_face);
|
||||||
|
plutovg_array_destroy(state->stroke.dash.array);
|
||||||
|
plutovg_span_buffer_destroy(&state->clip_spans);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_canvas_t* plutovg_canvas_create(plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
plutovg_canvas_t* canvas = malloc(sizeof(plutovg_canvas_t));
|
||||||
|
plutovg_init_reference(canvas);
|
||||||
|
canvas->surface = plutovg_surface_reference(surface);
|
||||||
|
canvas->path = plutovg_path_create();
|
||||||
|
canvas->state = plutovg_state_create();
|
||||||
|
canvas->freed_state = NULL;
|
||||||
|
canvas->face_cache = NULL;
|
||||||
|
canvas->clip_rect = PLUTOVG_MAKE_RECT(0, 0, surface->width, surface->height);
|
||||||
|
plutovg_span_buffer_init(&canvas->clip_spans);
|
||||||
|
plutovg_span_buffer_init(&canvas->fill_spans);
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_canvas_t* plutovg_canvas_reference(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_increment_reference(canvas);
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_destroy(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
if(plutovg_destroy_reference(canvas)) {
|
||||||
|
while(canvas->state) {
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
canvas->state = state->next;
|
||||||
|
plutovg_state_destroy(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(canvas->freed_state) {
|
||||||
|
plutovg_state_t* state = canvas->freed_state;
|
||||||
|
canvas->freed_state = state->next;
|
||||||
|
plutovg_state_destroy(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_font_face_cache_destroy(canvas->face_cache);
|
||||||
|
plutovg_span_buffer_destroy(&canvas->fill_spans);
|
||||||
|
plutovg_span_buffer_destroy(&canvas->clip_spans);
|
||||||
|
plutovg_surface_destroy(canvas->surface);
|
||||||
|
plutovg_path_destroy(canvas->path);
|
||||||
|
free(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_canvas_get_reference_count(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return plutovg_get_reference_count(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_canvas_get_surface(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_save(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_state_t* new_state = canvas->freed_state;
|
||||||
|
if(new_state == NULL)
|
||||||
|
new_state = plutovg_state_create();
|
||||||
|
else
|
||||||
|
canvas->freed_state = new_state->next;
|
||||||
|
plutovg_state_copy(new_state, canvas->state);
|
||||||
|
new_state->next = canvas->state;
|
||||||
|
canvas->state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_restore(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
if(canvas->state->next == NULL)
|
||||||
|
return;
|
||||||
|
plutovg_state_t* old_state = canvas->state;
|
||||||
|
canvas->state = old_state->next;
|
||||||
|
plutovg_state_reset(old_state);
|
||||||
|
old_state->next = canvas->freed_state;
|
||||||
|
canvas->freed_state = old_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_rgb(plutovg_canvas_t* canvas, float r, float g, float b)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_rgba(canvas, r, g, b, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_rgba(plutovg_canvas_t* canvas, float r, float g, float b, float a)
|
||||||
|
{
|
||||||
|
plutovg_color_init_rgba(&canvas->state->color, r, g, b, a);
|
||||||
|
plutovg_canvas_set_paint(canvas, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_color(plutovg_canvas_t* canvas, const plutovg_color_t* color)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_rgba(canvas, color->r, color->g, color->b, color->a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_linear_gradient(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_paint_t* paint = plutovg_paint_create_linear_gradient(x1, y1, x2, y2, spread, stops, nstops, matrix);
|
||||||
|
plutovg_canvas_set_paint(canvas, paint);
|
||||||
|
plutovg_paint_destroy(paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_radial_gradient(plutovg_canvas_t* canvas, float cx, float cy, float cr, float fx, float fy, float fr, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_paint_t* paint = plutovg_paint_create_radial_gradient(cx, cy, cr, fx, fy, fr, spread, stops, nstops, matrix);
|
||||||
|
plutovg_canvas_set_paint(canvas, paint);
|
||||||
|
plutovg_paint_destroy(paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_texture(plutovg_canvas_t* canvas, plutovg_surface_t* surface, plutovg_texture_type_t type, float opacity, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_paint_t* paint = plutovg_paint_create_texture(surface, type, opacity, matrix);
|
||||||
|
plutovg_canvas_set_paint(canvas, paint);
|
||||||
|
plutovg_paint_destroy(paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_paint(plutovg_canvas_t* canvas, plutovg_paint_t* paint)
|
||||||
|
{
|
||||||
|
paint = plutovg_paint_reference(paint);
|
||||||
|
plutovg_paint_destroy(canvas->state->paint);
|
||||||
|
canvas->state->paint = paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_canvas_get_paint(const plutovg_canvas_t* canvas, plutovg_color_t* color)
|
||||||
|
{
|
||||||
|
if(color)
|
||||||
|
*color = canvas->state->color;
|
||||||
|
return canvas->state->paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_font_face_cache(plutovg_canvas_t* canvas, plutovg_font_face_cache_t* cache)
|
||||||
|
{
|
||||||
|
cache = plutovg_font_face_cache_reference(cache);
|
||||||
|
plutovg_font_face_cache_destroy(canvas->face_cache);
|
||||||
|
canvas->face_cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_font_face_cache_t* plutovg_canvas_get_font_face_cache(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->face_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_add_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, plutovg_font_face_t* face)
|
||||||
|
{
|
||||||
|
if(canvas->face_cache == NULL)
|
||||||
|
canvas->face_cache = plutovg_font_face_cache_create();
|
||||||
|
plutovg_font_face_cache_add(canvas->face_cache, family, bold, italic, face);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_canvas_add_font_file(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, const char* filename, int ttcindex)
|
||||||
|
{
|
||||||
|
if(canvas->face_cache == NULL)
|
||||||
|
canvas->face_cache = plutovg_font_face_cache_create();
|
||||||
|
return plutovg_font_face_cache_add_file(canvas->face_cache, family, bold, italic, filename, ttcindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_canvas_select_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic)
|
||||||
|
{
|
||||||
|
if(canvas->face_cache == NULL)
|
||||||
|
return false;
|
||||||
|
plutovg_font_face_t* face = plutovg_font_face_cache_get(canvas->face_cache, family, bold, italic);
|
||||||
|
if(face == NULL)
|
||||||
|
return false;
|
||||||
|
plutovg_canvas_set_font_face(canvas, face);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_font(plutovg_canvas_t* canvas, plutovg_font_face_t* face, float size)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_font_face(canvas, face);
|
||||||
|
plutovg_canvas_set_font_size(canvas, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_font_face(plutovg_canvas_t* canvas, plutovg_font_face_t* face)
|
||||||
|
{
|
||||||
|
face = plutovg_font_face_reference(face);
|
||||||
|
plutovg_font_face_destroy(canvas->state->font_face);
|
||||||
|
canvas->state->font_face = face;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_font_face_t* plutovg_canvas_get_font_face(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->font_face;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_font_size(plutovg_canvas_t* canvas, float size)
|
||||||
|
{
|
||||||
|
canvas->state->font_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_get_font_size(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->font_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_fill_rule(plutovg_canvas_t* canvas, plutovg_fill_rule_t winding)
|
||||||
|
{
|
||||||
|
canvas->state->winding = winding;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_fill_rule_t plutovg_canvas_get_fill_rule(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->winding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_operator(plutovg_canvas_t* canvas, plutovg_operator_t op)
|
||||||
|
{
|
||||||
|
canvas->state->op = op;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_operator_t plutovg_canvas_get_operator(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->op;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_opacity(plutovg_canvas_t* canvas, float opacity)
|
||||||
|
{
|
||||||
|
canvas->state->opacity = plutovg_clamp(opacity, 0.f, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_get_opacity(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_line_width(plutovg_canvas_t* canvas, float line_width)
|
||||||
|
{
|
||||||
|
canvas->state->stroke.style.width = line_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_get_line_width(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->stroke.style.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_line_cap(plutovg_canvas_t* canvas, plutovg_line_cap_t line_cap)
|
||||||
|
{
|
||||||
|
canvas->state->stroke.style.cap = line_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_line_cap_t plutovg_canvas_get_line_cap(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->stroke.style.cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_line_join(plutovg_canvas_t* canvas, plutovg_line_join_t line_join)
|
||||||
|
{
|
||||||
|
canvas->state->stroke.style.join = line_join;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_line_join_t plutovg_canvas_get_line_join(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->stroke.style.join;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_miter_limit(plutovg_canvas_t* canvas, float miter_limit)
|
||||||
|
{
|
||||||
|
canvas->state->stroke.style.miter_limit = miter_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_get_miter_limit(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->stroke.style.miter_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_dash(plutovg_canvas_t* canvas, float offset, const float* dashes, int ndashes)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_dash_offset(canvas, offset);
|
||||||
|
plutovg_canvas_set_dash_array(canvas, dashes, ndashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_dash_offset(plutovg_canvas_t* canvas, float offset)
|
||||||
|
{
|
||||||
|
canvas->state->stroke.dash.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_get_dash_offset(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->state->stroke.dash.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_dash_array(plutovg_canvas_t* canvas, const float* dashes, int ndashes)
|
||||||
|
{
|
||||||
|
plutovg_array_clear(canvas->state->stroke.dash.array);
|
||||||
|
plutovg_array_append_data(canvas->state->stroke.dash.array, dashes, ndashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_canvas_get_dash_array(const plutovg_canvas_t* canvas, const float** dashes)
|
||||||
|
{
|
||||||
|
if(dashes)
|
||||||
|
*dashes = canvas->state->stroke.dash.array.data;
|
||||||
|
return canvas->state->stroke.dash.array.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_translate(plutovg_canvas_t* canvas, float tx, float ty)
|
||||||
|
{
|
||||||
|
plutovg_matrix_translate(&canvas->state->matrix, tx, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_scale(plutovg_canvas_t* canvas, float sx, float sy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_scale(&canvas->state->matrix, sx, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_shear(plutovg_canvas_t* canvas, float shx, float shy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_shear(&canvas->state->matrix, shx, shy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_rotate(plutovg_canvas_t* canvas, float angle)
|
||||||
|
{
|
||||||
|
plutovg_matrix_rotate(&canvas->state->matrix, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_transform(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_matrix_multiply(&canvas->state->matrix, matrix, &canvas->state->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_reset_matrix(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init_identity(&canvas->state->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_set_matrix(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
canvas->state->matrix = matrix ? *matrix : PLUTOVG_IDENTITY_MATRIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_get_matrix(const plutovg_canvas_t* canvas, plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
*matrix = canvas->state->matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_map(const plutovg_canvas_t* canvas, float x, float y, float* xx, float* yy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_map(&canvas->state->matrix, x, y, xx, yy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_map_point(const plutovg_canvas_t* canvas, const plutovg_point_t* src, plutovg_point_t* dst)
|
||||||
|
{
|
||||||
|
plutovg_matrix_map_point(&canvas->state->matrix, src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_map_rect(const plutovg_canvas_t* canvas, const plutovg_rect_t* src, plutovg_rect_t* dst)
|
||||||
|
{
|
||||||
|
plutovg_matrix_map_rect(&canvas->state->matrix, src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_move_to(plutovg_canvas_t* canvas, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_move_to(canvas->path, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_line_to(plutovg_canvas_t* canvas, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_line_to(canvas->path, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_quad_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2)
|
||||||
|
{
|
||||||
|
plutovg_path_quad_to(canvas->path, x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_cubic_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2, float x3, float y3)
|
||||||
|
{
|
||||||
|
plutovg_path_cubic_to(canvas->path, x1, y1, x2, y2, x3, y3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_arc_to(plutovg_canvas_t* canvas, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_arc_to(canvas->path, rx, ry, angle, large_arc_flag, sweep_flag, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_path_add_rect(canvas->path, x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_round_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h, float rx, float ry)
|
||||||
|
{
|
||||||
|
plutovg_path_add_round_rect(canvas->path, x, y, w, h, rx, ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_ellipse(plutovg_canvas_t* canvas, float cx, float cy, float rx, float ry)
|
||||||
|
{
|
||||||
|
plutovg_path_add_ellipse(canvas->path, cx, cy, rx, ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_circle(plutovg_canvas_t* canvas, float cx, float cy, float r)
|
||||||
|
{
|
||||||
|
plutovg_path_add_circle(canvas->path, cx, cy, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_arc(plutovg_canvas_t* canvas, float cx, float cy, float r, float a0, float a1, bool ccw)
|
||||||
|
{
|
||||||
|
plutovg_path_add_arc(canvas->path, cx, cy, r, a0, a1, ccw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_add_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_path_add_path(canvas->path, path, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_new_path(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_path_reset(canvas->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_close_path(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_path_close(canvas->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_get_current_point(const plutovg_canvas_t* canvas, float* x, float* y)
|
||||||
|
{
|
||||||
|
plutovg_path_get_current_point(canvas->path, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_canvas_get_path(const plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
return canvas->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_canvas_fill_contains(plutovg_canvas_t* canvas, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
|
||||||
|
return plutovg_span_buffer_contains(&canvas->fill_spans, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_canvas_stroke_contains(plutovg_canvas_t* canvas, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
|
||||||
|
return plutovg_span_buffer_contains(&canvas->fill_spans, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_canvas_clip_contains(plutovg_canvas_t* canvas, float x, float y)
|
||||||
|
{
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
return plutovg_span_buffer_contains(&canvas->state->clip_spans, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float l = canvas->clip_rect.x;
|
||||||
|
float t = canvas->clip_rect.y;
|
||||||
|
float r = canvas->clip_rect.x + canvas->clip_rect.w;
|
||||||
|
float b = canvas->clip_rect.y + canvas->clip_rect.h;
|
||||||
|
|
||||||
|
return x >= l && x <= r && y >= t && y <= b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_fill_extents(plutovg_canvas_t *canvas, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
|
||||||
|
plutovg_span_buffer_extents(&canvas->fill_spans, extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_stroke_extents(plutovg_canvas_t *canvas, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, &canvas->state->stroke, PLUTOVG_FILL_RULE_NON_ZERO);
|
||||||
|
plutovg_span_buffer_extents(&canvas->fill_spans, extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_clip_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
plutovg_span_buffer_extents(&canvas->state->clip_spans, extents);
|
||||||
|
} else {
|
||||||
|
extents->x = canvas->clip_rect.x;
|
||||||
|
extents->y = canvas->clip_rect.y;
|
||||||
|
extents->w = canvas->clip_rect.w;
|
||||||
|
extents->h = canvas->clip_rect.h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_fill(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_canvas_fill_preserve(canvas);
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_stroke(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_canvas_stroke_preserve(canvas);
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_clip(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_canvas_clip_preserve(canvas);
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_paint(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
plutovg_blend(canvas, &canvas->state->clip_spans);
|
||||||
|
} else {
|
||||||
|
plutovg_span_buffer_init_rect(&canvas->clip_spans, 0, 0, canvas->surface->width, canvas->surface->height);
|
||||||
|
plutovg_blend(canvas, &canvas->clip_spans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_fill_preserve(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
|
||||||
|
plutovg_blend(canvas, &canvas->clip_spans);
|
||||||
|
} else {
|
||||||
|
plutovg_blend(canvas, &canvas->fill_spans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_stroke_preserve(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, &canvas->state->stroke, PLUTOVG_FILL_RULE_NON_ZERO);
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
|
||||||
|
plutovg_blend(canvas, &canvas->clip_spans);
|
||||||
|
} else {
|
||||||
|
plutovg_blend(canvas, &canvas->fill_spans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_clip_preserve(plutovg_canvas_t* canvas)
|
||||||
|
{
|
||||||
|
if(canvas->state->clipping) {
|
||||||
|
plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
|
||||||
|
plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
|
||||||
|
plutovg_span_buffer_copy(&canvas->state->clip_spans, &canvas->clip_spans);
|
||||||
|
} else {
|
||||||
|
plutovg_rasterize(&canvas->state->clip_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
|
||||||
|
canvas->state->clipping = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_fill_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_rect(canvas, x, y, w, h);
|
||||||
|
plutovg_canvas_fill(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_fill_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_add_path(canvas, path);
|
||||||
|
plutovg_canvas_fill(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_stroke_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_rect(canvas, x, y, w, h);
|
||||||
|
plutovg_canvas_stroke(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_stroke_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_add_path(canvas, path);
|
||||||
|
plutovg_canvas_stroke(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_clip_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_rect(canvas, x, y, w, h);
|
||||||
|
plutovg_canvas_clip(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_clip_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
plutovg_canvas_add_path(canvas, path);
|
||||||
|
plutovg_canvas_clip(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_add_glyph(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
if(state->font_face && state->font_size > 0.f)
|
||||||
|
return plutovg_font_face_get_glyph_path(state->font_face, state->font_size, x, y, codepoint, canvas->path);
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_add_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
if(state->font_face == NULL || state->font_size <= 0.f)
|
||||||
|
return 0.f;
|
||||||
|
plutovg_text_iterator_t it;
|
||||||
|
plutovg_text_iterator_init(&it, text, length, encoding);
|
||||||
|
float advance_width = 0.f;
|
||||||
|
while(plutovg_text_iterator_has_next(&it)) {
|
||||||
|
plutovg_codepoint_t codepoint = plutovg_text_iterator_next(&it);
|
||||||
|
advance_width += plutovg_font_face_get_glyph_path(state->font_face, state->font_size, x + advance_width, y, codepoint, canvas->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return advance_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_fill_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
|
||||||
|
plutovg_canvas_fill(canvas);
|
||||||
|
return advance_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_stroke_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
|
||||||
|
plutovg_canvas_stroke(canvas);
|
||||||
|
return advance_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_clip_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_canvas_new_path(canvas);
|
||||||
|
float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
|
||||||
|
plutovg_canvas_clip(canvas);
|
||||||
|
return advance_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_font_metrics(const plutovg_canvas_t* canvas, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
if(state->font_face && state->font_size > 0.f) {
|
||||||
|
plutovg_font_face_get_metrics(state->font_face, state->font_size, ascent, descent, line_gap, extents);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ascent) *ascent = 0.f;
|
||||||
|
if(descent) *descent = 0.f;
|
||||||
|
if(line_gap) *line_gap = 0.f;
|
||||||
|
if(extents) {
|
||||||
|
extents->x = 0.f;
|
||||||
|
extents->y = 0.f;
|
||||||
|
extents->w = 0.f;
|
||||||
|
extents->h = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_canvas_glyph_metrics(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
if(state->font_face && state->font_size > 0.f) {
|
||||||
|
plutovg_font_face_get_glyph_metrics(state->font_face, state->font_size, codepoint, advance_width, left_side_bearing, extents);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(advance_width) *advance_width = 0.f;
|
||||||
|
if(left_side_bearing) *left_side_bearing = 0.f;
|
||||||
|
if(extents) {
|
||||||
|
extents->x = 0.f;
|
||||||
|
extents->y = 0.f;
|
||||||
|
extents->w = 0.f;
|
||||||
|
extents->h = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_canvas_text_extents(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_state_t* state = canvas->state;
|
||||||
|
if(state->font_face && state->font_size > 0.f) {
|
||||||
|
return plutovg_font_face_text_extents(state->font_face, state->font_size, text, length, encoding, extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extents) {
|
||||||
|
extents->x = 0.f;
|
||||||
|
extents->y = 0.f;
|
||||||
|
extents->w = 0.f;
|
||||||
|
extents->h = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
1065
vendor/lunasvg/plutovg/source/plutovg-font.c
vendored
Normal file
1065
vendor/lunasvg/plutovg/source/plutovg-font.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
441
vendor/lunasvg/plutovg/source/plutovg-ft-math.c
vendored
Normal file
441
vendor/lunasvg/plutovg/source/plutovg-ft-math.c
vendored
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
/***************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* 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, FTL.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 "plutovg-ft-math.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#include <intrin.h>
|
||||||
|
static inline int clz(unsigned int x) {
|
||||||
|
unsigned long r = 0;
|
||||||
|
if (_BitScanReverse(&r, x))
|
||||||
|
return 31 - r;
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
#define PVG_FT_MSB(x) (31 - clz(x))
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define PVG_FT_MSB(x) (31 - __builtin_clz(x))
|
||||||
|
#else
|
||||||
|
static inline int clz(unsigned int x) {
|
||||||
|
int n = 0;
|
||||||
|
if (x == 0) return 32;
|
||||||
|
if (x <= 0x0000FFFFU) { n += 16; x <<= 16; }
|
||||||
|
if (x <= 0x00FFFFFFU) { n += 8; x <<= 8; }
|
||||||
|
if (x <= 0x0FFFFFFFU) { n += 4; x <<= 4; }
|
||||||
|
if (x <= 0x3FFFFFFFU) { n += 2; x <<= 2; }
|
||||||
|
if (x <= 0x7FFFFFFFU) { n += 1; }
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
#define PVG_FT_MSB(x) (31 - clz(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PVG_FT_PAD_FLOOR(x, n) ((x) & ~((n)-1))
|
||||||
|
#define PVG_FT_PAD_ROUND(x, n) PVG_FT_PAD_FLOOR((x) + ((n) / 2), n)
|
||||||
|
#define PVG_FT_PAD_CEIL(x, n) PVG_FT_PAD_FLOOR((x) + ((n)-1), n)
|
||||||
|
|
||||||
|
/* transfer sign leaving a positive number */
|
||||||
|
#define PVG_FT_MOVE_SIGN(x, s) \
|
||||||
|
PVG_FT_BEGIN_STMNT \
|
||||||
|
if (x < 0) { \
|
||||||
|
x = -x; \
|
||||||
|
s = -s; \
|
||||||
|
} \
|
||||||
|
PVG_FT_END_STMNT
|
||||||
|
|
||||||
|
PVG_FT_Long PVG_FT_MulFix(PVG_FT_Long a, PVG_FT_Long b)
|
||||||
|
{
|
||||||
|
PVG_FT_Int s = 1;
|
||||||
|
PVG_FT_Long c;
|
||||||
|
|
||||||
|
PVG_FT_MOVE_SIGN(a, s);
|
||||||
|
PVG_FT_MOVE_SIGN(b, s);
|
||||||
|
|
||||||
|
c = (PVG_FT_Long)(((PVG_FT_Int64)a * b + 0x8000L) >> 16);
|
||||||
|
|
||||||
|
return (s > 0) ? c : -c;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVG_FT_Long PVG_FT_MulDiv(PVG_FT_Long a, PVG_FT_Long b, PVG_FT_Long c)
|
||||||
|
{
|
||||||
|
PVG_FT_Int s = 1;
|
||||||
|
PVG_FT_Long d;
|
||||||
|
|
||||||
|
PVG_FT_MOVE_SIGN(a, s);
|
||||||
|
PVG_FT_MOVE_SIGN(b, s);
|
||||||
|
PVG_FT_MOVE_SIGN(c, s);
|
||||||
|
|
||||||
|
d = (PVG_FT_Long)(c > 0 ? ((PVG_FT_Int64)a * b + (c >> 1)) / c : 0x7FFFFFFFL);
|
||||||
|
|
||||||
|
return (s > 0) ? d : -d;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVG_FT_Long PVG_FT_DivFix(PVG_FT_Long a, PVG_FT_Long b)
|
||||||
|
{
|
||||||
|
PVG_FT_Int s = 1;
|
||||||
|
PVG_FT_Long q;
|
||||||
|
|
||||||
|
PVG_FT_MOVE_SIGN(a, s);
|
||||||
|
PVG_FT_MOVE_SIGN(b, s);
|
||||||
|
|
||||||
|
q = (PVG_FT_Long)(b > 0 ? (((PVG_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 PVG_FT_TRIG_SCALE 0xDBD95B16UL
|
||||||
|
|
||||||
|
/* the highest bit in overflow-safe vector components, */
|
||||||
|
/* MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||||
|
#define PVG_FT_TRIG_SAFE_MSB 29
|
||||||
|
|
||||||
|
/* this table was generated for PVG_FT_PI = 180L << 16, i.e. degrees */
|
||||||
|
#define PVG_FT_TRIG_MAX_ITERS 23
|
||||||
|
|
||||||
|
static const PVG_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 PVG_FT_Fixed ft_trig_downscale(PVG_FT_Fixed val)
|
||||||
|
{
|
||||||
|
PVG_FT_Fixed s;
|
||||||
|
PVG_FT_Int64 v;
|
||||||
|
|
||||||
|
s = val;
|
||||||
|
val = PVG_FT_ABS(val);
|
||||||
|
|
||||||
|
v = (val * (PVG_FT_Int64)PVG_FT_TRIG_SCALE) + 0x100000000UL;
|
||||||
|
val = (PVG_FT_Fixed)(v >> 32);
|
||||||
|
|
||||||
|
return (s >= 0) ? val : -val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* undefined and never called for zero vector */
|
||||||
|
static PVG_FT_Int ft_trig_prenorm(PVG_FT_Vector* vec)
|
||||||
|
{
|
||||||
|
PVG_FT_Pos x, y;
|
||||||
|
PVG_FT_Int shift;
|
||||||
|
|
||||||
|
x = vec->x;
|
||||||
|
y = vec->y;
|
||||||
|
|
||||||
|
shift = PVG_FT_MSB(PVG_FT_ABS(x) | PVG_FT_ABS(y));
|
||||||
|
|
||||||
|
if (shift <= PVG_FT_TRIG_SAFE_MSB) {
|
||||||
|
shift = PVG_FT_TRIG_SAFE_MSB - shift;
|
||||||
|
vec->x = (PVG_FT_Pos)((PVG_FT_ULong)x << shift);
|
||||||
|
vec->y = (PVG_FT_Pos)((PVG_FT_ULong)y << shift);
|
||||||
|
} else {
|
||||||
|
shift -= PVG_FT_TRIG_SAFE_MSB;
|
||||||
|
vec->x = x >> shift;
|
||||||
|
vec->y = y >> shift;
|
||||||
|
shift = -shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ft_trig_pseudo_rotate(PVG_FT_Vector* vec, PVG_FT_Angle theta)
|
||||||
|
{
|
||||||
|
PVG_FT_Int i;
|
||||||
|
PVG_FT_Fixed x, y, xtemp, b;
|
||||||
|
const PVG_FT_Fixed* arctanptr;
|
||||||
|
|
||||||
|
x = vec->x;
|
||||||
|
y = vec->y;
|
||||||
|
|
||||||
|
/* Rotate inside [-PI/4,PI/4] sector */
|
||||||
|
while (theta < -PVG_FT_ANGLE_PI4) {
|
||||||
|
xtemp = y;
|
||||||
|
y = -x;
|
||||||
|
x = xtemp;
|
||||||
|
theta += PVG_FT_ANGLE_PI2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (theta > PVG_FT_ANGLE_PI4) {
|
||||||
|
xtemp = -y;
|
||||||
|
y = x;
|
||||||
|
x = xtemp;
|
||||||
|
theta -= PVG_FT_ANGLE_PI2;
|
||||||
|
}
|
||||||
|
|
||||||
|
arctanptr = ft_trig_arctan_table;
|
||||||
|
|
||||||
|
/* Pseudorotations, with right shifts */
|
||||||
|
for (i = 1, b = 1; i < PVG_FT_TRIG_MAX_ITERS; b <<= 1, i++) {
|
||||||
|
PVG_FT_Fixed v1 = ((y + b) >> i);
|
||||||
|
PVG_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(PVG_FT_Vector* vec)
|
||||||
|
{
|
||||||
|
PVG_FT_Angle theta;
|
||||||
|
PVG_FT_Int i;
|
||||||
|
PVG_FT_Fixed x, y, xtemp, b;
|
||||||
|
const PVG_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 = PVG_FT_ANGLE_PI2;
|
||||||
|
xtemp = y;
|
||||||
|
y = -x;
|
||||||
|
x = xtemp;
|
||||||
|
} else {
|
||||||
|
theta = y > 0 ? PVG_FT_ANGLE_PI : -PVG_FT_ANGLE_PI;
|
||||||
|
x = -x;
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (y < -x) {
|
||||||
|
theta = -PVG_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 < PVG_FT_TRIG_MAX_ITERS; b <<= 1, i++) {
|
||||||
|
PVG_FT_Fixed v1 = ((y + b) >> i);
|
||||||
|
PVG_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 = PVG_FT_PAD_ROUND(theta, 32);
|
||||||
|
else
|
||||||
|
theta = -PVG_FT_PAD_ROUND(-theta, 32);
|
||||||
|
|
||||||
|
vec->x = x;
|
||||||
|
vec->y = theta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Fixed PVG_FT_Cos(PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
PVG_FT_Vector v;
|
||||||
|
|
||||||
|
v.x = PVG_FT_TRIG_SCALE >> 8;
|
||||||
|
v.y = 0;
|
||||||
|
ft_trig_pseudo_rotate(&v, angle);
|
||||||
|
|
||||||
|
return (v.x + 0x80L) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Fixed PVG_FT_Sin(PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
return PVG_FT_Cos(PVG_FT_ANGLE_PI2 - angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Fixed PVG_FT_Tan(PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
PVG_FT_Vector v;
|
||||||
|
|
||||||
|
v.x = PVG_FT_TRIG_SCALE >> 8;
|
||||||
|
v.y = 0;
|
||||||
|
ft_trig_pseudo_rotate(&v, angle);
|
||||||
|
|
||||||
|
return PVG_FT_DivFix(v.y, v.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Angle PVG_FT_Atan2(PVG_FT_Fixed dx, PVG_FT_Fixed dy)
|
||||||
|
{
|
||||||
|
PVG_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 PVG_FT_Vector_Unit(PVG_FT_Vector* vec, PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
vec->x = PVG_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PVG_FT_Vector_Rotate(PVG_FT_Vector* vec, PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
PVG_FT_Int shift;
|
||||||
|
PVG_FT_Vector v = *vec;
|
||||||
|
|
||||||
|
if ( v.x == 0 && v.y == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
PVG_FT_Int32 half = (PVG_FT_Int32)1L << ( shift - 1 );
|
||||||
|
|
||||||
|
|
||||||
|
vec->x = ( v.x + half - ( v.x < 0 ) ) >> shift;
|
||||||
|
vec->y = ( v.y + half - ( v.y < 0 ) ) >> shift;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shift = -shift;
|
||||||
|
vec->x = (PVG_FT_Pos)( (PVG_FT_ULong)v.x << shift );
|
||||||
|
vec->y = (PVG_FT_Pos)( (PVG_FT_ULong)v.y << shift );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Fixed PVG_FT_Vector_Length(PVG_FT_Vector* vec)
|
||||||
|
{
|
||||||
|
PVG_FT_Int shift;
|
||||||
|
PVG_FT_Vector v;
|
||||||
|
|
||||||
|
v = *vec;
|
||||||
|
|
||||||
|
/* handle trivial cases */
|
||||||
|
if (v.x == 0) {
|
||||||
|
return PVG_FT_ABS(v.y);
|
||||||
|
} else if (v.y == 0) {
|
||||||
|
return PVG_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 (PVG_FT_Fixed)((PVG_FT_UInt32)v.x << -shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
void PVG_FT_Vector_Polarize(PVG_FT_Vector* vec, PVG_FT_Fixed* length,
|
||||||
|
PVG_FT_Angle* angle)
|
||||||
|
{
|
||||||
|
PVG_FT_Int shift;
|
||||||
|
PVG_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)
|
||||||
|
: (PVG_FT_Fixed)((PVG_FT_UInt32)v.x << -shift);
|
||||||
|
*angle = v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
void PVG_FT_Vector_From_Polar(PVG_FT_Vector* vec, PVG_FT_Fixed length,
|
||||||
|
PVG_FT_Angle angle)
|
||||||
|
{
|
||||||
|
vec->x = length;
|
||||||
|
vec->y = 0;
|
||||||
|
|
||||||
|
PVG_FT_Vector_Rotate(vec, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* documentation is in fttrigon.h */
|
||||||
|
|
||||||
|
PVG_FT_Angle PVG_FT_Angle_Diff( PVG_FT_Angle angle1, PVG_FT_Angle angle2 )
|
||||||
|
{
|
||||||
|
PVG_FT_Angle delta = angle2 - angle1;
|
||||||
|
|
||||||
|
while ( delta <= -PVG_FT_ANGLE_PI )
|
||||||
|
delta += PVG_FT_ANGLE_2PI;
|
||||||
|
|
||||||
|
while ( delta > PVG_FT_ANGLE_PI )
|
||||||
|
delta -= PVG_FT_ANGLE_2PI;
|
||||||
|
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* END */
|
||||||
436
vendor/lunasvg/plutovg/source/plutovg-ft-math.h
vendored
Normal file
436
vendor/lunasvg/plutovg/source/plutovg-ft-math.h
vendored
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
/***************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* 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, FTL.TXT. By continuing to use, modify, or distribute */
|
||||||
|
/* this file you indicate that you have read the license and */
|
||||||
|
/* understand and accept it fully. */
|
||||||
|
/* */
|
||||||
|
/***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PLUTOVG_FT_MATH_H
|
||||||
|
#define PLUTOVG_FT_MATH_H
|
||||||
|
|
||||||
|
#include "plutovg-ft-types.h"
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* The min and max functions missing in C. As usual, be careful not to */
|
||||||
|
/* write things like PVG_FT_MIN( a++, b++ ) to avoid side effects. */
|
||||||
|
/* */
|
||||||
|
#define PVG_FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) )
|
||||||
|
#define PVG_FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) )
|
||||||
|
|
||||||
|
#define PVG_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 PVG_FT_HYPOT( x, y ) \
|
||||||
|
( x = PVG_FT_ABS( x ), \
|
||||||
|
y = PVG_FT_ABS( y ), \
|
||||||
|
x > y ? x + ( 3 * y >> 3 ) \
|
||||||
|
: y + ( 3 * x >> 3 ) )
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Function> */
|
||||||
|
/* PVG_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. */
|
||||||
|
/* */
|
||||||
|
PVG_FT_Long
|
||||||
|
PVG_FT_MulFix( PVG_FT_Long a,
|
||||||
|
PVG_FT_Long b );
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Function> */
|
||||||
|
/* PVG_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'. */
|
||||||
|
/* */
|
||||||
|
PVG_FT_Long
|
||||||
|
PVG_FT_MulDiv( PVG_FT_Long a,
|
||||||
|
PVG_FT_Long b,
|
||||||
|
PVG_FT_Long c );
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Function> */
|
||||||
|
/* PVG_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'. */
|
||||||
|
/* */
|
||||||
|
PVG_FT_Long
|
||||||
|
PVG_FT_DivFix( PVG_FT_Long a,
|
||||||
|
PVG_FT_Long b );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Section> */
|
||||||
|
/* computations */
|
||||||
|
/* */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @type:
|
||||||
|
* PVG_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 PVG_FT_Fixed PVG_FT_Angle;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @macro:
|
||||||
|
* PVG_FT_ANGLE_PI
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* The angle pi expressed in @PVG_FT_Angle units.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PVG_FT_ANGLE_PI ( 180L << 16 )
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @macro:
|
||||||
|
* PVG_FT_ANGLE_2PI
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* The angle 2*pi expressed in @PVG_FT_Angle units.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PVG_FT_ANGLE_2PI ( PVG_FT_ANGLE_PI * 2 )
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @macro:
|
||||||
|
* PVG_FT_ANGLE_PI2
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* The angle pi/2 expressed in @PVG_FT_Angle units.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PVG_FT_ANGLE_PI2 ( PVG_FT_ANGLE_PI / 2 )
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @macro:
|
||||||
|
* PVG_FT_ANGLE_PI4
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* The angle pi/4 expressed in @PVG_FT_Angle units.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PVG_FT_ANGLE_PI4 ( PVG_FT_ANGLE_PI / 4 )
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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 @PVG_FT_Vector_Unit.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Fixed
|
||||||
|
PVG_FT_Sin( PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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 @PVG_FT_Vector_Unit.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Fixed
|
||||||
|
PVG_FT_Cos( PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_FT_Tan
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* Return the tangent of a given angle in fixed-point format.
|
||||||
|
*
|
||||||
|
* @input:
|
||||||
|
* angle ::
|
||||||
|
* The input angle.
|
||||||
|
*
|
||||||
|
* @return:
|
||||||
|
* The tangent value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Fixed
|
||||||
|
PVG_FT_Tan( PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Angle
|
||||||
|
PVG_FT_Atan2( PVG_FT_Fixed x,
|
||||||
|
PVG_FT_Fixed y );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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'.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Angle
|
||||||
|
PVG_FT_Angle_Diff( PVG_FT_Angle angle1,
|
||||||
|
PVG_FT_Angle angle2 );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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
|
||||||
|
PVG_FT_Vector_Unit( PVG_FT_Vector* vec,
|
||||||
|
PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_FT_Vector_Rotate
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* Rotate a vector by a given angle.
|
||||||
|
*
|
||||||
|
* @inout:
|
||||||
|
* vec ::
|
||||||
|
* The address of target vector.
|
||||||
|
*
|
||||||
|
* @input:
|
||||||
|
* angle ::
|
||||||
|
* The input angle.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PVG_FT_Vector_Rotate( PVG_FT_Vector* vec,
|
||||||
|
PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PVG_FT_Fixed
|
||||||
|
PVG_FT_Vector_Length( PVG_FT_Vector* vec );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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
|
||||||
|
PVG_FT_Vector_Polarize( PVG_FT_Vector* vec,
|
||||||
|
PVG_FT_Fixed *length,
|
||||||
|
PVG_FT_Angle *angle );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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
|
||||||
|
PVG_FT_Vector_From_Polar( PVG_FT_Vector* vec,
|
||||||
|
PVG_FT_Fixed length,
|
||||||
|
PVG_FT_Angle angle );
|
||||||
|
|
||||||
|
#endif /* PLUTOVG_FT_MATH_H */
|
||||||
1889
vendor/lunasvg/plutovg/source/plutovg-ft-raster.c
vendored
Normal file
1889
vendor/lunasvg/plutovg/source/plutovg-ft-raster.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
420
vendor/lunasvg/plutovg/source/plutovg-ft-raster.h
vendored
Normal file
420
vendor/lunasvg/plutovg/source/plutovg-ft-raster.h
vendored
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
/***************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* 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, FTL.TXT. By continuing to use, modify, or distribute */
|
||||||
|
/* this file you indicate that you have read the license and */
|
||||||
|
/* understand and accept it fully. */
|
||||||
|
/* */
|
||||||
|
/***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PLUTOVG_FT_RASTER_H
|
||||||
|
#define PLUTOVG_FT_RASTER_H
|
||||||
|
|
||||||
|
#include "plutovg-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 PVG_FT_BBox_
|
||||||
|
{
|
||||||
|
PVG_FT_Pos xMin, yMin;
|
||||||
|
PVG_FT_Pos xMax, yMax;
|
||||||
|
|
||||||
|
} PVG_FT_BBox;
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Struct> */
|
||||||
|
/* PVG_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' @PVG_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 @PVG_FT_OUTLINE_FLAGS.*/
|
||||||
|
/* */
|
||||||
|
typedef struct PVG_FT_Outline_
|
||||||
|
{
|
||||||
|
int n_contours; /* number of contours in glyph */
|
||||||
|
int n_points; /* number of points in the glyph */
|
||||||
|
|
||||||
|
PVG_FT_Vector* points; /* the outline's points */
|
||||||
|
char* tags; /* the points flags */
|
||||||
|
int* contours; /* the contour end points */
|
||||||
|
char* contours_flag; /* the contour open flags */
|
||||||
|
|
||||||
|
int flags; /* outline masks */
|
||||||
|
|
||||||
|
} PVG_FT_Outline;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Enum> */
|
||||||
|
/* PVG_FT_OUTLINE_FLAGS */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A list of bit-field constants use for the flags in an outline's */
|
||||||
|
/* `flags' field. */
|
||||||
|
/* */
|
||||||
|
/* <Values> */
|
||||||
|
/* PVG_FT_OUTLINE_NONE :: */
|
||||||
|
/* Value~0 is reserved. */
|
||||||
|
/* */
|
||||||
|
/* PVG_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. */
|
||||||
|
/* */
|
||||||
|
/* PVG_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). */
|
||||||
|
/* */
|
||||||
|
/* PVG_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 @PVG_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 PVG_FT_OUTLINE_NONE 0x0
|
||||||
|
#define PVG_FT_OUTLINE_OWNER 0x1
|
||||||
|
#define PVG_FT_OUTLINE_EVEN_ODD_FILL 0x2
|
||||||
|
#define PVG_FT_OUTLINE_REVERSE_FILL 0x4
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
|
#define PVG_FT_CURVE_TAG( flag ) ( flag & 3 )
|
||||||
|
|
||||||
|
#define PVG_FT_CURVE_TAG_ON 1
|
||||||
|
#define PVG_FT_CURVE_TAG_CONIC 0
|
||||||
|
#define PVG_FT_CURVE_TAG_CUBIC 2
|
||||||
|
|
||||||
|
|
||||||
|
#define PVG_FT_Curve_Tag_On PVG_FT_CURVE_TAG_ON
|
||||||
|
#define PVG_FT_Curve_Tag_Conic PVG_FT_CURVE_TAG_CONIC
|
||||||
|
#define PVG_FT_Curve_Tag_Cubic PVG_FT_CURVE_TAG_CUBIC
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Function> */
|
||||||
|
/* PVG_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. */
|
||||||
|
/* */
|
||||||
|
PVG_FT_Error
|
||||||
|
PVG_FT_Outline_Check( PVG_FT_Outline* outline );
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Function> */
|
||||||
|
/* PVG_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 @PVG_FT_Glyph_Get_CBox for a discussion of tricky fonts. */
|
||||||
|
/* */
|
||||||
|
void
|
||||||
|
PVG_FT_Outline_Get_CBox( const PVG_FT_Outline* outline,
|
||||||
|
PVG_FT_BBox *acbox );
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Struct> */
|
||||||
|
/* PVG_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 */
|
||||||
|
/* @PVG_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 PVG_FT_Span_
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int len;
|
||||||
|
int y;
|
||||||
|
unsigned char coverage;
|
||||||
|
|
||||||
|
} PVG_FT_Span;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <FuncType> */
|
||||||
|
/* PVG_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 `PVG_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
|
||||||
|
(*PVG_FT_SpanFunc)( int count,
|
||||||
|
const PVG_FT_Span* spans,
|
||||||
|
void* user );
|
||||||
|
|
||||||
|
#define PVG_FT_Raster_Span_Func PVG_FT_SpanFunc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Enum> */
|
||||||
|
/* PVG_FT_RASTER_FLAG_XXX */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A list of bit flag constants as used in the `flags' field of a */
|
||||||
|
/* @PVG_FT_Raster_Params structure. */
|
||||||
|
/* */
|
||||||
|
/* <Values> */
|
||||||
|
/* PVG_FT_RASTER_FLAG_DEFAULT :: This value is 0. */
|
||||||
|
/* */
|
||||||
|
/* PVG_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). */
|
||||||
|
/* */
|
||||||
|
/* PVG_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. */
|
||||||
|
/* */
|
||||||
|
/* PVG_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 */
|
||||||
|
/* @PVG_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 PVG_FT_RASTER_FLAG_DEFAULT 0x0
|
||||||
|
#define PVG_FT_RASTER_FLAG_AA 0x1
|
||||||
|
#define PVG_FT_RASTER_FLAG_DIRECT 0x2
|
||||||
|
#define PVG_FT_RASTER_FLAG_CLIP 0x4
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Struct> */
|
||||||
|
/* PVG_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 */
|
||||||
|
/* @PVG_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 @PVG_FT_RASTER_FLAG_AA */
|
||||||
|
/* bit flag is set in the `flags' field, otherwise a monochrome */
|
||||||
|
/* bitmap is generated. */
|
||||||
|
/* */
|
||||||
|
/* If the @PVG_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 PVG_FT_Raster_Params_
|
||||||
|
{
|
||||||
|
const void* source;
|
||||||
|
int flags;
|
||||||
|
PVG_FT_SpanFunc gray_spans;
|
||||||
|
void* user;
|
||||||
|
PVG_FT_BBox clip_box;
|
||||||
|
|
||||||
|
} PVG_FT_Raster_Params;
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
PVG_FT_Raster_Render(const PVG_FT_Raster_Params *params);
|
||||||
|
|
||||||
|
#endif // PLUTOVG_FT_RASTER_H
|
||||||
1873
vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c
vendored
Normal file
1873
vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
320
vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h
vendored
Normal file
320
vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h
vendored
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
/***************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* 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, FTL.TXT. By continuing to use, modify, or distribute */
|
||||||
|
/* this file you indicate that you have read the license and */
|
||||||
|
/* understand and accept it fully. */
|
||||||
|
/* */
|
||||||
|
/***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PLUTOVG_FT_STROKER_H
|
||||||
|
#define PLUTOVG_FT_STROKER_H
|
||||||
|
|
||||||
|
#include "plutovg-ft-raster.h"
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @type:
|
||||||
|
* PVG_FT_Stroker
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* Opaque handler to a path stroker object.
|
||||||
|
*/
|
||||||
|
typedef struct PVG_FT_StrokerRec_* PVG_FT_Stroker;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @enum:
|
||||||
|
* PVG_FT_Stroker_LineJoin
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* These values determine how two joining lines are rendered
|
||||||
|
* in a stroker.
|
||||||
|
*
|
||||||
|
* @values:
|
||||||
|
* PVG_FT_STROKER_LINEJOIN_ROUND ::
|
||||||
|
* Used to render rounded line joins. Circular arcs are used
|
||||||
|
* to join two lines smoothly.
|
||||||
|
*
|
||||||
|
* PVG_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.
|
||||||
|
*
|
||||||
|
* PVG_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. PVG_FT_STROKER_LINEJOIN_MITER_FIXED generates a miter
|
||||||
|
* line join as used in PostScript and PDF.
|
||||||
|
*
|
||||||
|
* PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE ::
|
||||||
|
* PVG_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.
|
||||||
|
* PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE generates a mitered line
|
||||||
|
* join as used in XPS. PVG_FT_STROKER_LINEJOIN_MITER is an alias
|
||||||
|
* for PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE, retained for
|
||||||
|
* backwards compatibility.
|
||||||
|
*/
|
||||||
|
typedef enum PVG_FT_Stroker_LineJoin_
|
||||||
|
{
|
||||||
|
PVG_FT_STROKER_LINEJOIN_ROUND = 0,
|
||||||
|
PVG_FT_STROKER_LINEJOIN_BEVEL = 1,
|
||||||
|
PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE = 2,
|
||||||
|
PVG_FT_STROKER_LINEJOIN_MITER = PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE,
|
||||||
|
PVG_FT_STROKER_LINEJOIN_MITER_FIXED = 3
|
||||||
|
|
||||||
|
} PVG_FT_Stroker_LineJoin;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @enum:
|
||||||
|
* PVG_FT_Stroker_LineCap
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* These values determine how the end of opened sub-paths are
|
||||||
|
* rendered in a stroke.
|
||||||
|
*
|
||||||
|
* @values:
|
||||||
|
* PVG_FT_STROKER_LINECAP_BUTT ::
|
||||||
|
* The end of lines is rendered as a full stop on the last
|
||||||
|
* point itself.
|
||||||
|
*
|
||||||
|
* PVG_FT_STROKER_LINECAP_ROUND ::
|
||||||
|
* The end of lines is rendered as a half-circle around the
|
||||||
|
* last point.
|
||||||
|
*
|
||||||
|
* PVG_FT_STROKER_LINECAP_SQUARE ::
|
||||||
|
* The end of lines is rendered as a square around the
|
||||||
|
* last point.
|
||||||
|
*/
|
||||||
|
typedef enum PVG_FT_Stroker_LineCap_
|
||||||
|
{
|
||||||
|
PVG_FT_STROKER_LINECAP_BUTT = 0,
|
||||||
|
PVG_FT_STROKER_LINECAP_ROUND,
|
||||||
|
PVG_FT_STROKER_LINECAP_SQUARE
|
||||||
|
|
||||||
|
} PVG_FT_Stroker_LineCap;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @enum:
|
||||||
|
* PVG_FT_StrokerBorder
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* These values are used to select a given stroke border
|
||||||
|
* in @PVG_FT_Stroker_GetBorderCounts and @PVG_FT_Stroker_ExportBorder.
|
||||||
|
*
|
||||||
|
* @values:
|
||||||
|
* PVG_FT_STROKER_BORDER_LEFT ::
|
||||||
|
* Select the left border, relative to the drawing direction.
|
||||||
|
*
|
||||||
|
* PVG_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 @PVG_FT_Outline_GetInsideBorder and
|
||||||
|
* @PVG_FT_Outline_GetOutsideBorder to get these.
|
||||||
|
*/
|
||||||
|
typedef enum PVG_FT_StrokerBorder_
|
||||||
|
{
|
||||||
|
PVG_FT_STROKER_BORDER_LEFT = 0,
|
||||||
|
PVG_FT_STROKER_BORDER_RIGHT
|
||||||
|
|
||||||
|
} PVG_FT_StrokerBorder;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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.
|
||||||
|
*/
|
||||||
|
PVG_FT_Error
|
||||||
|
PVG_FT_Stroker_New( PVG_FT_Stroker *astroker );
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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 PVG_FT_STROKER_LINEJOIN_MITER_FIXED and
|
||||||
|
* PVG_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
|
||||||
|
PVG_FT_Stroker_Set( PVG_FT_Stroker stroker,
|
||||||
|
PVG_FT_Fixed radius,
|
||||||
|
PVG_FT_Stroker_LineCap line_cap,
|
||||||
|
PVG_FT_Stroker_LineJoin line_join,
|
||||||
|
PVG_FT_Fixed miter_limit );
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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 @PVG_FT_Stroker_GetCounts and @PVG_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 @PVG_FT_Stroker_Rewind automatically.
|
||||||
|
*/
|
||||||
|
PVG_FT_Error
|
||||||
|
PVG_FT_Stroker_ParseOutline( PVG_FT_Stroker stroker,
|
||||||
|
const PVG_FT_Outline* outline);
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_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.
|
||||||
|
*/
|
||||||
|
PVG_FT_Error
|
||||||
|
PVG_FT_Stroker_GetCounts( PVG_FT_Stroker stroker,
|
||||||
|
PVG_FT_UInt *anum_points,
|
||||||
|
PVG_FT_UInt *anum_contours );
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_FT_Stroker_Export
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* Call this function after @PVG_FT_Stroker_GetBorderCounts to
|
||||||
|
* export all borders to your own @PVG_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
|
||||||
|
PVG_FT_Stroker_Export( PVG_FT_Stroker stroker,
|
||||||
|
PVG_FT_Outline* outline );
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
*
|
||||||
|
* @function:
|
||||||
|
* PVG_FT_Stroker_Done
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
* Destroy a stroker object.
|
||||||
|
*
|
||||||
|
* @input:
|
||||||
|
* stroker ::
|
||||||
|
* A stroker handle. Can be NULL.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PVG_FT_Stroker_Done( PVG_FT_Stroker stroker );
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PLUTOVG_FT_STROKER_H
|
||||||
176
vendor/lunasvg/plutovg/source/plutovg-ft-types.h
vendored
Normal file
176
vendor/lunasvg/plutovg/source/plutovg-ft-types.h
vendored
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* fttypes.h
|
||||||
|
*
|
||||||
|
* FreeType simple types definitions (specification only).
|
||||||
|
*
|
||||||
|
* Copyright (C) 1996-2020 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, FTL.TXT. By continuing to use, modify, or distribute
|
||||||
|
* this file you indicate that you have read the license and
|
||||||
|
* understand and accept it fully.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PLUTOVG_FT_TYPES_H
|
||||||
|
#define PLUTOVG_FT_TYPES_H
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Fixed */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* This type is used to store 16.16 fixed-point values, like scaling */
|
||||||
|
/* values or matrix coefficients. */
|
||||||
|
/* */
|
||||||
|
typedef signed long PVG_FT_Fixed;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Int */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A typedef for the int type. */
|
||||||
|
/* */
|
||||||
|
typedef signed int PVG_FT_Int;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_UInt */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A typedef for the unsigned int type. */
|
||||||
|
/* */
|
||||||
|
typedef unsigned int PVG_FT_UInt;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Long */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A typedef for signed long. */
|
||||||
|
/* */
|
||||||
|
typedef signed long PVG_FT_Long;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_ULong */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A typedef for unsigned long. */
|
||||||
|
/* */
|
||||||
|
typedef unsigned long PVG_FT_ULong;
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Short */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A typedef for signed short. */
|
||||||
|
/* */
|
||||||
|
typedef signed short PVG_FT_Short;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Byte */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A simple typedef for the _unsigned_ char type. */
|
||||||
|
/* */
|
||||||
|
typedef unsigned char PVG_FT_Byte;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_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 PVG_FT_Bool;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Error */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* The FreeType error code type. A value of~0 is always interpreted */
|
||||||
|
/* as a successful operation. */
|
||||||
|
/* */
|
||||||
|
typedef int PVG_FT_Error;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Type> */
|
||||||
|
/* PVG_FT_Pos */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* The type PVG_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 PVG_FT_Pos;
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* <Struct> */
|
||||||
|
/* PVG_FT_Vector */
|
||||||
|
/* */
|
||||||
|
/* <Description> */
|
||||||
|
/* A simple structure used to store a 2D vector; coordinates are of */
|
||||||
|
/* the PVG_FT_Pos type. */
|
||||||
|
/* */
|
||||||
|
/* <Fields> */
|
||||||
|
/* x :: The horizontal coordinate. */
|
||||||
|
/* y :: The vertical coordinate. */
|
||||||
|
/* */
|
||||||
|
typedef struct PVG_FT_Vector_
|
||||||
|
{
|
||||||
|
PVG_FT_Pos x;
|
||||||
|
PVG_FT_Pos y;
|
||||||
|
|
||||||
|
} PVG_FT_Vector;
|
||||||
|
|
||||||
|
|
||||||
|
typedef long long int PVG_FT_Int64;
|
||||||
|
typedef unsigned long long int PVG_FT_UInt64;
|
||||||
|
|
||||||
|
typedef signed int PVG_FT_Int32;
|
||||||
|
typedef unsigned int PVG_FT_UInt32;
|
||||||
|
|
||||||
|
#define PVG_FT_BOOL( x ) ( (PVG_FT_Bool)( x ) )
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PVG_FT_BEGIN_STMNT do {
|
||||||
|
#define PVG_FT_END_STMNT } while (0)
|
||||||
|
|
||||||
|
#endif // PLUTOVG_FT_TYPES_H
|
||||||
229
vendor/lunasvg/plutovg/source/plutovg-matrix.c
vendored
Normal file
229
vendor/lunasvg/plutovg/source/plutovg-matrix.c
vendored
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
#include "plutovg.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
void plutovg_matrix_init(plutovg_matrix_t* matrix, float a, float b, float c, float d, float e, float f)
|
||||||
|
{
|
||||||
|
matrix->a = a; matrix->b = b;
|
||||||
|
matrix->c = c; matrix->d = d;
|
||||||
|
matrix->e = e; matrix->f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_init_identity(plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init(matrix, 1, 0, 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, float tx, float ty)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init(matrix, 1, 0, 0, 1, tx, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_init_scale(plutovg_matrix_t* matrix, float sx, float sy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init(matrix, sx, 0, 0, sy, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_init_rotate(plutovg_matrix_t* matrix, float angle)
|
||||||
|
{
|
||||||
|
float c = cosf(angle);
|
||||||
|
float s = sinf(angle);
|
||||||
|
plutovg_matrix_init(matrix, c, s, -s, c, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_init_shear(plutovg_matrix_t* matrix, float shx, float shy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init(matrix, 1, tanf(shy), tanf(shx), 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_translate(plutovg_matrix_t* matrix, float tx, float ty)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t m;
|
||||||
|
plutovg_matrix_init_translate(&m, tx, ty);
|
||||||
|
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_scale(plutovg_matrix_t* matrix, float sx, float sy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t m;
|
||||||
|
plutovg_matrix_init_scale(&m, sx, sy);
|
||||||
|
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_rotate(plutovg_matrix_t* matrix, float angle)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t m;
|
||||||
|
plutovg_matrix_init_rotate(&m, angle);
|
||||||
|
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_shear(plutovg_matrix_t* matrix, float shx, float shy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t m;
|
||||||
|
plutovg_matrix_init_shear(&m, shx, shy);
|
||||||
|
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_multiply(plutovg_matrix_t* matrix, const plutovg_matrix_t* left, const plutovg_matrix_t* right)
|
||||||
|
{
|
||||||
|
float a = left->a * right->a + left->b * right->c;
|
||||||
|
float b = left->a * right->b + left->b * right->d;
|
||||||
|
float c = left->c * right->a + left->d * right->c;
|
||||||
|
float d = left->c * right->b + left->d * right->d;
|
||||||
|
float e = left->e * right->a + left->f * right->c + right->e;
|
||||||
|
float f = left->e * right->b + left->f * right->d + right->f;
|
||||||
|
plutovg_matrix_init(matrix, a, b, c, d, e, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_matrix_invert(const plutovg_matrix_t* matrix, plutovg_matrix_t* inverse)
|
||||||
|
{
|
||||||
|
float det = (matrix->a * matrix->d - matrix->b * matrix->c);
|
||||||
|
if(det == 0.f)
|
||||||
|
return false;
|
||||||
|
if(inverse) {
|
||||||
|
float inv_det = 1.f / det;
|
||||||
|
float a = matrix->a * inv_det;
|
||||||
|
float b = matrix->b * inv_det;
|
||||||
|
float c = matrix->c * inv_det;
|
||||||
|
float d = matrix->d * inv_det;
|
||||||
|
float e = (matrix->c * matrix->f - matrix->d * matrix->e) * inv_det;
|
||||||
|
float f = (matrix->b * matrix->e - matrix->a * matrix->f) * inv_det;
|
||||||
|
plutovg_matrix_init(inverse, d, -b, -c, a, e, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_matrix_map(const plutovg_matrix_t* matrix, float x, float y, float* xx, float* yy)
|
||||||
|
{
|
||||||
|
*xx = x * matrix->a + y * matrix->c + matrix->e;
|
||||||
|
*yy = x * matrix->b + y * matrix->d + matrix->f;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_points(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst, int count)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < count; ++i) {
|
||||||
|
plutovg_matrix_map_point(matrix, &src[i], &dst[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_points(matrix, p, p, 4);
|
||||||
|
|
||||||
|
float l = p[0].x;
|
||||||
|
float t = p[0].y;
|
||||||
|
float r = p[0].x;
|
||||||
|
float 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst->x = l;
|
||||||
|
dst->y = t;
|
||||||
|
dst->w = r - l;
|
||||||
|
dst->h = b - t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_matrix_parameters(const char** begin, const char* end, float values[6], int required, int optional)
|
||||||
|
{
|
||||||
|
if(!plutovg_skip_ws_and_delim(begin, end, '('))
|
||||||
|
return 0;
|
||||||
|
int count = 0;
|
||||||
|
int max_count = required + optional;
|
||||||
|
bool has_trailing_comma = false;
|
||||||
|
for(; count < max_count; ++count) {
|
||||||
|
if(!plutovg_parse_number(begin, end, values + count))
|
||||||
|
break;
|
||||||
|
plutovg_skip_ws_or_comma(begin, end, &has_trailing_comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!has_trailing_comma && (count == required || count == max_count)
|
||||||
|
&& plutovg_skip_delim(begin, end, ')')) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_matrix_parse(plutovg_matrix_t* matrix, const char* data, int length)
|
||||||
|
{
|
||||||
|
float values[6];
|
||||||
|
plutovg_matrix_init_identity(matrix);
|
||||||
|
if(length == -1)
|
||||||
|
length = strlen(data);
|
||||||
|
const char* it = data;
|
||||||
|
const char* end = it + length;
|
||||||
|
bool has_trailing_comma = false;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
while(it < end) {
|
||||||
|
if(plutovg_skip_string(&it, end, "matrix")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 6, 0);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
plutovg_matrix_t m = { values[0], values[1], values[2], values[3], values[4], values[5] };
|
||||||
|
plutovg_matrix_multiply(matrix, &m, matrix);
|
||||||
|
} else if(plutovg_skip_string(&it, end, "translate")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 1, 1);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
if(count == 1) {
|
||||||
|
plutovg_matrix_translate(matrix, values[0], 0);
|
||||||
|
} else {
|
||||||
|
plutovg_matrix_translate(matrix, values[0], values[1]);
|
||||||
|
}
|
||||||
|
} else if(plutovg_skip_string(&it, end, "scale")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 1, 1);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
if(count == 1) {
|
||||||
|
plutovg_matrix_scale(matrix, values[0], values[0]);
|
||||||
|
} else {
|
||||||
|
plutovg_matrix_scale(matrix, values[0], values[1]);
|
||||||
|
}
|
||||||
|
} else if(plutovg_skip_string(&it, end, "rotate")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 1, 2);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
if(count == 3)
|
||||||
|
plutovg_matrix_translate(matrix, values[1], values[2]);
|
||||||
|
plutovg_matrix_rotate(matrix, PLUTOVG_DEG2RAD(values[0]));
|
||||||
|
if(count == 3) {
|
||||||
|
plutovg_matrix_translate(matrix, -values[1], -values[2]);
|
||||||
|
}
|
||||||
|
} else if(plutovg_skip_string(&it, end, "skewX")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 1, 0);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
plutovg_matrix_shear(matrix, PLUTOVG_DEG2RAD(values[0]), 0);
|
||||||
|
} else if(plutovg_skip_string(&it, end, "skewY")) {
|
||||||
|
int count = parse_matrix_parameters(&it, end, values, 1, 0);
|
||||||
|
if(count == 0)
|
||||||
|
return false;
|
||||||
|
plutovg_matrix_shear(matrix, 0, PLUTOVG_DEG2RAD(values[0]));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_skip_ws_or_comma(&it, end, &has_trailing_comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !has_trailing_comma;
|
||||||
|
}
|
||||||
489
vendor/lunasvg/plutovg/source/plutovg-paint.c
vendored
Normal file
489
vendor/lunasvg/plutovg/source/plutovg-paint.c
vendored
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
#include "plutovg-private.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
void plutovg_color_init_rgb(plutovg_color_t* color, float r, float g, float b)
|
||||||
|
{
|
||||||
|
plutovg_color_init_rgba(color, r, g, b, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_rgba(plutovg_color_t* color, float r, float g, float b, float a)
|
||||||
|
{
|
||||||
|
color->r = plutovg_clamp(r, 0.f, 1.f);
|
||||||
|
color->g = plutovg_clamp(g, 0.f, 1.f);
|
||||||
|
color->b = plutovg_clamp(b, 0.f, 1.f);
|
||||||
|
color->a = plutovg_clamp(a, 0.f, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_rgb8(plutovg_color_t* color, int r, int g, int b)
|
||||||
|
{
|
||||||
|
plutovg_color_init_rgba8(color, r, g, b, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_rgba8(plutovg_color_t* color, int r, int g, int b, int a)
|
||||||
|
{
|
||||||
|
plutovg_color_init_rgba(color, r / 255.f, g / 255.f, b / 255.f, a / 255.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_rgba32(plutovg_color_t* color, unsigned int value)
|
||||||
|
{
|
||||||
|
uint8_t r = (value >> 24) & 0xFF;
|
||||||
|
uint8_t g = (value >> 16) & 0xFF;
|
||||||
|
uint8_t b = (value >> 8) & 0xFF;
|
||||||
|
uint8_t a = (value >> 0) & 0xFF;
|
||||||
|
plutovg_color_init_rgba8(color, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_argb32(plutovg_color_t* color, unsigned int value)
|
||||||
|
{
|
||||||
|
uint8_t a = (value >> 24) & 0xFF;
|
||||||
|
uint8_t r = (value >> 16) & 0xFF;
|
||||||
|
uint8_t g = (value >> 8) & 0xFF;
|
||||||
|
uint8_t b = (value >> 0) & 0xFF;
|
||||||
|
plutovg_color_init_rgba8(color, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_hsl(plutovg_color_t* color, float h, float s, float l)
|
||||||
|
{
|
||||||
|
plutovg_color_init_hsla(color, h, s, l, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float hsl_component(float h, float s, float l, float n)
|
||||||
|
{
|
||||||
|
const float k = fmodf(n + h / 30.f, 12.f);
|
||||||
|
const float a = s * plutovg_min(l, 1.f - l);
|
||||||
|
return l - a * plutovg_max(-1.f, plutovg_min(1.f, plutovg_min(k - 3.f, 9.f - k)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_color_init_hsla(plutovg_color_t* color, float h, float s, float l, float a)
|
||||||
|
{
|
||||||
|
h = fmodf(h, 360.f);
|
||||||
|
if(h < 0.f) { h += 360.f; }
|
||||||
|
|
||||||
|
float r = hsl_component(h, s, l, 0);
|
||||||
|
float g = hsl_component(h, s, l, 8);
|
||||||
|
float b = hsl_component(h, s, l, 4);
|
||||||
|
plutovg_color_init_rgba(color, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int plutovg_color_to_rgba32(const plutovg_color_t* color)
|
||||||
|
{
|
||||||
|
uint32_t r = lroundf(color->r * 255);
|
||||||
|
uint32_t g = lroundf(color->g * 255);
|
||||||
|
uint32_t b = lroundf(color->b * 255);
|
||||||
|
uint32_t a = lroundf(color->a * 255);
|
||||||
|
return (r << 24) | (g << 16) | (b << 8) | (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int plutovg_color_to_argb32(const plutovg_color_t* color)
|
||||||
|
{
|
||||||
|
uint32_t a = lroundf(color->a * 255);
|
||||||
|
uint32_t r = lroundf(color->r * 255);
|
||||||
|
uint32_t g = lroundf(color->g * 255);
|
||||||
|
uint32_t b = lroundf(color->b * 255);
|
||||||
|
return (a << 24) | (r << 16) | (g << 8) | (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t hex_digit(uint8_t c)
|
||||||
|
{
|
||||||
|
if(c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if(c >= 'a' && c <= 'f')
|
||||||
|
return 10 + c - 'a';
|
||||||
|
return 10 + c - 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t hex_byte(uint8_t c1, uint8_t c2)
|
||||||
|
{
|
||||||
|
uint8_t h1 = hex_digit(c1);
|
||||||
|
uint8_t h2 = hex_digit(c2);
|
||||||
|
return (h1 << 4) | h2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_NAME 20
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
uint32_t value;
|
||||||
|
} color_entry_t;
|
||||||
|
|
||||||
|
static int color_entry_compare(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
const char* name = a;
|
||||||
|
const color_entry_t* entry = b;
|
||||||
|
return strcmp(name, entry->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_rgb_component(const char** begin, const char* end, float* component)
|
||||||
|
{
|
||||||
|
float value = 0;
|
||||||
|
if(!plutovg_parse_number(begin, end, &value))
|
||||||
|
return false;
|
||||||
|
if(plutovg_skip_delim(begin, end, '%'))
|
||||||
|
value *= 2.55f;
|
||||||
|
*component = plutovg_clamp(value, 0.f, 255.f) / 255.f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_alpha_component(const char** begin, const char* end, float* component)
|
||||||
|
{
|
||||||
|
float value = 0;
|
||||||
|
if(!plutovg_parse_number(begin, end, &value))
|
||||||
|
return false;
|
||||||
|
if(plutovg_skip_delim(begin, end, '%'))
|
||||||
|
value /= 100.f;
|
||||||
|
*component = plutovg_clamp(value, 0.f, 1.f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_color_parse(plutovg_color_t* color, const char* data, int length)
|
||||||
|
{
|
||||||
|
if(length == -1)
|
||||||
|
length = strlen(data);
|
||||||
|
const char* it = data;
|
||||||
|
const char* end = it + length;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
if(plutovg_skip_delim(&it, end, '#')) {
|
||||||
|
int r, g, b, a = 255;
|
||||||
|
const char* begin = it;
|
||||||
|
while(it < end && isxdigit(*it))
|
||||||
|
++it;
|
||||||
|
int count = it - begin;
|
||||||
|
if(count == 3 || count == 4) {
|
||||||
|
r = hex_byte(begin[0], begin[0]);
|
||||||
|
g = hex_byte(begin[1], begin[1]);
|
||||||
|
b = hex_byte(begin[2], begin[2]);
|
||||||
|
if(count == 4) {
|
||||||
|
a = hex_byte(begin[3], begin[3]);
|
||||||
|
}
|
||||||
|
} else if(count == 6 || count == 8) {
|
||||||
|
r = hex_byte(begin[0], begin[1]);
|
||||||
|
g = hex_byte(begin[2], begin[3]);
|
||||||
|
b = hex_byte(begin[4], begin[5]);
|
||||||
|
if(count == 8) {
|
||||||
|
a = hex_byte(begin[6], begin[7]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_color_init_rgba8(color, r, g, b, a);
|
||||||
|
} else {
|
||||||
|
int name_length = 0;
|
||||||
|
char name[MAX_NAME + 1];
|
||||||
|
while(it < end && name_length < MAX_NAME && isalpha(*it))
|
||||||
|
name[name_length++] = tolower(*it++);
|
||||||
|
name[name_length] = '\0';
|
||||||
|
|
||||||
|
if(strcmp(name, "transparent") == 0) {
|
||||||
|
plutovg_color_init_rgba(color, 0, 0, 0, 0);
|
||||||
|
} else if(strcmp(name, "rgb") == 0 || strcmp(name, "rgba") == 0) {
|
||||||
|
if(!plutovg_skip_ws_and_delim(&it, end, '('))
|
||||||
|
return 0;
|
||||||
|
float r, g, b, a = 1.f;
|
||||||
|
if(!parse_rgb_component(&it, end, &r)
|
||||||
|
|| !plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
|| !parse_rgb_component(&it, end, &g)
|
||||||
|
|| !plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
|| !parse_rgb_component(&it, end, &b)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
&& !parse_alpha_component(&it, end, &a)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
if(!plutovg_skip_delim(&it, end, ')'))
|
||||||
|
return 0;
|
||||||
|
plutovg_color_init_rgba(color, r, g, b, a);
|
||||||
|
} else if(strcmp(name, "hsl") == 0 || strcmp(name, "hsla") == 0) {
|
||||||
|
if(!plutovg_skip_ws_and_delim(&it, end, '('))
|
||||||
|
return 0;
|
||||||
|
float h, s, l, a = 1.f;
|
||||||
|
if(!plutovg_parse_number(&it, end, &h)
|
||||||
|
|| !plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
|| !parse_alpha_component(&it, end, &s)
|
||||||
|
|| !plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
|| !parse_alpha_component(&it, end, &l)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(plutovg_skip_ws_and_comma(&it, end)
|
||||||
|
&& !parse_alpha_component(&it, end, &a)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
if(!plutovg_skip_delim(&it, end, ')'))
|
||||||
|
return 0;
|
||||||
|
plutovg_color_init_hsla(color, h, s, l, a);
|
||||||
|
} else {
|
||||||
|
static const color_entry_t colormap[] = {
|
||||||
|
{"aliceblue", 0xF0F8FF},
|
||||||
|
{"antiquewhite", 0xFAEBD7},
|
||||||
|
{"aqua", 0x00FFFF},
|
||||||
|
{"aquamarine", 0x7FFFD4},
|
||||||
|
{"azure", 0xF0FFFF},
|
||||||
|
{"beige", 0xF5F5DC},
|
||||||
|
{"bisque", 0xFFE4C4},
|
||||||
|
{"black", 0x000000},
|
||||||
|
{"blanchedalmond", 0xFFEBCD},
|
||||||
|
{"blue", 0x0000FF},
|
||||||
|
{"blueviolet", 0x8A2BE2},
|
||||||
|
{"brown", 0xA52A2A},
|
||||||
|
{"burlywood", 0xDEB887},
|
||||||
|
{"cadetblue", 0x5F9EA0},
|
||||||
|
{"chartreuse", 0x7FFF00},
|
||||||
|
{"chocolate", 0xD2691E},
|
||||||
|
{"coral", 0xFF7F50},
|
||||||
|
{"cornflowerblue", 0x6495ED},
|
||||||
|
{"cornsilk", 0xFFF8DC},
|
||||||
|
{"crimson", 0xDC143C},
|
||||||
|
{"cyan", 0x00FFFF},
|
||||||
|
{"darkblue", 0x00008B},
|
||||||
|
{"darkcyan", 0x008B8B},
|
||||||
|
{"darkgoldenrod", 0xB8860B},
|
||||||
|
{"darkgray", 0xA9A9A9},
|
||||||
|
{"darkgreen", 0x006400},
|
||||||
|
{"darkgrey", 0xA9A9A9},
|
||||||
|
{"darkkhaki", 0xBDB76B},
|
||||||
|
{"darkmagenta", 0x8B008B},
|
||||||
|
{"darkolivegreen", 0x556B2F},
|
||||||
|
{"darkorange", 0xFF8C00},
|
||||||
|
{"darkorchid", 0x9932CC},
|
||||||
|
{"darkred", 0x8B0000},
|
||||||
|
{"darksalmon", 0xE9967A},
|
||||||
|
{"darkseagreen", 0x8FBC8F},
|
||||||
|
{"darkslateblue", 0x483D8B},
|
||||||
|
{"darkslategray", 0x2F4F4F},
|
||||||
|
{"darkslategrey", 0x2F4F4F},
|
||||||
|
{"darkturquoise", 0x00CED1},
|
||||||
|
{"darkviolet", 0x9400D3},
|
||||||
|
{"deeppink", 0xFF1493},
|
||||||
|
{"deepskyblue", 0x00BFFF},
|
||||||
|
{"dimgray", 0x696969},
|
||||||
|
{"dimgrey", 0x696969},
|
||||||
|
{"dodgerblue", 0x1E90FF},
|
||||||
|
{"firebrick", 0xB22222},
|
||||||
|
{"floralwhite", 0xFFFAF0},
|
||||||
|
{"forestgreen", 0x228B22},
|
||||||
|
{"fuchsia", 0xFF00FF},
|
||||||
|
{"gainsboro", 0xDCDCDC},
|
||||||
|
{"ghostwhite", 0xF8F8FF},
|
||||||
|
{"gold", 0xFFD700},
|
||||||
|
{"goldenrod", 0xDAA520},
|
||||||
|
{"gray", 0x808080},
|
||||||
|
{"green", 0x008000},
|
||||||
|
{"greenyellow", 0xADFF2F},
|
||||||
|
{"grey", 0x808080},
|
||||||
|
{"honeydew", 0xF0FFF0},
|
||||||
|
{"hotpink", 0xFF69B4},
|
||||||
|
{"indianred", 0xCD5C5C},
|
||||||
|
{"indigo", 0x4B0082},
|
||||||
|
{"ivory", 0xFFFFF0},
|
||||||
|
{"khaki", 0xF0E68C},
|
||||||
|
{"lavender", 0xE6E6FA},
|
||||||
|
{"lavenderblush", 0xFFF0F5},
|
||||||
|
{"lawngreen", 0x7CFC00},
|
||||||
|
{"lemonchiffon", 0xFFFACD},
|
||||||
|
{"lightblue", 0xADD8E6},
|
||||||
|
{"lightcoral", 0xF08080},
|
||||||
|
{"lightcyan", 0xE0FFFF},
|
||||||
|
{"lightgoldenrodyellow", 0xFAFAD2},
|
||||||
|
{"lightgray", 0xD3D3D3},
|
||||||
|
{"lightgreen", 0x90EE90},
|
||||||
|
{"lightgrey", 0xD3D3D3},
|
||||||
|
{"lightpink", 0xFFB6C1},
|
||||||
|
{"lightsalmon", 0xFFA07A},
|
||||||
|
{"lightseagreen", 0x20B2AA},
|
||||||
|
{"lightskyblue", 0x87CEFA},
|
||||||
|
{"lightslategray", 0x778899},
|
||||||
|
{"lightslategrey", 0x778899},
|
||||||
|
{"lightsteelblue", 0xB0C4DE},
|
||||||
|
{"lightyellow", 0xFFFFE0},
|
||||||
|
{"lime", 0x00FF00},
|
||||||
|
{"limegreen", 0x32CD32},
|
||||||
|
{"linen", 0xFAF0E6},
|
||||||
|
{"magenta", 0xFF00FF},
|
||||||
|
{"maroon", 0x800000},
|
||||||
|
{"mediumaquamarine", 0x66CDAA},
|
||||||
|
{"mediumblue", 0x0000CD},
|
||||||
|
{"mediumorchid", 0xBA55D3},
|
||||||
|
{"mediumpurple", 0x9370DB},
|
||||||
|
{"mediumseagreen", 0x3CB371},
|
||||||
|
{"mediumslateblue", 0x7B68EE},
|
||||||
|
{"mediumspringgreen", 0x00FA9A},
|
||||||
|
{"mediumturquoise", 0x48D1CC},
|
||||||
|
{"mediumvioletred", 0xC71585},
|
||||||
|
{"midnightblue", 0x191970},
|
||||||
|
{"mintcream", 0xF5FFFA},
|
||||||
|
{"mistyrose", 0xFFE4E1},
|
||||||
|
{"moccasin", 0xFFE4B5},
|
||||||
|
{"navajowhite", 0xFFDEAD},
|
||||||
|
{"navy", 0x000080},
|
||||||
|
{"oldlace", 0xFDF5E6},
|
||||||
|
{"olive", 0x808000},
|
||||||
|
{"olivedrab", 0x6B8E23},
|
||||||
|
{"orange", 0xFFA500},
|
||||||
|
{"orangered", 0xFF4500},
|
||||||
|
{"orchid", 0xDA70D6},
|
||||||
|
{"palegoldenrod", 0xEEE8AA},
|
||||||
|
{"palegreen", 0x98FB98},
|
||||||
|
{"paleturquoise", 0xAFEEEE},
|
||||||
|
{"palevioletred", 0xDB7093},
|
||||||
|
{"papayawhip", 0xFFEFD5},
|
||||||
|
{"peachpuff", 0xFFDAB9},
|
||||||
|
{"peru", 0xCD853F},
|
||||||
|
{"pink", 0xFFC0CB},
|
||||||
|
{"plum", 0xDDA0DD},
|
||||||
|
{"powderblue", 0xB0E0E6},
|
||||||
|
{"purple", 0x800080},
|
||||||
|
{"rebeccapurple", 0x663399},
|
||||||
|
{"red", 0xFF0000},
|
||||||
|
{"rosybrown", 0xBC8F8F},
|
||||||
|
{"royalblue", 0x4169E1},
|
||||||
|
{"saddlebrown", 0x8B4513},
|
||||||
|
{"salmon", 0xFA8072},
|
||||||
|
{"sandybrown", 0xF4A460},
|
||||||
|
{"seagreen", 0x2E8B57},
|
||||||
|
{"seashell", 0xFFF5EE},
|
||||||
|
{"sienna", 0xA0522D},
|
||||||
|
{"silver", 0xC0C0C0},
|
||||||
|
{"skyblue", 0x87CEEB},
|
||||||
|
{"slateblue", 0x6A5ACD},
|
||||||
|
{"slategray", 0x708090},
|
||||||
|
{"slategrey", 0x708090},
|
||||||
|
{"snow", 0xFFFAFA},
|
||||||
|
{"springgreen", 0x00FF7F},
|
||||||
|
{"steelblue", 0x4682B4},
|
||||||
|
{"tan", 0xD2B48C},
|
||||||
|
{"teal", 0x008080},
|
||||||
|
{"thistle", 0xD8BFD8},
|
||||||
|
{"tomato", 0xFF6347},
|
||||||
|
{"turquoise", 0x40E0D0},
|
||||||
|
{"violet", 0xEE82EE},
|
||||||
|
{"wheat", 0xF5DEB3},
|
||||||
|
{"white", 0xFFFFFF},
|
||||||
|
{"whitesmoke", 0xF5F5F5},
|
||||||
|
{"yellow", 0xFFFF00},
|
||||||
|
{"yellowgreen", 0x9ACD32}
|
||||||
|
};
|
||||||
|
|
||||||
|
const color_entry_t* entry = bsearch(name, colormap, sizeof(colormap) / sizeof(color_entry_t), sizeof(color_entry_t), color_entry_compare);
|
||||||
|
if(entry == NULL)
|
||||||
|
return 0;
|
||||||
|
plutovg_color_init_argb32(color, 0xFF000000 | entry->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
return it - data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* plutovg_paint_create(plutovg_paint_type_t type, size_t size)
|
||||||
|
{
|
||||||
|
plutovg_paint_t* paint = malloc(size);
|
||||||
|
plutovg_init_reference(paint);
|
||||||
|
paint->type = type;
|
||||||
|
return paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_create_rgb(float r, float g, float b)
|
||||||
|
{
|
||||||
|
return plutovg_paint_create_rgba(r, g, b, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_create_rgba(float r, float g, float b, float a)
|
||||||
|
{
|
||||||
|
plutovg_solid_paint_t* solid = plutovg_paint_create(PLUTOVG_PAINT_TYPE_COLOR, sizeof(plutovg_solid_paint_t));
|
||||||
|
solid->color.r = plutovg_clamp(r, 0.f, 1.f);
|
||||||
|
solid->color.g = plutovg_clamp(g, 0.f, 1.f);
|
||||||
|
solid->color.b = plutovg_clamp(b, 0.f, 1.f);
|
||||||
|
solid->color.a = plutovg_clamp(a, 0.f, 1.f);
|
||||||
|
return &solid->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static plutovg_gradient_paint_t* plutovg_gradient_create(plutovg_gradient_type_t type, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_gradient_paint_t* gradient = plutovg_paint_create(PLUTOVG_PAINT_TYPE_GRADIENT, sizeof(plutovg_gradient_paint_t) + nstops * sizeof(plutovg_gradient_stop_t));
|
||||||
|
gradient->type = type;
|
||||||
|
gradient->spread = spread;
|
||||||
|
gradient->matrix = matrix ? *matrix : PLUTOVG_IDENTITY_MATRIX;
|
||||||
|
gradient->stops = (plutovg_gradient_stop_t*)(gradient + 1);
|
||||||
|
gradient->nstops = nstops;
|
||||||
|
|
||||||
|
float prev_offset = 0.f;
|
||||||
|
for(int i = 0; i < nstops; ++i) {
|
||||||
|
const plutovg_gradient_stop_t* stop = stops + i;
|
||||||
|
gradient->stops[i].offset = plutovg_max(prev_offset, plutovg_clamp(stop->offset, 0.f, 1.f));
|
||||||
|
gradient->stops[i].color.r = plutovg_clamp(stop->color.r, 0.f, 1.f);
|
||||||
|
gradient->stops[i].color.g = plutovg_clamp(stop->color.g, 0.f, 1.f);
|
||||||
|
gradient->stops[i].color.b = plutovg_clamp(stop->color.b, 0.f, 1.f);
|
||||||
|
gradient->stops[i].color.a = plutovg_clamp(stop->color.a, 0.f, 1.f);
|
||||||
|
prev_offset = gradient->stops[i].offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_create_linear_gradient(float x1, float y1, float x2, float y2, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_gradient_paint_t* gradient = plutovg_gradient_create(PLUTOVG_GRADIENT_TYPE_LINEAR, spread, stops, nstops, matrix);
|
||||||
|
gradient->values[0] = x1;
|
||||||
|
gradient->values[1] = y1;
|
||||||
|
gradient->values[2] = x2;
|
||||||
|
gradient->values[3] = y2;
|
||||||
|
return &gradient->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_create_radial_gradient(float cx, float cy, float cr, float fx, float fy, float fr, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_gradient_paint_t* gradient = plutovg_gradient_create(PLUTOVG_GRADIENT_TYPE_RADIAL, spread, stops, nstops, matrix);
|
||||||
|
gradient->values[0] = cx;
|
||||||
|
gradient->values[1] = cy;
|
||||||
|
gradient->values[2] = cr;
|
||||||
|
gradient->values[3] = fx;
|
||||||
|
gradient->values[4] = fy;
|
||||||
|
gradient->values[5] = fr;
|
||||||
|
return &gradient->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_create_texture(plutovg_surface_t* surface, plutovg_texture_type_t type, float opacity, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_texture_paint_t* texture = plutovg_paint_create(PLUTOVG_PAINT_TYPE_TEXTURE, sizeof(plutovg_texture_paint_t));
|
||||||
|
texture->type = type;
|
||||||
|
texture->opacity = plutovg_clamp(opacity, 0.f, 1.f);
|
||||||
|
texture->matrix = matrix ? *matrix : PLUTOVG_IDENTITY_MATRIX;
|
||||||
|
texture->surface = plutovg_surface_reference(surface);
|
||||||
|
return &texture->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_paint_t* plutovg_paint_reference(plutovg_paint_t* paint)
|
||||||
|
{
|
||||||
|
plutovg_increment_reference(paint);
|
||||||
|
return paint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_paint_destroy(plutovg_paint_t* paint)
|
||||||
|
{
|
||||||
|
if(plutovg_destroy_reference(paint)) {
|
||||||
|
if(paint->type == PLUTOVG_PAINT_TYPE_TEXTURE) {
|
||||||
|
plutovg_texture_paint_t* texture = (plutovg_texture_paint_t*)(paint);
|
||||||
|
plutovg_surface_destroy(texture->surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_paint_get_reference_count(const plutovg_paint_t* paint)
|
||||||
|
{
|
||||||
|
return plutovg_get_reference_count(paint);
|
||||||
|
}
|
||||||
918
vendor/lunasvg/plutovg/source/plutovg-path.c
vendored
Normal file
918
vendor/lunasvg/plutovg/source/plutovg-path.c
vendored
Normal file
@@ -0,0 +1,918 @@
|
|||||||
|
#include "plutovg-private.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void plutovg_path_iterator_init(plutovg_path_iterator_t* it, const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
it->elements = path->elements.data;
|
||||||
|
it->size = path->elements.size;
|
||||||
|
it->index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_path_iterator_has_next(const plutovg_path_iterator_t* it)
|
||||||
|
{
|
||||||
|
return it->index < it->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_command_t plutovg_path_iterator_next(plutovg_path_iterator_t* it, plutovg_point_t points[3])
|
||||||
|
{
|
||||||
|
const plutovg_path_element_t* elements = it->elements + it->index;
|
||||||
|
switch(elements[0].header.command) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
points[0] = elements[1].point;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
points[0] = elements[1].point;
|
||||||
|
points[1] = elements[2].point;
|
||||||
|
points[2] = elements[3].point;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->index += elements[0].header.length;
|
||||||
|
return elements[0].header.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_path_create(void)
|
||||||
|
{
|
||||||
|
plutovg_path_t* path = malloc(sizeof(plutovg_path_t));
|
||||||
|
plutovg_init_reference(path);
|
||||||
|
path->num_points = 0;
|
||||||
|
path->num_contours = 0;
|
||||||
|
path->num_curves = 0;
|
||||||
|
path->start_point = PLUTOVG_EMPTY_POINT;
|
||||||
|
plutovg_array_init(path->elements);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_path_reference(plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_increment_reference(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_destroy(plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
if(plutovg_destroy_reference(path)) {
|
||||||
|
plutovg_array_destroy(path->elements);
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_path_get_reference_count(const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
return plutovg_get_reference_count(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_path_get_elements(const plutovg_path_t* path, const plutovg_path_element_t** elements)
|
||||||
|
{
|
||||||
|
if(elements)
|
||||||
|
*elements = path->elements.data;
|
||||||
|
return path->elements.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static plutovg_path_element_t* plutovg_path_add_command(plutovg_path_t* path, plutovg_path_command_t command, int npoints)
|
||||||
|
{
|
||||||
|
const int length = npoints + 1;
|
||||||
|
plutovg_array_ensure(path->elements, length);
|
||||||
|
plutovg_path_element_t* elements = path->elements.data + path->elements.size;
|
||||||
|
elements->header.command = command;
|
||||||
|
elements->header.length = length;
|
||||||
|
path->elements.size += length;
|
||||||
|
path->num_points += npoints;
|
||||||
|
return elements + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_move_to(plutovg_path_t* path, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_element_t* elements = plutovg_path_add_command(path, PLUTOVG_PATH_COMMAND_MOVE_TO, 1);
|
||||||
|
elements[0].point = PLUTOVG_MAKE_POINT(x, y);
|
||||||
|
path->start_point = PLUTOVG_MAKE_POINT(x, y);
|
||||||
|
path->num_contours += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_line_to(plutovg_path_t* path, float x, float y)
|
||||||
|
{
|
||||||
|
if(path->elements.size == 0)
|
||||||
|
plutovg_path_move_to(path, 0, 0);
|
||||||
|
plutovg_path_element_t* elements = plutovg_path_add_command(path, PLUTOVG_PATH_COMMAND_LINE_TO, 1);
|
||||||
|
elements[0].point = PLUTOVG_MAKE_POINT(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_quad_to(plutovg_path_t* path, float x1, float y1, float x2, float y2)
|
||||||
|
{
|
||||||
|
float current_x, current_y;
|
||||||
|
plutovg_path_get_current_point(path, ¤t_x, ¤t_y);
|
||||||
|
float cp1x = 2.f / 3.f * x1 + 1.f / 3.f * current_x;
|
||||||
|
float cp1y = 2.f / 3.f * y1 + 1.f / 3.f * current_y;
|
||||||
|
float cp2x = 2.f / 3.f * x1 + 1.f / 3.f * x2;
|
||||||
|
float cp2y = 2.f / 3.f * y1 + 1.f / 3.f * y2;
|
||||||
|
plutovg_path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_cubic_to(plutovg_path_t* path, float x1, float y1, float x2, float y2, float x3, float y3)
|
||||||
|
{
|
||||||
|
if(path->elements.size == 0)
|
||||||
|
plutovg_path_move_to(path, 0, 0);
|
||||||
|
plutovg_path_element_t* elements = plutovg_path_add_command(path, PLUTOVG_PATH_COMMAND_CUBIC_TO, 3);
|
||||||
|
elements[0].point = PLUTOVG_MAKE_POINT(x1, y1);
|
||||||
|
elements[1].point = PLUTOVG_MAKE_POINT(x2, y2);
|
||||||
|
elements[2].point = PLUTOVG_MAKE_POINT(x3, y3);
|
||||||
|
path->num_curves += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_arc_to(plutovg_path_t* path, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
|
||||||
|
{
|
||||||
|
float current_x, current_y;
|
||||||
|
plutovg_path_get_current_point(path, ¤t_x, ¤t_y);
|
||||||
|
if(rx == 0.f || ry == 0.f || (current_x == x && current_y == y)) {
|
||||||
|
plutovg_path_line_to(path, x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rx < 0.f) rx = -rx;
|
||||||
|
if(ry < 0.f) ry = -ry;
|
||||||
|
|
||||||
|
float dx = (current_x - x) * 0.5f;
|
||||||
|
float dy = (current_y - y) * 0.5f;
|
||||||
|
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_matrix_init_rotate(&matrix, -angle);
|
||||||
|
plutovg_matrix_map(&matrix, dx, dy, &dx, &dy);
|
||||||
|
|
||||||
|
float rxrx = rx * rx;
|
||||||
|
float ryry = ry * ry;
|
||||||
|
float dxdx = dx * dx;
|
||||||
|
float dydy = dy * dy;
|
||||||
|
float radius = dxdx / rxrx + dydy / ryry;
|
||||||
|
if(radius > 1.f) {
|
||||||
|
rx *= sqrtf(radius);
|
||||||
|
ry *= sqrtf(radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_matrix_init_scale(&matrix, 1.f / rx, 1.f / ry);
|
||||||
|
plutovg_matrix_rotate(&matrix, -angle);
|
||||||
|
|
||||||
|
float x1, y1;
|
||||||
|
float x2, y2;
|
||||||
|
plutovg_matrix_map(&matrix, current_x, current_y, &x1, &y1);
|
||||||
|
plutovg_matrix_map(&matrix, x, y, &x2, &y2);
|
||||||
|
|
||||||
|
float dx1 = x2 - x1;
|
||||||
|
float dy1 = y2 - y1;
|
||||||
|
float d = dx1 * dx1 + dy1 * dy1;
|
||||||
|
float scale_sq = 1.f / d - 0.25f;
|
||||||
|
if(scale_sq < 0.f) scale_sq = 0.f;
|
||||||
|
float scale = sqrtf(scale_sq);
|
||||||
|
if(sweep_flag == large_arc_flag)
|
||||||
|
scale = -scale;
|
||||||
|
dx1 *= scale;
|
||||||
|
dy1 *= scale;
|
||||||
|
|
||||||
|
float cx1 = 0.5f * (x1 + x2) - dy1;
|
||||||
|
float cy1 = 0.5f * (y1 + y2) + dx1;
|
||||||
|
|
||||||
|
float th1 = atan2f(y1 - cy1, x1 - cx1);
|
||||||
|
float th2 = atan2f(y2 - cy1, x2 - cx1);
|
||||||
|
float th_arc = th2 - th1;
|
||||||
|
if(th_arc < 0.f && sweep_flag)
|
||||||
|
th_arc += PLUTOVG_TWO_PI;
|
||||||
|
else if(th_arc > 0.f && !sweep_flag)
|
||||||
|
th_arc -= PLUTOVG_TWO_PI;
|
||||||
|
plutovg_matrix_init_rotate(&matrix, angle);
|
||||||
|
plutovg_matrix_scale(&matrix, rx, ry);
|
||||||
|
int segments = (int)(ceilf(fabsf(th_arc / (PLUTOVG_HALF_PI + 0.001f))));
|
||||||
|
for(int i = 0; i < segments; i++) {
|
||||||
|
float th_start = th1 + i * th_arc / segments;
|
||||||
|
float th_end = th1 + (i + 1) * th_arc / segments;
|
||||||
|
float t = (8.f / 6.f) * tanf(0.25f * (th_end - th_start));
|
||||||
|
|
||||||
|
float x3 = cosf(th_end) + cx1;
|
||||||
|
float y3 = sinf(th_end) + cy1;
|
||||||
|
|
||||||
|
float cp2x = x3 + t * sinf(th_end);
|
||||||
|
float cp2y = y3 - t * cosf(th_end);
|
||||||
|
|
||||||
|
float cp1x = cosf(th_start) - t * sinf(th_start);
|
||||||
|
float cp1y = sinf(th_start) + t * cosf(th_start);
|
||||||
|
|
||||||
|
cp1x += cx1;
|
||||||
|
cp1y += cy1;
|
||||||
|
|
||||||
|
plutovg_matrix_map(&matrix, cp1x, cp1y, &cp1x, &cp1y);
|
||||||
|
plutovg_matrix_map(&matrix, cp2x, cp2y, &cp2x, &cp2y);
|
||||||
|
plutovg_matrix_map(&matrix, x3, y3, &x3, &y3);
|
||||||
|
|
||||||
|
plutovg_path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, x3, y3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_close(plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
if(path->elements.size == 0)
|
||||||
|
return;
|
||||||
|
plutovg_path_element_t* elements = plutovg_path_add_command(path, PLUTOVG_PATH_COMMAND_CLOSE, 1);
|
||||||
|
elements[0].point = path->start_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_get_current_point(const plutovg_path_t* path, float* x, float* y)
|
||||||
|
{
|
||||||
|
float xx = 0.f;
|
||||||
|
float yy = 0.f;
|
||||||
|
if(path->num_points > 0) {
|
||||||
|
xx = path->elements.data[path->elements.size - 1].point.x;
|
||||||
|
yy = path->elements.data[path->elements.size - 1].point.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x) *x = xx;
|
||||||
|
if(y) *y = yy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_reserve(plutovg_path_t* path, int count)
|
||||||
|
{
|
||||||
|
plutovg_array_ensure(path->elements, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_reset(plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_array_clear(path->elements);
|
||||||
|
path->start_point = PLUTOVG_EMPTY_POINT;
|
||||||
|
path->num_points = 0;
|
||||||
|
path->num_contours = 0;
|
||||||
|
path->num_curves = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_add_rect(plutovg_path_t* path, float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_path_reserve(path, 6 * 2);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_add_round_rect(plutovg_path_t* path, float x, float y, float w, float h, float rx, float ry)
|
||||||
|
{
|
||||||
|
rx = plutovg_min(rx, w * 0.5f);
|
||||||
|
ry = plutovg_min(ry, h * 0.5f);
|
||||||
|
if(rx == 0.f && ry == 0.f) {
|
||||||
|
plutovg_path_add_rect(path, x, y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float right = x + w;
|
||||||
|
float bottom = y + h;
|
||||||
|
|
||||||
|
float cpx = rx * PLUTOVG_KAPPA;
|
||||||
|
float cpy = ry * PLUTOVG_KAPPA;
|
||||||
|
|
||||||
|
plutovg_path_reserve(path, 6 * 2 + 4 * 4);
|
||||||
|
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, float cx, float cy, float rx, float ry)
|
||||||
|
{
|
||||||
|
float left = cx - rx;
|
||||||
|
float top = cy - ry;
|
||||||
|
float right = cx + rx;
|
||||||
|
float bottom = cy + ry;
|
||||||
|
|
||||||
|
float cpx = rx * PLUTOVG_KAPPA;
|
||||||
|
float cpy = ry * PLUTOVG_KAPPA;
|
||||||
|
|
||||||
|
plutovg_path_reserve(path, 2 * 2 + 4 * 4);
|
||||||
|
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, float cx, float cy, float r)
|
||||||
|
{
|
||||||
|
plutovg_path_add_ellipse(path, cx, cy, r, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_add_arc(plutovg_path_t* path, float cx, float cy, float r, float a0, float a1, bool ccw)
|
||||||
|
{
|
||||||
|
float da = a1 - a0;
|
||||||
|
if(fabsf(da) > PLUTOVG_TWO_PI) {
|
||||||
|
da = PLUTOVG_TWO_PI;
|
||||||
|
} else if(da != 0.f && ccw != (da < 0.f)) {
|
||||||
|
da += PLUTOVG_TWO_PI * (ccw ? -1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int seg_n = (int)(ceilf(fabsf(da) / PLUTOVG_HALF_PI));
|
||||||
|
if(seg_n == 0)
|
||||||
|
return;
|
||||||
|
float a = a0;
|
||||||
|
float ax = cx + cosf(a) * r;
|
||||||
|
float ay = cy + sinf(a) * r;
|
||||||
|
|
||||||
|
float seg_a = da / seg_n;
|
||||||
|
float d = (seg_a / PLUTOVG_HALF_PI) * PLUTOVG_KAPPA * r;
|
||||||
|
float dx = -sinf(a) * d;
|
||||||
|
float dy = cosf(a) * d;
|
||||||
|
|
||||||
|
plutovg_path_reserve(path, 2 + 4 * seg_n);
|
||||||
|
if(path->elements.size == 0) {
|
||||||
|
plutovg_path_move_to(path, ax, ay);
|
||||||
|
} else {
|
||||||
|
plutovg_path_line_to(path, ax, ay);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < seg_n; i++) {
|
||||||
|
float cp1x = ax + dx;
|
||||||
|
float cp1y = ay + dy;
|
||||||
|
|
||||||
|
a += seg_a;
|
||||||
|
ax = cx + cosf(a) * r;
|
||||||
|
ay = cy + sinf(a) * r;
|
||||||
|
|
||||||
|
dx = -sinf(a) * d;
|
||||||
|
dy = cosf(a) * d;
|
||||||
|
|
||||||
|
float cp2x = ax - dx;
|
||||||
|
float cp2y = ay - dy;
|
||||||
|
|
||||||
|
plutovg_path_cubic_to(path, cp1x, cp1y, cp2x, cp2y, ax, ay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_transform(plutovg_path_t* path, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
plutovg_path_element_t* elements = path->elements.data;
|
||||||
|
for(int i = 0; i < path->elements.size; i += elements[i].header.length) {
|
||||||
|
switch(elements[i].header.command) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
plutovg_matrix_map_point(matrix, &elements[i + 1].point, &elements[i + 1].point);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
plutovg_matrix_map_point(matrix, &elements[i + 1].point, &elements[i + 1].point);
|
||||||
|
plutovg_matrix_map_point(matrix, &elements[i + 2].point, &elements[i + 2].point);
|
||||||
|
plutovg_matrix_map_point(matrix, &elements[i + 3].point, &elements[i + 3].point);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_add_path(plutovg_path_t* path, const plutovg_path_t* source, const plutovg_matrix_t* matrix)
|
||||||
|
{
|
||||||
|
if(matrix == NULL) {
|
||||||
|
plutovg_array_append(path->elements, source->elements);
|
||||||
|
path->start_point = source->start_point;
|
||||||
|
path->num_points += source->num_points;
|
||||||
|
path->num_contours += source->num_contours;
|
||||||
|
path->num_curves += source->num_curves;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_iterator_t it;
|
||||||
|
plutovg_path_iterator_init(&it, source);
|
||||||
|
|
||||||
|
plutovg_point_t points[3];
|
||||||
|
plutovg_array_ensure(path->elements, source->elements.size);
|
||||||
|
while(plutovg_path_iterator_has_next(&it)) {
|
||||||
|
switch(plutovg_path_iterator_next(&it, points)) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 1);
|
||||||
|
plutovg_path_move_to(path, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 1);
|
||||||
|
plutovg_path_line_to(path, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 3);
|
||||||
|
plutovg_path_cubic_to(path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
plutovg_path_close(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_traverse(const plutovg_path_t* path, plutovg_path_traverse_func_t traverse_func, void* closure)
|
||||||
|
{
|
||||||
|
plutovg_path_iterator_t it;
|
||||||
|
plutovg_path_iterator_init(&it, path);
|
||||||
|
|
||||||
|
plutovg_point_t points[3];
|
||||||
|
while(plutovg_path_iterator_has_next(&it)) {
|
||||||
|
switch(plutovg_path_iterator_next(&it, points)) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
traverse_func(closure, PLUTOVG_PATH_COMMAND_MOVE_TO, points, 1);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
traverse_func(closure, PLUTOVG_PATH_COMMAND_LINE_TO, points, 1);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
traverse_func(closure, PLUTOVG_PATH_COMMAND_CUBIC_TO, points, 3);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
traverse_func(closure, PLUTOVG_PATH_COMMAND_CLOSE, points, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x1; float y1;
|
||||||
|
float x2; float y2;
|
||||||
|
float x3; float y3;
|
||||||
|
float x4; float y4;
|
||||||
|
} bezier_t;
|
||||||
|
|
||||||
|
static inline void split_bezier(const bezier_t* b, bezier_t* first, bezier_t* second)
|
||||||
|
{
|
||||||
|
float c = (b->x2 + b->x3) * 0.5f;
|
||||||
|
first->x2 = (b->x1 + b->x2) * 0.5f;
|
||||||
|
second->x3 = (b->x3 + b->x4) * 0.5f;
|
||||||
|
first->x1 = b->x1;
|
||||||
|
second->x4 = b->x4;
|
||||||
|
first->x3 = (first->x2 + c) * 0.5f;
|
||||||
|
second->x2 = (second->x3 + c) * 0.5f;
|
||||||
|
first->x4 = second->x1 = (first->x3 + second->x2) * 0.5f;
|
||||||
|
|
||||||
|
c = (b->y2 + b->y3) * 0.5f;
|
||||||
|
first->y2 = (b->y1 + b->y2) * 0.5f;
|
||||||
|
second->y3 = (b->y3 + b->y4) * 0.5f;
|
||||||
|
first->y1 = b->y1;
|
||||||
|
second->y4 = b->y4;
|
||||||
|
first->y3 = (first->y2 + c) * 0.5f;
|
||||||
|
second->y2 = (second->y3 + c) * 0.5f;
|
||||||
|
first->y4 = second->y1 = (first->y3 + second->y2) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_traverse_flatten(const plutovg_path_t* path, plutovg_path_traverse_func_t traverse_func, void* closure)
|
||||||
|
{
|
||||||
|
if(path->num_curves == 0) {
|
||||||
|
plutovg_path_traverse(path, traverse_func, closure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float threshold = 0.25f;
|
||||||
|
|
||||||
|
plutovg_path_iterator_t it;
|
||||||
|
plutovg_path_iterator_init(&it, path);
|
||||||
|
|
||||||
|
bezier_t beziers[32];
|
||||||
|
plutovg_point_t points[3];
|
||||||
|
plutovg_point_t current_point = {0, 0};
|
||||||
|
while(plutovg_path_iterator_has_next(&it)) {
|
||||||
|
plutovg_path_command_t command = plutovg_path_iterator_next(&it, points);
|
||||||
|
switch(command) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
traverse_func(closure, command, points, 1);
|
||||||
|
current_point = points[0];
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
beziers[0].x1 = current_point.x;
|
||||||
|
beziers[0].y1 = current_point.y;
|
||||||
|
beziers[0].x2 = points[0].x;
|
||||||
|
beziers[0].y2 = points[0].y;
|
||||||
|
beziers[0].x3 = points[1].x;
|
||||||
|
beziers[0].y3 = points[1].y;
|
||||||
|
beziers[0].x4 = points[2].x;
|
||||||
|
beziers[0].y4 = points[2].y;
|
||||||
|
bezier_t* b = beziers;
|
||||||
|
while(b >= beziers) {
|
||||||
|
float y4y1 = b->y4 - b->y1;
|
||||||
|
float x4x1 = b->x4 - b->x1;
|
||||||
|
float l = fabsf(x4x1) + fabsf(y4y1);
|
||||||
|
float d;
|
||||||
|
if(l > 1.f) {
|
||||||
|
d = fabsf((x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2)) + fabsf((x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3));
|
||||||
|
} else {
|
||||||
|
d = fabsf(b->x1 - b->x2) + fabsf(b->y1 - b->y2) + fabsf(b->x1 - b->x3) + fabsf(b->y1 - b->y3);
|
||||||
|
l = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(d < threshold*l || b == beziers + 31) {
|
||||||
|
plutovg_point_t p = { b->x4, b->y4 };
|
||||||
|
traverse_func(closure, PLUTOVG_PATH_COMMAND_LINE_TO, &p, 1);
|
||||||
|
--b;
|
||||||
|
} else {
|
||||||
|
split_bezier(b, b + 1, b);
|
||||||
|
++b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_point = points[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const float* dashes; int ndashes;
|
||||||
|
float start_phase; float phase;
|
||||||
|
int start_index; int index;
|
||||||
|
bool start_toggle; bool toggle;
|
||||||
|
plutovg_point_t current_point;
|
||||||
|
plutovg_path_traverse_func_t traverse_func;
|
||||||
|
void* closure;
|
||||||
|
} dasher_t;
|
||||||
|
|
||||||
|
static void dash_traverse_func(void* closure, plutovg_path_command_t command, const plutovg_point_t* points, int npoints)
|
||||||
|
{
|
||||||
|
dasher_t* dasher = (dasher_t*)(closure);
|
||||||
|
if(command == PLUTOVG_PATH_COMMAND_MOVE_TO) {
|
||||||
|
if(dasher->start_toggle)
|
||||||
|
dasher->traverse_func(dasher->closure, PLUTOVG_PATH_COMMAND_MOVE_TO, points, npoints);
|
||||||
|
dasher->current_point = points[0];
|
||||||
|
dasher->phase = dasher->start_phase;
|
||||||
|
dasher->index = dasher->start_index;
|
||||||
|
dasher->toggle = dasher->start_toggle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(command == PLUTOVG_PATH_COMMAND_LINE_TO || command == PLUTOVG_PATH_COMMAND_CLOSE);
|
||||||
|
plutovg_point_t p0 = dasher->current_point;
|
||||||
|
plutovg_point_t p1 = points[0];
|
||||||
|
float dx = p1.x - p0.x;
|
||||||
|
float dy = p1.y - p0.y;
|
||||||
|
float dist0 = sqrtf(dx*dx + dy*dy);
|
||||||
|
float dist1 = 0.f;
|
||||||
|
while(dist0 - dist1 > dasher->dashes[dasher->index % dasher->ndashes] - dasher->phase) {
|
||||||
|
dist1 += dasher->dashes[dasher->index % dasher->ndashes] - dasher->phase;
|
||||||
|
float a = dist1 / dist0;
|
||||||
|
plutovg_point_t p = { p0.x + a * dx, p0.y + a * dy };
|
||||||
|
if(dasher->toggle) {
|
||||||
|
dasher->traverse_func(dasher->closure, PLUTOVG_PATH_COMMAND_LINE_TO, &p, 1);
|
||||||
|
} else {
|
||||||
|
dasher->traverse_func(dasher->closure, PLUTOVG_PATH_COMMAND_MOVE_TO, &p, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dasher->phase = 0.f;
|
||||||
|
dasher->toggle = !dasher->toggle;
|
||||||
|
dasher->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dasher->toggle) {
|
||||||
|
dasher->traverse_func(dasher->closure, PLUTOVG_PATH_COMMAND_LINE_TO, &p1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dasher->phase += dist0 - dist1;
|
||||||
|
dasher->current_point = p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_path_traverse_dashed(const plutovg_path_t* path, float offset, const float* dashes, int ndashes, plutovg_path_traverse_func_t traverse_func, void* closure)
|
||||||
|
{
|
||||||
|
float dash_sum = 0.f;
|
||||||
|
for(int i = 0; i < ndashes; ++i)
|
||||||
|
dash_sum += dashes[i];
|
||||||
|
if(ndashes % 2 == 1)
|
||||||
|
dash_sum *= 2.f;
|
||||||
|
if(dash_sum <= 0.f) {
|
||||||
|
plutovg_path_traverse(path, traverse_func, closure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dasher_t dasher;
|
||||||
|
dasher.dashes = dashes;
|
||||||
|
dasher.ndashes = ndashes;
|
||||||
|
dasher.start_phase = fmodf(offset, dash_sum);
|
||||||
|
if(dasher.start_phase < 0.f)
|
||||||
|
dasher.start_phase += dash_sum;
|
||||||
|
dasher.start_index = 0;
|
||||||
|
dasher.start_toggle = true;
|
||||||
|
while(dasher.start_phase > 0.f && dasher.start_phase >= dasher.dashes[dasher.start_index % dasher.ndashes]) {
|
||||||
|
dasher.start_phase -= dashes[dasher.start_index % dasher.ndashes];
|
||||||
|
dasher.start_toggle = !dasher.start_toggle;
|
||||||
|
dasher.start_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dasher.phase = dasher.start_phase;
|
||||||
|
dasher.index = dasher.start_index;
|
||||||
|
dasher.toggle = dasher.start_toggle;
|
||||||
|
dasher.current_point = PLUTOVG_EMPTY_POINT;
|
||||||
|
dasher.traverse_func = traverse_func;
|
||||||
|
dasher.closure = closure;
|
||||||
|
plutovg_path_traverse_flatten(path, dash_traverse_func, &dasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_path_clone(const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_path_t* clone = plutovg_path_create();
|
||||||
|
plutovg_array_append(clone->elements, path->elements);
|
||||||
|
clone->start_point = path->start_point;
|
||||||
|
clone->num_points = path->num_points;
|
||||||
|
clone->num_contours = path->num_contours;
|
||||||
|
clone->num_curves = path->num_curves;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clone_traverse_func(void* closure, plutovg_path_command_t command, const plutovg_point_t* points, int npoints)
|
||||||
|
{
|
||||||
|
plutovg_path_t* path = (plutovg_path_t*)(closure);
|
||||||
|
switch(command) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
plutovg_path_move_to(path, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
plutovg_path_line_to(path, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
plutovg_path_cubic_to(path, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
plutovg_path_close(path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_path_clone_flatten(const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
plutovg_path_t* clone = plutovg_path_create();
|
||||||
|
plutovg_path_reserve(clone, path->elements.size + path->num_curves * 32);
|
||||||
|
plutovg_path_traverse_flatten(path, clone_traverse_func, clone);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* plutovg_path_clone_dashed(const plutovg_path_t* path, float offset, const float* dashes, int ndashes)
|
||||||
|
{
|
||||||
|
plutovg_path_t* clone = plutovg_path_create();
|
||||||
|
plutovg_path_reserve(clone, path->elements.size + path->num_curves * 32);
|
||||||
|
plutovg_path_traverse_dashed(path, offset, dashes, ndashes, clone_traverse_func, clone);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
plutovg_point_t current_point;
|
||||||
|
bool is_first_point;
|
||||||
|
float length;
|
||||||
|
float x1;
|
||||||
|
float y1;
|
||||||
|
float x2;
|
||||||
|
float y2;
|
||||||
|
} extents_calculator_t;
|
||||||
|
|
||||||
|
static void extents_traverse_func(void* closure, plutovg_path_command_t command, const plutovg_point_t* points, int npoints)
|
||||||
|
{
|
||||||
|
extents_calculator_t* calculator = (extents_calculator_t*)(closure);
|
||||||
|
if(calculator->is_first_point) {
|
||||||
|
assert(command == PLUTOVG_PATH_COMMAND_MOVE_TO);
|
||||||
|
calculator->is_first_point = false;
|
||||||
|
calculator->current_point = points[0];
|
||||||
|
calculator->x1 = points[0].x;
|
||||||
|
calculator->y1 = points[0].y;
|
||||||
|
calculator->x2 = points[0].x;
|
||||||
|
calculator->y2 = points[0].y;
|
||||||
|
calculator->length = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < npoints; ++i) {
|
||||||
|
calculator->x1 = plutovg_min(calculator->x1, points[i].x);
|
||||||
|
calculator->y1 = plutovg_min(calculator->y1, points[i].y);
|
||||||
|
calculator->x2 = plutovg_max(calculator->x2, points[i].x);
|
||||||
|
calculator->y2 = plutovg_max(calculator->y2, points[i].y);
|
||||||
|
if(command != PLUTOVG_PATH_COMMAND_MOVE_TO)
|
||||||
|
calculator->length += hypotf(points[i].x - calculator->current_point.x, points[i].y - calculator->current_point.y);
|
||||||
|
calculator->current_point = points[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_path_extents(const plutovg_path_t* path, plutovg_rect_t* extents, bool tight)
|
||||||
|
{
|
||||||
|
extents_calculator_t calculator = {{0, 0}, true, 0, 0, 0, 0, 0};
|
||||||
|
if(tight) {
|
||||||
|
plutovg_path_traverse_flatten(path, extents_traverse_func, &calculator);
|
||||||
|
} else {
|
||||||
|
plutovg_path_traverse(path, extents_traverse_func, &calculator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extents) {
|
||||||
|
extents->x = calculator.x1;
|
||||||
|
extents->y = calculator.y1;
|
||||||
|
extents->w = calculator.x2 - calculator.x1;
|
||||||
|
extents->h = calculator.y2 - calculator.y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculator.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
float plutovg_path_length(const plutovg_path_t* path)
|
||||||
|
{
|
||||||
|
return plutovg_path_extents(path, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool parse_arc_flag(const char** begin, const char* end, bool* flag)
|
||||||
|
{
|
||||||
|
if(plutovg_skip_delim(begin, end, '0'))
|
||||||
|
*flag = 0;
|
||||||
|
else if(plutovg_skip_delim(begin, end, '1'))
|
||||||
|
*flag = 1;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
plutovg_skip_ws_or_comma(begin, end, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool parse_path_coordinates(const char** begin, const char* end, float values[6], int offset, int count)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < count; i++) {
|
||||||
|
if(!plutovg_parse_number(begin, end, values + offset + i))
|
||||||
|
return false;
|
||||||
|
plutovg_skip_ws_or_comma(begin, end, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_path_parse(plutovg_path_t* path, const char* data, int length)
|
||||||
|
{
|
||||||
|
if(length == -1)
|
||||||
|
length = strlen(data);
|
||||||
|
const char* it = data;
|
||||||
|
const char* end = it + length;
|
||||||
|
|
||||||
|
float values[6];
|
||||||
|
bool flags[2];
|
||||||
|
|
||||||
|
float start_x = 0;
|
||||||
|
float start_y = 0;
|
||||||
|
float current_x = 0;
|
||||||
|
float current_y = 0;
|
||||||
|
float last_control_x = 0;
|
||||||
|
float last_control_y = 0;
|
||||||
|
|
||||||
|
char command = 0;
|
||||||
|
char last_command = 0;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
while(it < end) {
|
||||||
|
if(PLUTOVG_IS_ALPHA(*it)) {
|
||||||
|
command = *it++;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!last_command && !(command == 'M' || command == 'm'))
|
||||||
|
return false;
|
||||||
|
if(command == 'M' || command == 'm') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 2))
|
||||||
|
return false;
|
||||||
|
if(command == 'm') {
|
||||||
|
values[0] += current_x;
|
||||||
|
values[1] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_move_to(path, values[0], values[1]);
|
||||||
|
current_x = start_x = values[0];
|
||||||
|
current_y = start_y = values[1];
|
||||||
|
command = command == 'm' ? 'l' : 'L';
|
||||||
|
} else if(command == 'L' || command == 'l') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 2))
|
||||||
|
return false;
|
||||||
|
if(command == 'l') {
|
||||||
|
values[0] += current_x;
|
||||||
|
values[1] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_line_to(path, values[0], values[1]);
|
||||||
|
current_x = values[0];
|
||||||
|
current_y = values[1];
|
||||||
|
} else if(command == 'H' || command == 'h') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 1))
|
||||||
|
return false;
|
||||||
|
if(command == 'h') {
|
||||||
|
values[0] += current_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_line_to(path, values[0], current_y);
|
||||||
|
current_x = values[0];
|
||||||
|
} else if(command == 'V' || command == 'v') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 1, 1))
|
||||||
|
return false;
|
||||||
|
if(command == 'v') {
|
||||||
|
values[1] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_line_to(path, current_x, values[1]);
|
||||||
|
current_y = values[1];
|
||||||
|
} else if(command == 'Q' || command == 'q') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 4))
|
||||||
|
return false;
|
||||||
|
if(command == 'q') {
|
||||||
|
values[0] += current_x;
|
||||||
|
values[1] += current_y;
|
||||||
|
values[2] += current_x;
|
||||||
|
values[3] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_quad_to(path, values[0], values[1], values[2], values[3]);
|
||||||
|
last_control_x = values[0];
|
||||||
|
last_control_y = values[1];
|
||||||
|
current_x = values[2];
|
||||||
|
current_y = values[3];
|
||||||
|
} else if(command == 'C' || command == 'c') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 6))
|
||||||
|
return false;
|
||||||
|
if(command == 'c') {
|
||||||
|
values[0] += current_x;
|
||||||
|
values[1] += current_y;
|
||||||
|
values[2] += current_x;
|
||||||
|
values[3] += current_y;
|
||||||
|
values[4] += current_x;
|
||||||
|
values[5] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_cubic_to(path, values[0], values[1], values[2], values[3], values[4], values[5]);
|
||||||
|
last_control_x = values[2];
|
||||||
|
last_control_y = values[3];
|
||||||
|
current_x = values[4];
|
||||||
|
current_y = values[5];
|
||||||
|
} else if(command == 'T' || command == 't') {
|
||||||
|
if(last_command != 'Q' && last_command != 'q' && last_command != 'T' && last_command != 't') {
|
||||||
|
values[0] = current_x;
|
||||||
|
values[1] = current_y;
|
||||||
|
} else {
|
||||||
|
values[0] = 2 * current_x - last_control_x;
|
||||||
|
values[1] = 2 * current_y - last_control_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 2, 2))
|
||||||
|
return false;
|
||||||
|
if(command == 't') {
|
||||||
|
values[2] += current_x;
|
||||||
|
values[3] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_quad_to(path, values[0], values[1], values[2], values[3]);
|
||||||
|
last_control_x = values[0];
|
||||||
|
last_control_y = values[1];
|
||||||
|
current_x = values[2];
|
||||||
|
current_y = values[3];
|
||||||
|
} else if(command == 'S' || command == 's') {
|
||||||
|
if(last_command != 'C' && last_command != 'c' && last_command != 'S' && last_command != 's') {
|
||||||
|
values[0] = current_x;
|
||||||
|
values[1] = current_y;
|
||||||
|
} else {
|
||||||
|
values[0] = 2 * current_x - last_control_x;
|
||||||
|
values[1] = 2 * current_y - last_control_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 2, 4))
|
||||||
|
return false;
|
||||||
|
if(command == 's') {
|
||||||
|
values[2] += current_x;
|
||||||
|
values[3] += current_y;
|
||||||
|
values[4] += current_x;
|
||||||
|
values[5] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_cubic_to(path, values[0], values[1], values[2], values[3], values[4], values[5]);
|
||||||
|
last_control_x = values[2];
|
||||||
|
last_control_y = values[3];
|
||||||
|
current_x = values[4];
|
||||||
|
current_y = values[5];
|
||||||
|
} else if(command == 'A' || command == 'a') {
|
||||||
|
if(!parse_path_coordinates(&it, end, values, 0, 3)
|
||||||
|
|| !parse_arc_flag(&it, end, &flags[0])
|
||||||
|
|| !parse_arc_flag(&it, end, &flags[1])
|
||||||
|
|| !parse_path_coordinates(&it, end, values, 3, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(command == 'a') {
|
||||||
|
values[3] += current_x;
|
||||||
|
values[4] += current_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_arc_to(path, values[0], values[1], PLUTOVG_DEG2RAD(values[2]), flags[0], flags[1], values[3], values[4]);
|
||||||
|
current_x = values[3];
|
||||||
|
current_y = values[4];
|
||||||
|
} else if(command == 'Z' || command == 'z') {
|
||||||
|
if(last_command == 'Z' || last_command == 'z')
|
||||||
|
return false;
|
||||||
|
plutovg_path_close(path);
|
||||||
|
current_x = start_x;
|
||||||
|
current_y = start_y;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
180
vendor/lunasvg/plutovg/source/plutovg-private.h
vendored
Normal file
180
vendor/lunasvg/plutovg/source/plutovg-private.h
vendored
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#ifndef PLUTOVG_PRIVATE_H
|
||||||
|
#define PLUTOVG_PRIVATE_H
|
||||||
|
|
||||||
|
#include "plutovg.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
typedef LONG plutovg_ref_count_t;
|
||||||
|
|
||||||
|
#define plutovg_init_reference(ob) ((ob)->ref_count = 1)
|
||||||
|
#define plutovg_increment_reference(ob) (void)(ob && InterlockedIncrement(&(ob)->ref_count))
|
||||||
|
#define plutovg_destroy_reference(ob) (ob && InterlockedDecrement(&(ob)->ref_count) == 0)
|
||||||
|
#define plutovg_get_reference_count(ob) ((ob) ? InterlockedCompareExchange((LONG*)&(ob)->ref_count, 0, 0) : 0)
|
||||||
|
|
||||||
|
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
typedef atomic_int plutovg_ref_count_t;
|
||||||
|
|
||||||
|
#define plutovg_init_reference(ob) atomic_init(&(ob)->ref_count, 1)
|
||||||
|
#define plutovg_increment_reference(ob) (void)(ob && atomic_fetch_add(&(ob)->ref_count, 1))
|
||||||
|
#define plutovg_destroy_reference(ob) (ob && atomic_fetch_sub(&(ob)->ref_count, 1) == 1)
|
||||||
|
#define plutovg_get_reference_count(ob) ((ob) ? atomic_load(&(ob)->ref_count) : 0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef int plutovg_ref_count_t;
|
||||||
|
|
||||||
|
#define plutovg_init_reference(ob) ((ob)->ref_count = 1)
|
||||||
|
#define plutovg_increment_reference(ob) (void)(ob && ++(ob)->ref_count)
|
||||||
|
#define plutovg_destroy_reference(ob) (ob && --(ob)->ref_count == 0)
|
||||||
|
#define plutovg_get_reference_count(ob) ((ob) ? (ob)->ref_count : 0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct plutovg_surface {
|
||||||
|
plutovg_ref_count_t ref_count;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int stride;
|
||||||
|
unsigned char* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct plutovg_path {
|
||||||
|
plutovg_ref_count_t ref_count;
|
||||||
|
int num_points;
|
||||||
|
int num_contours;
|
||||||
|
int num_curves;
|
||||||
|
plutovg_point_t start_point;
|
||||||
|
struct {
|
||||||
|
plutovg_path_element_t* data;
|
||||||
|
int size;
|
||||||
|
int capacity;
|
||||||
|
} elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PLUTOVG_PAINT_TYPE_COLOR,
|
||||||
|
PLUTOVG_PAINT_TYPE_GRADIENT,
|
||||||
|
PLUTOVG_PAINT_TYPE_TEXTURE
|
||||||
|
} plutovg_paint_type_t;
|
||||||
|
|
||||||
|
struct plutovg_paint {
|
||||||
|
plutovg_ref_count_t ref_count;
|
||||||
|
plutovg_paint_type_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
plutovg_paint_t base;
|
||||||
|
plutovg_color_t color;
|
||||||
|
} plutovg_solid_paint_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PLUTOVG_GRADIENT_TYPE_LINEAR,
|
||||||
|
PLUTOVG_GRADIENT_TYPE_RADIAL
|
||||||
|
} plutovg_gradient_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
plutovg_paint_t base;
|
||||||
|
plutovg_gradient_type_t type;
|
||||||
|
plutovg_spread_method_t spread;
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_gradient_stop_t* stops;
|
||||||
|
int nstops;
|
||||||
|
float values[6];
|
||||||
|
} plutovg_gradient_paint_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
plutovg_paint_t base;
|
||||||
|
plutovg_texture_type_t type;
|
||||||
|
float opacity;
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_surface_t* surface;
|
||||||
|
} plutovg_texture_paint_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
int len;
|
||||||
|
int y;
|
||||||
|
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_span_buffer_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float offset;
|
||||||
|
struct {
|
||||||
|
float* data;
|
||||||
|
int size;
|
||||||
|
int capacity;
|
||||||
|
} array;
|
||||||
|
} plutovg_stroke_dash_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float width;
|
||||||
|
plutovg_line_cap_t cap;
|
||||||
|
plutovg_line_join_t join;
|
||||||
|
float miter_limit;
|
||||||
|
} plutovg_stroke_style_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
plutovg_stroke_style_t style;
|
||||||
|
plutovg_stroke_dash_t dash;
|
||||||
|
} plutovg_stroke_data_t;
|
||||||
|
|
||||||
|
typedef struct plutovg_state {
|
||||||
|
plutovg_paint_t* paint;
|
||||||
|
plutovg_font_face_t* font_face;
|
||||||
|
plutovg_color_t color;
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_stroke_data_t stroke;
|
||||||
|
plutovg_span_buffer_t clip_spans;
|
||||||
|
plutovg_fill_rule_t winding;
|
||||||
|
plutovg_operator_t op;
|
||||||
|
float font_size;
|
||||||
|
float opacity;
|
||||||
|
bool clipping;
|
||||||
|
struct plutovg_state* next;
|
||||||
|
} plutovg_state_t;
|
||||||
|
|
||||||
|
struct plutovg_canvas {
|
||||||
|
plutovg_ref_count_t ref_count;
|
||||||
|
plutovg_surface_t* surface;
|
||||||
|
plutovg_path_t* path;
|
||||||
|
plutovg_state_t* state;
|
||||||
|
plutovg_state_t* freed_state;
|
||||||
|
plutovg_font_face_cache_t* face_cache;
|
||||||
|
plutovg_rect_t clip_rect;
|
||||||
|
plutovg_span_buffer_t clip_spans;
|
||||||
|
plutovg_span_buffer_t fill_spans;
|
||||||
|
};
|
||||||
|
|
||||||
|
void plutovg_span_buffer_init(plutovg_span_buffer_t* span_buffer);
|
||||||
|
void plutovg_span_buffer_init_rect(plutovg_span_buffer_t* span_buffer, int x, int y, int width, int height);
|
||||||
|
void plutovg_span_buffer_reset(plutovg_span_buffer_t* span_buffer);
|
||||||
|
void plutovg_span_buffer_destroy(plutovg_span_buffer_t* span_buffer);
|
||||||
|
void plutovg_span_buffer_copy(plutovg_span_buffer_t* span_buffer, const plutovg_span_buffer_t* source);
|
||||||
|
bool plutovg_span_buffer_contains(const plutovg_span_buffer_t* span_buffer, float x, float y);
|
||||||
|
void plutovg_span_buffer_extents(plutovg_span_buffer_t* span_buffer, plutovg_rect_t* extents);
|
||||||
|
void plutovg_span_buffer_intersect(plutovg_span_buffer_t* span_buffer, const plutovg_span_buffer_t* a, const plutovg_span_buffer_t* b);
|
||||||
|
|
||||||
|
void plutovg_rasterize(plutovg_span_buffer_t* span_buffer, const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_rect_t* clip_rect, const plutovg_stroke_data_t* stroke_data, plutovg_fill_rule_t winding);
|
||||||
|
void plutovg_blend(plutovg_canvas_t* canvas, const plutovg_span_buffer_t* span_buffer);
|
||||||
|
void plutovg_memfill32(unsigned int* dest, int length, unsigned int value);
|
||||||
|
|
||||||
|
#endif // PLUTOVG_PRIVATE_H
|
||||||
394
vendor/lunasvg/plutovg/source/plutovg-rasterize.c
vendored
Normal file
394
vendor/lunasvg/plutovg/source/plutovg-rasterize.c
vendored
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
#include "plutovg-private.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
#include "plutovg-ft-raster.h"
|
||||||
|
#include "plutovg-ft-stroker.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
void plutovg_span_buffer_init(plutovg_span_buffer_t* span_buffer)
|
||||||
|
{
|
||||||
|
plutovg_array_init(span_buffer->spans);
|
||||||
|
plutovg_span_buffer_reset(span_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_init_rect(plutovg_span_buffer_t* span_buffer, int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
plutovg_array_clear(span_buffer->spans);
|
||||||
|
plutovg_array_ensure(span_buffer->spans, height);
|
||||||
|
plutovg_span_t* spans = span_buffer->spans.data;
|
||||||
|
for(int i = 0; i < height; i++) {
|
||||||
|
spans[i].x = x;
|
||||||
|
spans[i].y = y + i;
|
||||||
|
spans[i].len = width;
|
||||||
|
spans[i].coverage = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
span_buffer->x = x;
|
||||||
|
span_buffer->y = y;
|
||||||
|
span_buffer->w = width;
|
||||||
|
span_buffer->h = height;
|
||||||
|
span_buffer->spans.size = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_reset(plutovg_span_buffer_t* span_buffer)
|
||||||
|
{
|
||||||
|
plutovg_array_clear(span_buffer->spans);
|
||||||
|
span_buffer->x = 0;
|
||||||
|
span_buffer->y = 0;
|
||||||
|
span_buffer->w = -1;
|
||||||
|
span_buffer->h = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_destroy(plutovg_span_buffer_t* span_buffer)
|
||||||
|
{
|
||||||
|
plutovg_array_destroy(span_buffer->spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_copy(plutovg_span_buffer_t* span_buffer, const plutovg_span_buffer_t* source)
|
||||||
|
{
|
||||||
|
plutovg_array_clear(span_buffer->spans);
|
||||||
|
plutovg_array_append(span_buffer->spans, source->spans);
|
||||||
|
span_buffer->x = source->x;
|
||||||
|
span_buffer->y = source->y;
|
||||||
|
span_buffer->w = source->w;
|
||||||
|
span_buffer->h = source->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_span_buffer_contains(const plutovg_span_buffer_t* span_buffer, float x, float y)
|
||||||
|
{
|
||||||
|
const int ix = (int)floorf(x);
|
||||||
|
const int iy = (int)floorf(y);
|
||||||
|
|
||||||
|
for(int i = 0; i < span_buffer->spans.size; i++) {
|
||||||
|
plutovg_span_t* span = &span_buffer->spans.data[i];
|
||||||
|
if(span->y != iy)
|
||||||
|
continue;
|
||||||
|
if(ix >= span->x && ix < (span->x + span->len)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_span_buffer_update_extents(plutovg_span_buffer_t* span_buffer)
|
||||||
|
{
|
||||||
|
if(span_buffer->w != -1 && span_buffer->h != -1)
|
||||||
|
return;
|
||||||
|
if(span_buffer->spans.size == 0) {
|
||||||
|
span_buffer->x = 0;
|
||||||
|
span_buffer->y = 0;
|
||||||
|
span_buffer->w = 0;
|
||||||
|
span_buffer->h = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_span_t* spans = span_buffer->spans.data;
|
||||||
|
int x1 = INT_MAX;
|
||||||
|
int y1 = spans[0].y;
|
||||||
|
int x2 = 0;
|
||||||
|
int y2 = spans[span_buffer->spans.size - 1].y;
|
||||||
|
for(int i = 0; i < span_buffer->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
span_buffer->x = x1;
|
||||||
|
span_buffer->y = y1;
|
||||||
|
span_buffer->w = x2 - x1;
|
||||||
|
span_buffer->h = y2 - y1 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_extents(plutovg_span_buffer_t* span_buffer, plutovg_rect_t* extents)
|
||||||
|
{
|
||||||
|
plutovg_span_buffer_update_extents(span_buffer);
|
||||||
|
extents->x = span_buffer->x;
|
||||||
|
extents->y = span_buffer->y;
|
||||||
|
extents->w = span_buffer->w;
|
||||||
|
extents->h = span_buffer->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_span_buffer_intersect(plutovg_span_buffer_t* span_buffer, const plutovg_span_buffer_t* a, const plutovg_span_buffer_t* b)
|
||||||
|
{
|
||||||
|
plutovg_span_buffer_reset(span_buffer);
|
||||||
|
plutovg_array_ensure(span_buffer->spans, plutovg_max(a->spans.size, b->spans.size));
|
||||||
|
|
||||||
|
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(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ax1 < bx1 && ax2 < bx1) {
|
||||||
|
++a_spans;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = plutovg_max(ax1, bx1);
|
||||||
|
int len = plutovg_min(ax2, bx2) - x;
|
||||||
|
if(len) {
|
||||||
|
plutovg_array_ensure(span_buffer->spans, 1);
|
||||||
|
plutovg_span_t* span = span_buffer->spans.data + span_buffer->spans.size;
|
||||||
|
span->x = x;
|
||||||
|
span->len = len;
|
||||||
|
span->y = a_spans->y;
|
||||||
|
span->coverage = (a_spans->coverage * b_spans->coverage) / 255;
|
||||||
|
span_buffer->spans.size += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ax2 < bx2) {
|
||||||
|
++a_spans;
|
||||||
|
} else {
|
||||||
|
++b_spans;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ALIGN_SIZE(size) (((size) + 7ul) & ~7ul)
|
||||||
|
static PVG_FT_Outline* ft_outline_create(int points, int contours)
|
||||||
|
{
|
||||||
|
size_t points_size = ALIGN_SIZE((points + contours) * sizeof(PVG_FT_Vector));
|
||||||
|
size_t tags_size = ALIGN_SIZE((points + contours) * sizeof(char));
|
||||||
|
size_t contours_size = ALIGN_SIZE(contours * sizeof(int));
|
||||||
|
size_t contours_flag_size = ALIGN_SIZE(contours * sizeof(char));
|
||||||
|
PVG_FT_Outline* outline = malloc(points_size + tags_size + contours_size + contours_flag_size + sizeof(PVG_FT_Outline));
|
||||||
|
|
||||||
|
PVG_FT_Byte* outline_data = (PVG_FT_Byte*)(outline + 1);
|
||||||
|
outline->points = (PVG_FT_Vector*)(outline_data);
|
||||||
|
outline->tags = (char*)(outline_data + points_size);
|
||||||
|
outline->contours = (int*)(outline_data + points_size + tags_size);
|
||||||
|
outline->contours_flag = (char*)(outline_data + points_size + tags_size + contours_size);
|
||||||
|
outline->n_points = 0;
|
||||||
|
outline->n_contours = 0;
|
||||||
|
outline->flags = 0x0;
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ft_outline_destroy(PVG_FT_Outline* outline)
|
||||||
|
{
|
||||||
|
free(outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FT_COORD(x) (PVG_FT_Pos)(roundf(x * 64))
|
||||||
|
static void ft_outline_move_to(PVG_FT_Outline* ft, float x, float y)
|
||||||
|
{
|
||||||
|
ft->points[ft->n_points].x = FT_COORD(x);
|
||||||
|
ft->points[ft->n_points].y = FT_COORD(y);
|
||||||
|
ft->tags[ft->n_points] = PVG_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 ft_outline_line_to(PVG_FT_Outline* ft, float x, float y)
|
||||||
|
{
|
||||||
|
ft->points[ft->n_points].x = FT_COORD(x);
|
||||||
|
ft->points[ft->n_points].y = FT_COORD(y);
|
||||||
|
ft->tags[ft->n_points] = PVG_FT_CURVE_TAG_ON;
|
||||||
|
ft->n_points++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ft_outline_cubic_to(PVG_FT_Outline* ft, float x1, float y1, float x2, float y2, float x3, float y3)
|
||||||
|
{
|
||||||
|
ft->points[ft->n_points].x = FT_COORD(x1);
|
||||||
|
ft->points[ft->n_points].y = FT_COORD(y1);
|
||||||
|
ft->tags[ft->n_points] = PVG_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] = PVG_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] = PVG_FT_CURVE_TAG_ON;
|
||||||
|
ft->n_points++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ft_outline_close(PVG_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] = PVG_FT_CURVE_TAG_ON;
|
||||||
|
ft->n_points++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ft_outline_end(PVG_FT_Outline* ft)
|
||||||
|
{
|
||||||
|
if(ft->n_points) {
|
||||||
|
ft->contours[ft->n_contours] = ft->n_points - 1;
|
||||||
|
ft->n_contours++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PVG_FT_Outline* ft_outline_convert_stroke(const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_stroke_data_t* stroke_data);
|
||||||
|
|
||||||
|
static PVG_FT_Outline* ft_outline_convert(const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_stroke_data_t* stroke_data)
|
||||||
|
{
|
||||||
|
if(stroke_data) {
|
||||||
|
return ft_outline_convert_stroke(path, matrix, stroke_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_iterator_t it;
|
||||||
|
plutovg_path_iterator_init(&it, path);
|
||||||
|
|
||||||
|
plutovg_point_t points[3];
|
||||||
|
PVG_FT_Outline* outline = ft_outline_create(path->num_points, path->num_contours);
|
||||||
|
while(plutovg_path_iterator_has_next(&it)) {
|
||||||
|
switch(plutovg_path_iterator_next(&it, points)) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 1);
|
||||||
|
ft_outline_move_to(outline, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 1);
|
||||||
|
ft_outline_line_to(outline, points[0].x, points[0].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
plutovg_matrix_map_points(matrix, points, points, 3);
|
||||||
|
ft_outline_cubic_to(outline, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
ft_outline_close(outline);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ft_outline_end(outline);
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PVG_FT_Outline* ft_outline_convert_dash(const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_stroke_dash_t* stroke_dash)
|
||||||
|
{
|
||||||
|
if(stroke_dash->array.size == 0)
|
||||||
|
return ft_outline_convert(path, matrix, NULL);
|
||||||
|
plutovg_path_t* dashed = plutovg_path_clone_dashed(path, stroke_dash->offset, stroke_dash->array.data, stroke_dash->array.size);
|
||||||
|
PVG_FT_Outline* outline = ft_outline_convert(dashed, matrix, NULL);
|
||||||
|
plutovg_path_destroy(dashed);
|
||||||
|
return outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PVG_FT_Outline* ft_outline_convert_stroke(const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_stroke_data_t* stroke_data)
|
||||||
|
{
|
||||||
|
double scale_x = sqrt(matrix->a * matrix->a + matrix->b * matrix->b);
|
||||||
|
double scale_y = sqrt(matrix->c * matrix->c + matrix->d * matrix->d);
|
||||||
|
|
||||||
|
double scale = hypot(scale_x, scale_y) / PLUTOVG_SQRT2;
|
||||||
|
double width = stroke_data->style.width * scale;
|
||||||
|
|
||||||
|
PVG_FT_Fixed ftWidth = (PVG_FT_Fixed)(width * 0.5 * (1 << 6));
|
||||||
|
PVG_FT_Fixed ftMiterLimit = (PVG_FT_Fixed)(stroke_data->style.miter_limit * (1 << 16));
|
||||||
|
|
||||||
|
PVG_FT_Stroker_LineCap ftCap;
|
||||||
|
switch(stroke_data->style.cap) {
|
||||||
|
case PLUTOVG_LINE_CAP_SQUARE:
|
||||||
|
ftCap = PVG_FT_STROKER_LINECAP_SQUARE;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_LINE_CAP_ROUND:
|
||||||
|
ftCap = PVG_FT_STROKER_LINECAP_ROUND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ftCap = PVG_FT_STROKER_LINECAP_BUTT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVG_FT_Stroker_LineJoin ftJoin;
|
||||||
|
switch(stroke_data->style.join) {
|
||||||
|
case PLUTOVG_LINE_JOIN_BEVEL:
|
||||||
|
ftJoin = PVG_FT_STROKER_LINEJOIN_BEVEL;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_LINE_JOIN_ROUND:
|
||||||
|
ftJoin = PVG_FT_STROKER_LINEJOIN_ROUND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ftJoin = PVG_FT_STROKER_LINEJOIN_MITER_FIXED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PVG_FT_Stroker stroker;
|
||||||
|
PVG_FT_Stroker_New(&stroker);
|
||||||
|
PVG_FT_Stroker_Set(stroker, ftWidth, ftCap, ftJoin, ftMiterLimit);
|
||||||
|
|
||||||
|
PVG_FT_Outline* outline = ft_outline_convert_dash(path, matrix, &stroke_data->dash);
|
||||||
|
PVG_FT_Stroker_ParseOutline(stroker, outline);
|
||||||
|
|
||||||
|
PVG_FT_UInt points;
|
||||||
|
PVG_FT_UInt contours;
|
||||||
|
PVG_FT_Stroker_GetCounts(stroker, &points, &contours);
|
||||||
|
|
||||||
|
PVG_FT_Outline* stroke_outline = ft_outline_create(points, contours);
|
||||||
|
PVG_FT_Stroker_Export(stroker, stroke_outline);
|
||||||
|
|
||||||
|
PVG_FT_Stroker_Done(stroker);
|
||||||
|
ft_outline_destroy(outline);
|
||||||
|
return stroke_outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spans_generation_callback(int count, const PVG_FT_Span* spans, void* user)
|
||||||
|
{
|
||||||
|
plutovg_span_buffer_t* span_buffer = (plutovg_span_buffer_t*)(user);
|
||||||
|
plutovg_array_append_data(span_buffer->spans, spans, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_rasterize(plutovg_span_buffer_t* span_buffer, const plutovg_path_t* path, const plutovg_matrix_t* matrix, const plutovg_rect_t* clip_rect, const plutovg_stroke_data_t* stroke_data, plutovg_fill_rule_t winding)
|
||||||
|
{
|
||||||
|
PVG_FT_Outline* outline = ft_outline_convert(path, matrix, stroke_data);
|
||||||
|
if(stroke_data) {
|
||||||
|
outline->flags = PVG_FT_OUTLINE_NONE;
|
||||||
|
} else {
|
||||||
|
switch(winding) {
|
||||||
|
case PLUTOVG_FILL_RULE_EVEN_ODD:
|
||||||
|
outline->flags = PVG_FT_OUTLINE_EVEN_ODD_FILL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outline->flags = PVG_FT_OUTLINE_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PVG_FT_Raster_Params params;
|
||||||
|
params.flags = PVG_FT_RASTER_FLAG_DIRECT | PVG_FT_RASTER_FLAG_AA;
|
||||||
|
params.gray_spans = spans_generation_callback;
|
||||||
|
params.user = span_buffer;
|
||||||
|
params.source = outline;
|
||||||
|
if(clip_rect) {
|
||||||
|
params.flags |= PVG_FT_RASTER_FLAG_CLIP;
|
||||||
|
params.clip_box.xMin = (PVG_FT_Pos)clip_rect->x;
|
||||||
|
params.clip_box.yMin = (PVG_FT_Pos)clip_rect->y;
|
||||||
|
params.clip_box.xMax = (PVG_FT_Pos)(clip_rect->x + clip_rect->w);
|
||||||
|
params.clip_box.yMax = (PVG_FT_Pos)(clip_rect->y + clip_rect->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_span_buffer_reset(span_buffer);
|
||||||
|
PVG_FT_Raster_Render(¶ms);
|
||||||
|
ft_outline_destroy(outline);
|
||||||
|
}
|
||||||
1724
vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h
vendored
Normal file
1724
vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7988
vendor/lunasvg/plutovg/source/plutovg-stb-image.h
vendored
Normal file
7988
vendor/lunasvg/plutovg/source/plutovg-stb-image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5077
vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h
vendored
Normal file
5077
vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
295
vendor/lunasvg/plutovg/source/plutovg-surface.c
vendored
Normal file
295
vendor/lunasvg/plutovg/source/plutovg-surface.c
vendored
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
#include "plutovg-private.h"
|
||||||
|
#include "plutovg-utils.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_STATIC
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "plutovg-stb-image-write.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_STATIC
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "plutovg-stb-image.h"
|
||||||
|
|
||||||
|
static plutovg_surface_t* plutovg_surface_create_uninitialized(int width, int height)
|
||||||
|
{
|
||||||
|
static const int kMaxSize = 1 << 15;
|
||||||
|
if(width <= 0 || height <= 0 || width >= kMaxSize || height >= kMaxSize)
|
||||||
|
return NULL;
|
||||||
|
const size_t size = width * height * 4;
|
||||||
|
plutovg_surface_t* surface = malloc(size + sizeof(plutovg_surface_t));
|
||||||
|
if(surface == NULL)
|
||||||
|
return NULL;
|
||||||
|
plutovg_init_reference(surface);
|
||||||
|
surface->width = width;
|
||||||
|
surface->height = height;
|
||||||
|
surface->stride = width * 4;
|
||||||
|
surface->data = (uint8_t*)(surface + 1);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_surface_create(int width, int height)
|
||||||
|
{
|
||||||
|
plutovg_surface_t* surface = plutovg_surface_create_uninitialized(width, height);
|
||||||
|
if(surface)
|
||||||
|
memset(surface->data, 0, surface->height * surface->stride);
|
||||||
|
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));
|
||||||
|
plutovg_init_reference(surface);
|
||||||
|
surface->width = width;
|
||||||
|
surface->height = height;
|
||||||
|
surface->stride = stride;
|
||||||
|
surface->data = data;
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static plutovg_surface_t* plutovg_surface_load_from_image(stbi_uc* image, int width, int height)
|
||||||
|
{
|
||||||
|
plutovg_surface_t* surface = plutovg_surface_create_uninitialized(width, height);
|
||||||
|
if(surface)
|
||||||
|
plutovg_convert_rgba_to_argb(surface->data, image, surface->width, surface->height, surface->stride);
|
||||||
|
stbi_image_free(image);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_surface_load_from_image_file(const char* filename)
|
||||||
|
{
|
||||||
|
int width, height, channels;
|
||||||
|
stbi_uc* image = stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha);
|
||||||
|
if(image == NULL)
|
||||||
|
return NULL;
|
||||||
|
return plutovg_surface_load_from_image(image, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_surface_load_from_image_data(const void* data, int length)
|
||||||
|
{
|
||||||
|
int width, height, channels;
|
||||||
|
stbi_uc* image = stbi_load_from_memory(data, length, &width, &height, &channels, STBI_rgb_alpha);
|
||||||
|
if(image == NULL)
|
||||||
|
return NULL;
|
||||||
|
return plutovg_surface_load_from_image(image, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t base64_table[128] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F,
|
||||||
|
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
|
||||||
|
0x3C, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||||
|
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||||
|
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||||
|
0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
|
||||||
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||||
|
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
|
||||||
|
0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_surface_load_from_image_base64(const char* data, int length)
|
||||||
|
{
|
||||||
|
plutovg_surface_t* surface = NULL;
|
||||||
|
uint8_t* output_data = NULL;
|
||||||
|
size_t output_length = 0;
|
||||||
|
|
||||||
|
size_t equals_sign_count = 0;
|
||||||
|
size_t sidx = 0;
|
||||||
|
size_t didx = 0;
|
||||||
|
|
||||||
|
if(length == -1)
|
||||||
|
length = strlen(data);
|
||||||
|
output_data = malloc(length);
|
||||||
|
if(output_data == NULL)
|
||||||
|
return NULL;
|
||||||
|
for(int i = 0; i < length; ++i) {
|
||||||
|
uint8_t cc = data[i];
|
||||||
|
if(cc == '=') {
|
||||||
|
++equals_sign_count;
|
||||||
|
} else if(cc == '+' || cc == '/' || PLUTOVG_IS_ALNUM(cc)) {
|
||||||
|
if(equals_sign_count > 0)
|
||||||
|
goto cleanup;
|
||||||
|
output_data[output_length++] = base64_table[cc];
|
||||||
|
} else if(!PLUTOVG_IS_WS(cc)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(output_length == 0 || equals_sign_count > 2 || (output_length % 4) == 1)
|
||||||
|
goto cleanup;
|
||||||
|
output_length -= (output_length + 3) / 4;
|
||||||
|
if(output_length == 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(output_length > 1) {
|
||||||
|
while(didx < output_length - 2) {
|
||||||
|
output_data[didx + 0] = (((output_data[sidx + 0] << 2) & 255) | ((output_data[sidx + 1] >> 4) & 003));
|
||||||
|
output_data[didx + 1] = (((output_data[sidx + 1] << 4) & 255) | ((output_data[sidx + 2] >> 2) & 017));
|
||||||
|
output_data[didx + 2] = (((output_data[sidx + 2] << 6) & 255) | ((output_data[sidx + 3] >> 0) & 077));
|
||||||
|
sidx += 4;
|
||||||
|
didx += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(didx < output_length)
|
||||||
|
output_data[didx] = (((output_data[sidx + 0] << 2) & 255) | ((output_data[sidx + 1] >> 4) & 003));
|
||||||
|
if(++didx < output_length) {
|
||||||
|
output_data[didx] = (((output_data[sidx + 1] << 4) & 255) | ((output_data[sidx + 2] >> 2) & 017));
|
||||||
|
}
|
||||||
|
|
||||||
|
surface = plutovg_surface_load_from_image_data(output_data, output_length);
|
||||||
|
cleanup:
|
||||||
|
free(output_data);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
plutovg_increment_reference(surface);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_surface_destroy(plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
if(plutovg_destroy_reference(surface)) {
|
||||||
|
free(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int plutovg_surface_get_reference_count(const plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
return plutovg_get_reference_count(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_surface_clear(plutovg_surface_t* surface, const plutovg_color_t* color)
|
||||||
|
{
|
||||||
|
uint32_t pixel = plutovg_premultiply_argb(plutovg_color_to_argb32(color));
|
||||||
|
for(int y = 0; y < surface->height; y++) {
|
||||||
|
uint32_t* pixels = (uint32_t*)(surface->data + surface->stride * y);
|
||||||
|
plutovg_memfill32(pixels, surface->width, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_surface_write_begin(const plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
plutovg_convert_argb_to_rgba(surface->data, surface->data, surface->width, surface->height, surface->stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plutovg_surface_write_end(const plutovg_surface_t* surface)
|
||||||
|
{
|
||||||
|
plutovg_convert_rgba_to_argb(surface->data, surface->data, surface->width, surface->height, surface->stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_surface_write_to_png(const plutovg_surface_t* surface, const char* filename)
|
||||||
|
{
|
||||||
|
plutovg_surface_write_begin(surface);
|
||||||
|
int success = stbi_write_png(filename, surface->width, surface->height, 4, surface->data, surface->stride);
|
||||||
|
plutovg_surface_write_end(surface);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_surface_write_to_jpg(const plutovg_surface_t* surface, const char* filename, int quality)
|
||||||
|
{
|
||||||
|
plutovg_surface_write_begin(surface);
|
||||||
|
int success = stbi_write_jpg(filename, surface->width, surface->height, 4, surface->data, quality);
|
||||||
|
plutovg_surface_write_end(surface);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_surface_write_to_png_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure)
|
||||||
|
{
|
||||||
|
plutovg_surface_write_begin(surface);
|
||||||
|
int success = stbi_write_png_to_func(write_func, closure, surface->width, surface->height, 4, surface->data, surface->stride);
|
||||||
|
plutovg_surface_write_end(surface);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool plutovg_surface_write_to_jpg_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure, int quality)
|
||||||
|
{
|
||||||
|
plutovg_surface_write_begin(surface);
|
||||||
|
int success = stbi_write_jpg_to_func(write_func, closure, surface->width, surface->height, 4, surface->data, quality);
|
||||||
|
plutovg_surface_write_end(surface);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_convert_argb_to_rgba(unsigned char* dst, const unsigned char* src, int width, int height, int stride)
|
||||||
|
{
|
||||||
|
for(int y = 0; y < height; y++) {
|
||||||
|
const uint32_t* src_row = (const uint32_t*)(src + stride * y);
|
||||||
|
unsigned char* dst_row = dst + stride * y;
|
||||||
|
for(int x = 0; x < width; x++) {
|
||||||
|
uint32_t pixel = src_row[x];
|
||||||
|
uint32_t a = (pixel >> 24) & 0xFF;
|
||||||
|
if(a == 0) {
|
||||||
|
*dst_row++ = 0;
|
||||||
|
*dst_row++ = 0;
|
||||||
|
*dst_row++ = 0;
|
||||||
|
*dst_row++ = 0;
|
||||||
|
} else {
|
||||||
|
uint32_t r = (pixel >> 16) & 0xFF;
|
||||||
|
uint32_t g = (pixel >> 8) & 0xFF;
|
||||||
|
uint32_t b = (pixel >> 0) & 0xFF;
|
||||||
|
if(a != 255) {
|
||||||
|
r = (r * 255) / a;
|
||||||
|
g = (g * 255) / a;
|
||||||
|
b = (b * 255) / a;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst_row++ = r;
|
||||||
|
*dst_row++ = g;
|
||||||
|
*dst_row++ = b;
|
||||||
|
*dst_row++ = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void plutovg_convert_rgba_to_argb(unsigned char* dst, const unsigned char* src, int width, int height, int stride)
|
||||||
|
{
|
||||||
|
for(int y = 0; y < height; y++) {
|
||||||
|
const unsigned char* src_row = src + stride * y;
|
||||||
|
uint32_t* dst_row = (uint32_t*)(dst + stride * y);
|
||||||
|
for(int x = 0; x < width; x++) {
|
||||||
|
uint32_t a = src_row[4 * x + 3];
|
||||||
|
if(a == 0) {
|
||||||
|
dst_row[x] = 0x00000000;
|
||||||
|
} else {
|
||||||
|
uint32_t r = src_row[4 * x + 0];
|
||||||
|
uint32_t g = src_row[4 * x + 1];
|
||||||
|
uint32_t b = src_row[4 * x + 2];
|
||||||
|
if(a != 255) {
|
||||||
|
r = (r * a) / 255;
|
||||||
|
g = (g * a) / 255;
|
||||||
|
b = (b * a) / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_row[x] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
211
vendor/lunasvg/plutovg/source/plutovg-utils.h
vendored
Normal file
211
vendor/lunasvg/plutovg/source/plutovg-utils.h
vendored
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
#ifndef PLUTOVG_UTILS_H
|
||||||
|
#define PLUTOVG_UTILS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define PLUTOVG_IS_NUM(c) ((c) >= '0' && (c) <= '9')
|
||||||
|
#define PLUTOVG_IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
|
||||||
|
#define PLUTOVG_IS_ALNUM(c) (PLUTOVG_IS_ALPHA(c) || PLUTOVG_IS_NUM(c))
|
||||||
|
#define PLUTOVG_IS_WS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
|
||||||
|
|
||||||
|
#define plutovg_min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define plutovg_max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define plutovg_clamp(v, lo, hi) ((v) < (lo) ? (lo) : ((v) > (hi) ? (hi) : (v)))
|
||||||
|
|
||||||
|
#define plutovg_alpha(c) (((c) >> 24) & 0xff)
|
||||||
|
#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, newcapacity * sizeof((array).data[0])); \
|
||||||
|
(array).capacity = newcapacity; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define plutovg_array_append_data(array, newdata, count) \
|
||||||
|
do { \
|
||||||
|
if(newdata && count > 0) { \
|
||||||
|
plutovg_array_ensure(array, count); \
|
||||||
|
memcpy((array).data + (array).size, newdata, (count) * sizeof((newdata)[0])); \
|
||||||
|
(array).size += count; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define plutovg_array_append(array, other) plutovg_array_append_data(array, (other).data, (other).size)
|
||||||
|
#define plutovg_array_clear(array) ((array).size = 0)
|
||||||
|
#define plutovg_array_destroy(array) free((array).data)
|
||||||
|
|
||||||
|
static inline uint32_t plutovg_premultiply_argb(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);
|
||||||
|
if(a != 255) {
|
||||||
|
r = (r * a) / 255;
|
||||||
|
g = (g * a) / 255;
|
||||||
|
b = (b * a) / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a << 24) | (r << 16) | (g << 8) | (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_parse_number(const char** begin, const char* end, float* number)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
float integer = 0;
|
||||||
|
float fraction = 0;
|
||||||
|
float exponent = 0;
|
||||||
|
int sign = 1;
|
||||||
|
int expsign = 1;
|
||||||
|
|
||||||
|
if(it < end && *it == '+') {
|
||||||
|
++it;
|
||||||
|
} else if(it < end && *it == '-') {
|
||||||
|
++it;
|
||||||
|
sign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it >= end || (*it != '.' && !PLUTOVG_IS_NUM(*it)))
|
||||||
|
return false;
|
||||||
|
if(PLUTOVG_IS_NUM(*it)) {
|
||||||
|
do {
|
||||||
|
integer = 10.f * integer + (*it++ - '0');
|
||||||
|
} while(it < end && PLUTOVG_IS_NUM(*it));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it < end && *it == '.') {
|
||||||
|
++it;
|
||||||
|
if(it >= end || !PLUTOVG_IS_NUM(*it))
|
||||||
|
return false;
|
||||||
|
float divisor = 1.f;
|
||||||
|
do {
|
||||||
|
fraction = 10.f * fraction + (*it++ - '0');
|
||||||
|
divisor *= 10.f;
|
||||||
|
} while(it < end && PLUTOVG_IS_NUM(*it));
|
||||||
|
fraction /= divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it < end && (*it == 'e' || *it == 'E')) {
|
||||||
|
++it;
|
||||||
|
if(it < end && *it == '+') {
|
||||||
|
++it;
|
||||||
|
} else if(it < end && *it == '-') {
|
||||||
|
++it;
|
||||||
|
expsign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it >= end || !PLUTOVG_IS_NUM(*it))
|
||||||
|
return false;
|
||||||
|
do {
|
||||||
|
exponent = 10 * exponent + (*it++ - '0');
|
||||||
|
} while(it < end && PLUTOVG_IS_NUM(*it));
|
||||||
|
}
|
||||||
|
|
||||||
|
*begin = it;
|
||||||
|
*number = sign * (integer + fraction);
|
||||||
|
if(exponent)
|
||||||
|
*number *= powf(10.f, expsign * exponent);
|
||||||
|
return *number >= -FLT_MAX && *number <= FLT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_delim(const char** begin, const char* end, const char delim)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
if(it < end && *it == delim) {
|
||||||
|
*begin = it + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_string(const char** begin, const char* end, const char* data)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
while(it < end && *data && *it == *data) {
|
||||||
|
++data;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(*data == '\0') {
|
||||||
|
*begin = it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_ws(const char** begin, const char* end)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
while(it < end && PLUTOVG_IS_WS(*it))
|
||||||
|
++it;
|
||||||
|
*begin = it;
|
||||||
|
return it < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_ws_and_delim(const char** begin, const char* end, char delim)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
if(plutovg_skip_ws(&it, end)) {
|
||||||
|
if(!plutovg_skip_delim(&it, end, delim))
|
||||||
|
return false;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*begin = it;
|
||||||
|
return it < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_ws_and_comma(const char** begin, const char* end)
|
||||||
|
{
|
||||||
|
return plutovg_skip_ws_and_delim(begin, end, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_ws_or_delim(const char** begin, const char* end, char delim, bool* has_delim)
|
||||||
|
{
|
||||||
|
const char* it = *begin;
|
||||||
|
if(has_delim)
|
||||||
|
*has_delim = false;
|
||||||
|
if(plutovg_skip_ws(&it, end)) {
|
||||||
|
if(plutovg_skip_delim(&it, end, delim)) {
|
||||||
|
if(has_delim)
|
||||||
|
*has_delim = true;
|
||||||
|
plutovg_skip_ws(&it, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it == *begin)
|
||||||
|
return false;
|
||||||
|
*begin = it;
|
||||||
|
return it < end;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool plutovg_skip_ws_or_comma(const char** begin, const char* end, bool* has_comma)
|
||||||
|
{
|
||||||
|
return plutovg_skip_ws_or_delim(begin, end, ',', has_comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PLUTOVG_UTILS_H
|
||||||
693
vendor/lunasvg/source/graphics.cpp
vendored
Normal file
693
vendor/lunasvg/source/graphics.cpp
vendored
Normal file
@@ -0,0 +1,693 @@
|
|||||||
|
#include "graphics.h"
|
||||||
|
#include "lunasvg.h"
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
const Color Color::Black(0xFF000000);
|
||||||
|
const Color Color::White(0xFFFFFFFF);
|
||||||
|
const Color Color::Transparent(0x00000000);
|
||||||
|
|
||||||
|
const Rect Rect::Empty(0, 0, 0, 0);
|
||||||
|
const Rect Rect::Invalid(0, 0, -1, -1);
|
||||||
|
const Rect Rect::Infinite(-FLT_MAX / 2.f, -FLT_MAX / 2.f, FLT_MAX, FLT_MAX);
|
||||||
|
|
||||||
|
Rect::Rect(const Box& box)
|
||||||
|
: x(box.x), y(box.y), w(box.w), h(box.h)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const Transform Transform::Identity(1, 0, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
Transform::Transform()
|
||||||
|
{
|
||||||
|
plutovg_matrix_init_identity(&m_matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform::Transform(float a, float b, float c, float d, float e, float f)
|
||||||
|
{
|
||||||
|
plutovg_matrix_init(&m_matrix, a, b, c, d, e, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform::Transform(const Matrix& matrix)
|
||||||
|
: Transform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::operator*(const Transform& transform) const
|
||||||
|
{
|
||||||
|
plutovg_matrix_t result;
|
||||||
|
plutovg_matrix_multiply(&result, &transform.m_matrix, &m_matrix);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::operator*=(const Transform& transform)
|
||||||
|
{
|
||||||
|
return (*this = *this * transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::multiply(const Transform& transform)
|
||||||
|
{
|
||||||
|
return (*this *= transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::translate(float tx, float ty)
|
||||||
|
{
|
||||||
|
return multiply(translated(tx, ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::scale(float sx, float sy)
|
||||||
|
{
|
||||||
|
return multiply(scaled(sx, sy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::rotate(float angle, float cx, float cy)
|
||||||
|
{
|
||||||
|
return multiply(rotated(angle, cx, cy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::shear(float shx, float shy)
|
||||||
|
{
|
||||||
|
return multiply(sheared(shx, shy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::postMultiply(const Transform& transform)
|
||||||
|
{
|
||||||
|
return (*this = transform * *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::postTranslate(float tx, float ty)
|
||||||
|
{
|
||||||
|
return postMultiply(translated(tx, ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::postScale(float sx, float sy)
|
||||||
|
{
|
||||||
|
return postMultiply(scaled(sx, sy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::postRotate(float angle, float cx, float cy)
|
||||||
|
{
|
||||||
|
return postMultiply(rotated(angle, cx, cy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::postShear(float shx, float shy)
|
||||||
|
{
|
||||||
|
return postMultiply(sheared(shx, shy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::inverse() const
|
||||||
|
{
|
||||||
|
plutovg_matrix_t inverse;
|
||||||
|
plutovg_matrix_invert(&m_matrix, &inverse);
|
||||||
|
return inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& Transform::invert()
|
||||||
|
{
|
||||||
|
plutovg_matrix_invert(&m_matrix, &m_matrix);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transform::reset()
|
||||||
|
{
|
||||||
|
plutovg_matrix_init_identity(&m_matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point Transform::mapPoint(float x, float y) const
|
||||||
|
{
|
||||||
|
plutovg_matrix_map(&m_matrix, x, y, &x, &y);
|
||||||
|
return Point(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point Transform::mapPoint(const Point& point) const
|
||||||
|
{
|
||||||
|
return mapPoint(point.x, point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect Transform::mapRect(const Rect& rect) const
|
||||||
|
{
|
||||||
|
if(!rect.isValid()) {
|
||||||
|
return Rect::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_rect_t result = {rect.x, rect.y, rect.w, rect.h};
|
||||||
|
plutovg_matrix_map_rect(&m_matrix, &result, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Transform::xScale() const
|
||||||
|
{
|
||||||
|
return std::sqrt(m_matrix.a * m_matrix.a + m_matrix.b * m_matrix.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Transform::yScale() const
|
||||||
|
{
|
||||||
|
return std::sqrt(m_matrix.c * m_matrix.c + m_matrix.d * m_matrix.d);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transform::parse(const char* data, size_t length)
|
||||||
|
{
|
||||||
|
return plutovg_matrix_parse(&m_matrix, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::rotated(float angle, float cx, float cy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
if(cx == 0.f && cy == 0.f) {
|
||||||
|
plutovg_matrix_init_rotate(&matrix, PLUTOVG_DEG2RAD(angle));
|
||||||
|
} else {
|
||||||
|
plutovg_matrix_init_translate(&matrix, cx, cy);
|
||||||
|
plutovg_matrix_rotate(&matrix, PLUTOVG_DEG2RAD(angle));
|
||||||
|
plutovg_matrix_translate(&matrix, -cx, -cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::scaled(float sx, float sy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_matrix_init_scale(&matrix, sx, sy);
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::sheared(float shx, float shy)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_matrix_init_shear(&matrix, PLUTOVG_DEG2RAD(shx), PLUTOVG_DEG2RAD(shy));
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::translated(float tx, float ty)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t matrix;
|
||||||
|
plutovg_matrix_init_translate(&matrix, tx, ty);
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path::Path(const Path& path)
|
||||||
|
: m_data(plutovg_path_reference(path.data()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Path::Path(Path&& path)
|
||||||
|
: m_data(path.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Path::~Path()
|
||||||
|
{
|
||||||
|
plutovg_path_destroy(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path& Path::operator=(const Path& path)
|
||||||
|
{
|
||||||
|
Path(path).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path& Path::operator=(Path&& path)
|
||||||
|
{
|
||||||
|
Path(std::move(path)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::moveTo(float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_move_to(ensure(), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::lineTo(float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_line_to(ensure(), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::quadTo(float x1, float y1, float x2, float y2)
|
||||||
|
{
|
||||||
|
plutovg_path_quad_to(ensure(), x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
|
||||||
|
{
|
||||||
|
plutovg_path_cubic_to(ensure(), x1, y1, x2, y2, x3, y3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y)
|
||||||
|
{
|
||||||
|
plutovg_path_arc_to(ensure(), rx, ry, PLUTOVG_DEG2RAD(xAxisRotation), largeArcFlag, sweepFlag, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::close()
|
||||||
|
{
|
||||||
|
plutovg_path_close(ensure());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addEllipse(float cx, float cy, float rx, float ry)
|
||||||
|
{
|
||||||
|
plutovg_path_add_ellipse(ensure(), cx, cy, rx, ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addRoundRect(float x, float y, float w, float h, float rx, float ry)
|
||||||
|
{
|
||||||
|
plutovg_path_add_round_rect(ensure(), x, y, w, h, rx, ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addRect(float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
plutovg_path_add_rect(ensure(), x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addEllipse(const Point& center, const Size& radii)
|
||||||
|
{
|
||||||
|
addEllipse(center.x, center.y, radii.w, radii.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addRoundRect(const Rect& rect, const Size& radii)
|
||||||
|
{
|
||||||
|
addRoundRect(rect.x, rect.y, rect.w, rect.h, radii.w, radii.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::addRect(const Rect& rect)
|
||||||
|
{
|
||||||
|
addRect(rect.x, rect.y, rect.w, rect.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Path::reset()
|
||||||
|
{
|
||||||
|
if(m_data == nullptr)
|
||||||
|
return;
|
||||||
|
if(isUnique()) {
|
||||||
|
plutovg_path_reset(m_data);
|
||||||
|
} else {
|
||||||
|
plutovg_path_destroy(m_data);
|
||||||
|
m_data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect Path::boundingRect() const
|
||||||
|
{
|
||||||
|
if(m_data == nullptr)
|
||||||
|
return Rect::Empty;
|
||||||
|
plutovg_rect_t extents;
|
||||||
|
plutovg_path_extents(m_data, &extents, false);
|
||||||
|
return extents;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Path::isEmpty() const
|
||||||
|
{
|
||||||
|
if(m_data)
|
||||||
|
return plutovg_path_get_elements(m_data, nullptr) == 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Path::isUnique() const
|
||||||
|
{
|
||||||
|
return plutovg_path_get_reference_count(m_data) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Path::parse(const char* data, size_t length)
|
||||||
|
{
|
||||||
|
plutovg_path_reset(ensure());
|
||||||
|
return plutovg_path_parse(m_data, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_path_t* Path::ensure()
|
||||||
|
{
|
||||||
|
if(isNull()) {
|
||||||
|
m_data = plutovg_path_create();
|
||||||
|
} else if(!isUnique()) {
|
||||||
|
plutovg_path_destroy(m_data);
|
||||||
|
m_data = plutovg_path_clone(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathIterator::PathIterator(const Path& path)
|
||||||
|
: m_size(plutovg_path_get_elements(path.data(), &m_elements))
|
||||||
|
, m_index(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PathCommand PathIterator::currentSegment(std::array<Point, 3>& points) const
|
||||||
|
{
|
||||||
|
auto command = m_elements[m_index].header.command;
|
||||||
|
switch(command) {
|
||||||
|
case PLUTOVG_PATH_COMMAND_MOVE_TO:
|
||||||
|
points[0] = m_elements[m_index + 1].point;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_LINE_TO:
|
||||||
|
points[0] = m_elements[m_index + 1].point;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CUBIC_TO:
|
||||||
|
points[0] = m_elements[m_index + 1].point;
|
||||||
|
points[1] = m_elements[m_index + 2].point;
|
||||||
|
points[2] = m_elements[m_index + 3].point;
|
||||||
|
break;
|
||||||
|
case PLUTOVG_PATH_COMMAND_CLOSE:
|
||||||
|
points[0] = m_elements[m_index + 1].point;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PathCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathIterator::next()
|
||||||
|
{
|
||||||
|
m_index += m_elements[m_index].header.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::FontFace(plutovg_font_face_t* face)
|
||||||
|
: m_face(plutovg_font_face_reference(face))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::FontFace(const void* data, size_t length, plutovg_destroy_func_t destroy_func, void* closure)
|
||||||
|
: m_face(plutovg_font_face_load_from_data(data, length, 0, destroy_func, closure))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::FontFace(const char* filename)
|
||||||
|
: m_face(plutovg_font_face_load_from_file(filename, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::FontFace(const FontFace& face)
|
||||||
|
: m_face(plutovg_font_face_reference(face.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::FontFace(FontFace&& face)
|
||||||
|
: m_face(face.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace::~FontFace()
|
||||||
|
{
|
||||||
|
plutovg_font_face_destroy(m_face);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace& FontFace::operator=(const FontFace& face)
|
||||||
|
{
|
||||||
|
FontFace(face).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace& FontFace::operator=(FontFace&& face)
|
||||||
|
{
|
||||||
|
FontFace(std::move(face)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontFace::swap(FontFace& face)
|
||||||
|
{
|
||||||
|
std::swap(m_face, face.m_face);
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_font_face_t* FontFace::release()
|
||||||
|
{
|
||||||
|
return std::exchange(m_face, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FontFaceCache::addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face)
|
||||||
|
{
|
||||||
|
if(!face.isNull())
|
||||||
|
plutovg_font_face_cache_add(m_cache, family.data(), bold, italic, face.get());
|
||||||
|
return !face.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFace FontFaceCache::getFontFace(const std::string& family, bool bold, bool italic) const
|
||||||
|
{
|
||||||
|
if(auto face = plutovg_font_face_cache_get(m_cache, family.data(), bold, italic)) {
|
||||||
|
return FontFace(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char* generic;
|
||||||
|
const char* fallback;
|
||||||
|
} generic_fallbacks[] = {
|
||||||
|
#if defined(__linux__)
|
||||||
|
{"sans-serif", "DejaVu Sans"},
|
||||||
|
{"serif", "DejaVu Serif"},
|
||||||
|
{"monospace", "DejaVu Sans Mono"},
|
||||||
|
#else
|
||||||
|
{"sans-serif", "Arial"},
|
||||||
|
{"serif", "Times New Roman"},
|
||||||
|
{"monospace", "Courier New"},
|
||||||
|
#endif
|
||||||
|
{"cursive", "Comic Sans MS"},
|
||||||
|
{"fantasy", "Impact"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(auto value : generic_fallbacks) {
|
||||||
|
if(value.generic == family || family.empty()) {
|
||||||
|
return FontFace(plutovg_font_face_cache_get(m_cache, value.fallback, bold, italic));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FontFace();
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFaceCache::FontFaceCache()
|
||||||
|
: m_cache(plutovg_font_face_cache_create())
|
||||||
|
{
|
||||||
|
#ifndef LUNASVG_DISABLE_LOAD_SYSTEM_FONTS
|
||||||
|
plutovg_font_face_cache_load_sys(m_cache);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FontFaceCache* fontFaceCache()
|
||||||
|
{
|
||||||
|
static FontFaceCache cache;
|
||||||
|
return &cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::Font(const FontFace& face, float size)
|
||||||
|
: m_face(face), m_size(size)
|
||||||
|
{
|
||||||
|
if(m_size > 0.f && !m_face.isNull()) {
|
||||||
|
plutovg_font_face_get_metrics(m_face.get(), m_size, &m_ascent, &m_descent, &m_lineGap, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Font::xHeight() const
|
||||||
|
{
|
||||||
|
plutovg_rect_t extents = {0};
|
||||||
|
if(m_size > 0.f && !m_face.isNull())
|
||||||
|
plutovg_font_face_get_glyph_metrics(m_face.get(), m_size, 'x', nullptr, nullptr, &extents);
|
||||||
|
return extents.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Font::measureText(const std::u32string_view& text) const
|
||||||
|
{
|
||||||
|
if(m_size > 0.f && !m_face.isNull())
|
||||||
|
return plutovg_font_face_text_extents(m_face.get(), m_size, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, nullptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Canvas> Canvas::create(const Bitmap& bitmap)
|
||||||
|
{
|
||||||
|
return std::shared_ptr<Canvas>(new Canvas(bitmap));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Canvas> Canvas::create(float x, float y, float width, float height)
|
||||||
|
{
|
||||||
|
constexpr int kMaxSize = 1 << 15;
|
||||||
|
if(width <= 0 || height <= 0 || width >= kMaxSize || height >= kMaxSize)
|
||||||
|
return std::shared_ptr<Canvas>(new Canvas(0, 0, 1, 1));
|
||||||
|
auto l = static_cast<int>(std::floor(x));
|
||||||
|
auto t = static_cast<int>(std::floor(y));
|
||||||
|
auto r = static_cast<int>(std::ceil(x + width));
|
||||||
|
auto b = static_cast<int>(std::ceil(y + height));
|
||||||
|
return std::shared_ptr<Canvas>(new Canvas(l, t, r - l, b - t));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Canvas> Canvas::create(const Rect& extents)
|
||||||
|
{
|
||||||
|
return create(extents.x, extents.y, extents.w, extents.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setColor(const Color& color)
|
||||||
|
{
|
||||||
|
setColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setColor(float r, float g, float b, float a)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_rgba(m_canvas, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_linear_gradient(m_canvas, x1, y1, x2, y2, static_cast<plutovg_spread_method_t>(spread), stops.data(), stops.size(), &transform.matrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_radial_gradient(m_canvas, cx, cy, r, fx, fy, 0.f, static_cast<plutovg_spread_method_t>(spread), stops.data(), stops.size(), &transform.matrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_texture(m_canvas, source.surface(), static_cast<plutovg_texture_type_t>(type), opacity, &transform.matrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::fillPath(const Path& path, FillRule fillRule, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_fill_rule(m_canvas, static_cast<plutovg_fill_rule_t>(fillRule));
|
||||||
|
plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER);
|
||||||
|
plutovg_canvas_fill_path(m_canvas, path.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_line_width(m_canvas, strokeData.lineWidth());
|
||||||
|
plutovg_canvas_set_miter_limit(m_canvas, strokeData.miterLimit());
|
||||||
|
plutovg_canvas_set_line_cap(m_canvas, static_cast<plutovg_line_cap_t>(strokeData.lineCap()));
|
||||||
|
plutovg_canvas_set_line_join(m_canvas, static_cast<plutovg_line_join_t>(strokeData.lineJoin()));
|
||||||
|
plutovg_canvas_set_dash_offset(m_canvas, strokeData.dashOffset());
|
||||||
|
plutovg_canvas_set_dash_array(m_canvas, strokeData.dashArray().data(), strokeData.dashArray().size());
|
||||||
|
plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER);
|
||||||
|
plutovg_canvas_stroke_path(m_canvas, path.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_fill_rule(m_canvas, PLUTOVG_FILL_RULE_NON_ZERO);
|
||||||
|
plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER);
|
||||||
|
plutovg_canvas_set_font(m_canvas, font.face().get(), font.size());
|
||||||
|
plutovg_canvas_fill_text(m_canvas, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, origin.x, origin.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_line_width(m_canvas, strokeWidth);
|
||||||
|
plutovg_canvas_set_miter_limit(m_canvas, 4.f);
|
||||||
|
plutovg_canvas_set_line_cap(m_canvas, PLUTOVG_LINE_CAP_BUTT);
|
||||||
|
plutovg_canvas_set_line_join(m_canvas, PLUTOVG_LINE_JOIN_MITER);
|
||||||
|
plutovg_canvas_set_dash_offset(m_canvas, 0.f);
|
||||||
|
plutovg_canvas_set_dash_array(m_canvas, nullptr, 0);
|
||||||
|
plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER);
|
||||||
|
plutovg_canvas_set_font(m_canvas, font.face().get(), font.size());
|
||||||
|
plutovg_canvas_stroke_text(m_canvas, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF32, origin.x, origin.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::clipPath(const Path& path, FillRule clipRule, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_fill_rule(m_canvas, static_cast<plutovg_fill_rule_t>(clipRule));
|
||||||
|
plutovg_canvas_clip_path(m_canvas, path.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::clipRect(const Rect& rect, FillRule clipRule, const Transform& transform)
|
||||||
|
{
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_set_fill_rule(m_canvas, static_cast<plutovg_fill_rule_t>(clipRule));
|
||||||
|
plutovg_canvas_clip_rect(m_canvas, rect.x, rect.y, rect.w, rect.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform)
|
||||||
|
{
|
||||||
|
auto xScale = dstRect.w / srcRect.w;
|
||||||
|
auto yScale = dstRect.h / srcRect.h;
|
||||||
|
plutovg_matrix_t matrix = { xScale, 0, 0, yScale, -srcRect.x * xScale, -srcRect.y * yScale };
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_transform(m_canvas, &transform.matrix());
|
||||||
|
plutovg_canvas_translate(m_canvas, dstRect.x, dstRect.y);
|
||||||
|
plutovg_canvas_set_fill_rule(m_canvas, PLUTOVG_FILL_RULE_NON_ZERO);
|
||||||
|
plutovg_canvas_set_operator(m_canvas, PLUTOVG_OPERATOR_SRC_OVER);
|
||||||
|
plutovg_canvas_set_texture(m_canvas, image.surface(), PLUTOVG_TEXTURE_TYPE_PLAIN, 1.f, &matrix);
|
||||||
|
plutovg_canvas_fill_rect(m_canvas, 0, 0, dstRect.w, dstRect.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity)
|
||||||
|
{
|
||||||
|
plutovg_matrix_t matrix = { 1, 0, 0, 1, static_cast<float>(canvas.x()), static_cast<float>(canvas.y()) };
|
||||||
|
plutovg_canvas_set_matrix(m_canvas, &m_translation);
|
||||||
|
plutovg_canvas_set_operator(m_canvas, static_cast<plutovg_operator_t>(blendMode));
|
||||||
|
plutovg_canvas_set_texture(m_canvas, canvas.surface(), PLUTOVG_TEXTURE_TYPE_PLAIN, opacity, &matrix);
|
||||||
|
plutovg_canvas_paint(m_canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::save()
|
||||||
|
{
|
||||||
|
plutovg_canvas_save(m_canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::restore()
|
||||||
|
{
|
||||||
|
plutovg_canvas_restore(m_canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Canvas::width() const
|
||||||
|
{
|
||||||
|
return plutovg_surface_get_width(m_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Canvas::height() const
|
||||||
|
{
|
||||||
|
return plutovg_surface_get_height(m_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::convertToLuminanceMask()
|
||||||
|
{
|
||||||
|
auto width = plutovg_surface_get_width(m_surface);
|
||||||
|
auto height = plutovg_surface_get_height(m_surface);
|
||||||
|
auto stride = plutovg_surface_get_stride(m_surface);
|
||||||
|
auto data = plutovg_surface_get_data(m_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 a = (pixel >> 24) & 0xFF;
|
||||||
|
auto r = (pixel >> 16) & 0xFF;
|
||||||
|
auto g = (pixel >> 8) & 0xFF;
|
||||||
|
auto b = (pixel >> 0) & 0xFF;
|
||||||
|
if(a) {
|
||||||
|
r = (r * 255) / a;
|
||||||
|
g = (g * 255) / a;
|
||||||
|
b = (b * 255) / a;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto l = (r * 0.2125 + g * 0.7154 + b * 0.0721);
|
||||||
|
pixels[x] = static_cast<uint32_t>(l * (a / 255.0)) << 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::~Canvas()
|
||||||
|
{
|
||||||
|
plutovg_canvas_destroy(m_canvas);
|
||||||
|
plutovg_surface_destroy(m_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::Canvas(const Bitmap& bitmap)
|
||||||
|
: m_surface(plutovg_surface_reference(bitmap.surface()))
|
||||||
|
, m_canvas(plutovg_canvas_create(m_surface))
|
||||||
|
, m_translation({1, 0, 0, 1, 0, 0})
|
||||||
|
, m_x(0), m_y(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::Canvas(int x, int y, int width, int height)
|
||||||
|
: m_surface(plutovg_surface_create(width, height))
|
||||||
|
, m_canvas(plutovg_canvas_create(m_surface))
|
||||||
|
, m_translation({1, 0, 0, 1, -static_cast<float>(x), -static_cast<float>(y)})
|
||||||
|
, m_x(x), m_y(y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
562
vendor/lunasvg/source/graphics.h
vendored
Normal file
562
vendor/lunasvg/source/graphics.h
vendored
Normal file
@@ -0,0 +1,562 @@
|
|||||||
|
#ifndef LUNASVG_GRAPHICS_H
|
||||||
|
#define LUNASVG_GRAPHICS_H
|
||||||
|
|
||||||
|
#include <plutovg.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
enum class LineCap : uint8_t {
|
||||||
|
Butt = PLUTOVG_LINE_CAP_BUTT,
|
||||||
|
Round = PLUTOVG_LINE_CAP_ROUND,
|
||||||
|
Square = PLUTOVG_LINE_CAP_SQUARE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LineJoin : uint8_t {
|
||||||
|
Miter = PLUTOVG_LINE_JOIN_MITER,
|
||||||
|
Round = PLUTOVG_LINE_JOIN_ROUND,
|
||||||
|
Bevel = PLUTOVG_LINE_JOIN_BEVEL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FillRule : uint8_t {
|
||||||
|
NonZero = PLUTOVG_FILL_RULE_NON_ZERO,
|
||||||
|
EvenOdd = PLUTOVG_FILL_RULE_EVEN_ODD
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SpreadMethod : uint8_t {
|
||||||
|
Pad = PLUTOVG_SPREAD_METHOD_PAD,
|
||||||
|
Reflect = PLUTOVG_SPREAD_METHOD_REFLECT,
|
||||||
|
Repeat = PLUTOVG_SPREAD_METHOD_REPEAT
|
||||||
|
};
|
||||||
|
|
||||||
|
class Color {
|
||||||
|
public:
|
||||||
|
constexpr Color() = default;
|
||||||
|
constexpr explicit Color(uint32_t value) : m_value(value) {}
|
||||||
|
constexpr Color(int r, int g, int b, int a = 255) : m_value(a << 24 | r << 16 | g << 8 | b) {}
|
||||||
|
|
||||||
|
constexpr uint8_t alpha() const { return (m_value >> 24) & 0xff; }
|
||||||
|
constexpr uint8_t red() const { return (m_value >> 16) & 0xff; }
|
||||||
|
constexpr uint8_t green() const { return (m_value >> 8) & 0xff; }
|
||||||
|
constexpr uint8_t blue() const { return (m_value >> 0) & 0xff; }
|
||||||
|
|
||||||
|
constexpr float alphaF() const { return alpha() / 255.f; }
|
||||||
|
constexpr float redF() const { return red() / 255.f; }
|
||||||
|
constexpr float greenF() const { return green() / 255.f; }
|
||||||
|
constexpr float blueF() const { return blue() / 255.f; }
|
||||||
|
|
||||||
|
constexpr uint32_t value() const { return m_value; }
|
||||||
|
|
||||||
|
constexpr bool isOpaque() const { return alpha() == 255; }
|
||||||
|
constexpr bool isVisible() const { return alpha() > 0; }
|
||||||
|
|
||||||
|
constexpr Color opaqueColor() const { return Color(m_value | 0xFF000000); }
|
||||||
|
constexpr Color colorWithAlpha(float opacity) const;
|
||||||
|
|
||||||
|
static const Color Transparent;
|
||||||
|
static const Color Black;
|
||||||
|
static const Color White;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t m_value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Color Color::colorWithAlpha(float opacity) const
|
||||||
|
{
|
||||||
|
auto rgb = m_value & 0x00FFFFFF;
|
||||||
|
auto a = static_cast<int>(alpha() * std::clamp(opacity, 0.f, 1.f));
|
||||||
|
return Color(rgb | a << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
public:
|
||||||
|
constexpr Point() = default;
|
||||||
|
constexpr Point(const plutovg_point_t& point) : Point(point.x, point.y) {}
|
||||||
|
constexpr Point(float x, float y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
constexpr void move(float dx, float dy) { x += dx; y += dy; }
|
||||||
|
constexpr void move(float d) { move(d, d); }
|
||||||
|
constexpr void move(const Point& p) { move(p.x, p.y); }
|
||||||
|
|
||||||
|
constexpr void scale(float sx, float sy) { x *= sx; y *= sy; }
|
||||||
|
constexpr void scale(float s) { scale(s, s); }
|
||||||
|
|
||||||
|
constexpr float dot(const Point& p) const { return x * p.x + y * p.y; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
float x{0};
|
||||||
|
float y{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Point operator+(const Point& a, const Point& b)
|
||||||
|
{
|
||||||
|
return Point(a.x + b.x, a.y + b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Point operator-(const Point& a, const Point& b)
|
||||||
|
{
|
||||||
|
return Point(a.x - b.x, a.y - b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Point operator-(const Point& a)
|
||||||
|
{
|
||||||
|
return Point(-a.x, -a.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Point& operator+=(Point& a, const Point& b)
|
||||||
|
{
|
||||||
|
a.move(b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Point& operator-=(Point& a, const Point& b)
|
||||||
|
{
|
||||||
|
a.move(-b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float operator*(const Point& a, const Point& b)
|
||||||
|
{
|
||||||
|
return a.dot(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Size {
|
||||||
|
public:
|
||||||
|
constexpr Size() = default;
|
||||||
|
constexpr Size(float w, float h) : w(w), h(h) {}
|
||||||
|
|
||||||
|
constexpr void expand(float dw, float dh) { w += dw; h += dh; }
|
||||||
|
constexpr void expand(float d) { expand(d, d); }
|
||||||
|
constexpr void expand(const Size& s) { expand(s.w, s.h); }
|
||||||
|
|
||||||
|
constexpr void scale(float sw, float sh) { w *= sw; h *= sh; }
|
||||||
|
constexpr void scale(float s) { scale(s, s); }
|
||||||
|
|
||||||
|
constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
|
||||||
|
constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
|
||||||
|
constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
float w{0};
|
||||||
|
float h{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Size operator+(const Size& a, const Size& b)
|
||||||
|
{
|
||||||
|
return Size(a.w + b.w, a.h + b.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Size operator-(const Size& a, const Size& b)
|
||||||
|
{
|
||||||
|
return Size(a.w - b.w, a.h - b.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Size operator-(const Size& a)
|
||||||
|
{
|
||||||
|
return Size(-a.w, -a.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Size& operator+=(Size& a, const Size& b)
|
||||||
|
{
|
||||||
|
a.expand(b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Size& operator-=(Size& a, const Size& b)
|
||||||
|
{
|
||||||
|
a.expand(-b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Box;
|
||||||
|
|
||||||
|
class Rect {
|
||||||
|
public:
|
||||||
|
constexpr Rect() = default;
|
||||||
|
constexpr explicit Rect(const Size& size) : Rect(size.w, size.h) {}
|
||||||
|
constexpr Rect(float width, float height) : Rect(0, 0, width, height) {}
|
||||||
|
constexpr Rect(const Point& origin, const Size& size) : Rect(origin.x, origin.y, size.w, size.h) {}
|
||||||
|
constexpr Rect(const plutovg_rect_t& rect) : Rect(rect.x, rect.y, rect.w, rect.h) {}
|
||||||
|
constexpr Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {}
|
||||||
|
|
||||||
|
Rect(const Box& box);
|
||||||
|
|
||||||
|
constexpr void move(float dx, float dy) { x += dx; y += dy; }
|
||||||
|
constexpr void move(float d) { move(d, d); }
|
||||||
|
constexpr void move(const Point& p) { move(p.x, p.y); }
|
||||||
|
|
||||||
|
constexpr void scale(float sx, float sy) { x *= sx; y *= sy; w *= sx; h *= sy; }
|
||||||
|
constexpr void scale(float s) { scale(s, s); }
|
||||||
|
|
||||||
|
constexpr void inflate(float dx, float dy) { x -= dx; y -= dy; w += dx * 2.f; h += dy * 2.f; }
|
||||||
|
constexpr void inflate(float d) { inflate(d, d); }
|
||||||
|
|
||||||
|
constexpr bool contains(float px, float py) const { return px >= x && px <= x + w && py >= y && py <= y + h; }
|
||||||
|
constexpr bool contains(const Point& p) const { return contains(p.x, p.y); }
|
||||||
|
|
||||||
|
constexpr Rect intersected(const Rect& rect) const;
|
||||||
|
constexpr Rect united(const Rect& rect) const;
|
||||||
|
|
||||||
|
constexpr Rect& intersect(const Rect& o);
|
||||||
|
constexpr Rect& unite(const Rect& o);
|
||||||
|
|
||||||
|
constexpr Point origin() const { return Point(x, y); }
|
||||||
|
constexpr Size size() const { return Size(w, h); }
|
||||||
|
|
||||||
|
constexpr float right() const { return x + w; }
|
||||||
|
constexpr float bottom() const { return y + h; }
|
||||||
|
|
||||||
|
constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
|
||||||
|
constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
|
||||||
|
constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
|
||||||
|
|
||||||
|
static const Rect Empty;
|
||||||
|
static const Rect Invalid;
|
||||||
|
static const Rect Infinite;
|
||||||
|
|
||||||
|
public:
|
||||||
|
float x{0};
|
||||||
|
float y{0};
|
||||||
|
float w{0};
|
||||||
|
float h{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Rect Rect::intersected(const Rect& rect) const
|
||||||
|
{
|
||||||
|
if(!rect.isValid())
|
||||||
|
return *this;
|
||||||
|
if(!isValid())
|
||||||
|
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);
|
||||||
|
if(l >= r || t >= b)
|
||||||
|
return Rect::Empty;
|
||||||
|
return Rect(l, t, r - l, b - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Rect Rect::united(const Rect& rect) const
|
||||||
|
{
|
||||||
|
if(!rect.isValid())
|
||||||
|
return *this;
|
||||||
|
if(!isValid())
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Rect& Rect::intersect(const Rect& o)
|
||||||
|
{
|
||||||
|
*this = intersected(o);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Rect& Rect::unite(const Rect& o)
|
||||||
|
{
|
||||||
|
*this = united(o);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Matrix;
|
||||||
|
|
||||||
|
class Transform {
|
||||||
|
public:
|
||||||
|
Transform();
|
||||||
|
Transform(const Matrix& matrix);
|
||||||
|
Transform(float a, float b, float c, float d, float e, float f);
|
||||||
|
Transform(const plutovg_matrix_t& matrix) : m_matrix(matrix) {}
|
||||||
|
|
||||||
|
Transform operator*(const Transform& transform) const;
|
||||||
|
Transform& operator*=(const Transform& transform);
|
||||||
|
|
||||||
|
Transform& multiply(const Transform& transform);
|
||||||
|
Transform& translate(float tx, float ty);
|
||||||
|
Transform& scale(float sx, float sy);
|
||||||
|
Transform& rotate(float angle, float cx = 0.f, float cy = 0.f);
|
||||||
|
Transform& shear(float shx, float shy);
|
||||||
|
|
||||||
|
Transform& postMultiply(const Transform& transform);
|
||||||
|
Transform& postTranslate(float tx, float ty);
|
||||||
|
Transform& postScale(float sx, float sy);
|
||||||
|
Transform& postRotate(float angle, float cx = 0.f, float cy = 0.f);
|
||||||
|
Transform& postShear(float shx, float shy);
|
||||||
|
|
||||||
|
Transform inverse() const;
|
||||||
|
Transform& invert();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
Point mapPoint(float x, float y) const;
|
||||||
|
Point mapPoint(const Point& point) const;
|
||||||
|
Rect mapRect(const Rect& rect) const;
|
||||||
|
|
||||||
|
float xScale() const;
|
||||||
|
float yScale() const;
|
||||||
|
|
||||||
|
const plutovg_matrix_t& matrix() const { return m_matrix; }
|
||||||
|
plutovg_matrix_t& matrix() { return m_matrix; }
|
||||||
|
|
||||||
|
bool parse(const char* data, size_t length);
|
||||||
|
|
||||||
|
static Transform translated(float tx, float ty);
|
||||||
|
static Transform scaled(float sx, float sy);
|
||||||
|
static Transform rotated(float angle, float cx, float cy);
|
||||||
|
static Transform sheared(float shx, float shy);
|
||||||
|
|
||||||
|
static const Transform Identity;
|
||||||
|
|
||||||
|
private:
|
||||||
|
plutovg_matrix_t m_matrix;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PathCommand {
|
||||||
|
MoveTo = PLUTOVG_PATH_COMMAND_MOVE_TO,
|
||||||
|
LineTo = PLUTOVG_PATH_COMMAND_LINE_TO,
|
||||||
|
CubicTo = PLUTOVG_PATH_COMMAND_CUBIC_TO,
|
||||||
|
Close = PLUTOVG_PATH_COMMAND_CLOSE
|
||||||
|
};
|
||||||
|
|
||||||
|
class Path {
|
||||||
|
public:
|
||||||
|
Path() = default;
|
||||||
|
Path(const Path& path);
|
||||||
|
Path(Path&& path);
|
||||||
|
~Path();
|
||||||
|
|
||||||
|
Path& operator=(const Path& path);
|
||||||
|
Path& operator=(Path&& path);
|
||||||
|
|
||||||
|
void swap(Path& path);
|
||||||
|
|
||||||
|
void moveTo(float x, float y);
|
||||||
|
void lineTo(float x, float y);
|
||||||
|
void quadTo(float x1, float y1, float x2, float y2);
|
||||||
|
void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3);
|
||||||
|
void arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void addEllipse(float cx, float cy, float rx, float ry);
|
||||||
|
void addRoundRect(float x, float y, float w, float h, float rx, float ry);
|
||||||
|
void addRect(float x, float y, float w, float h);
|
||||||
|
|
||||||
|
void addEllipse(const Point& center, const Size& radii);
|
||||||
|
void addRoundRect(const Rect& rect, const Size& radii);
|
||||||
|
void addRect(const Rect& rect);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
Rect boundingRect() const;
|
||||||
|
bool isEmpty() const;
|
||||||
|
bool isUnique() const;
|
||||||
|
bool isNull() const { return m_data == nullptr; }
|
||||||
|
plutovg_path_t* data() const { return m_data; }
|
||||||
|
|
||||||
|
bool parse(const char* data, size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
plutovg_path_t* release();
|
||||||
|
plutovg_path_t* ensure();
|
||||||
|
plutovg_path_t* m_data = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void Path::swap(Path& path)
|
||||||
|
{
|
||||||
|
std::swap(m_data, path.m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline plutovg_path_t* Path::release()
|
||||||
|
{
|
||||||
|
return std::exchange(m_data, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PathIterator {
|
||||||
|
public:
|
||||||
|
PathIterator(const Path& path);
|
||||||
|
|
||||||
|
PathCommand currentSegment(std::array<Point, 3>& points) const;
|
||||||
|
bool isDone() const { return m_index >= m_size; }
|
||||||
|
void next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const plutovg_path_element_t* m_elements;
|
||||||
|
const int m_size;
|
||||||
|
int m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FontFace {
|
||||||
|
public:
|
||||||
|
FontFace() = default;
|
||||||
|
explicit FontFace(plutovg_font_face_t* face);
|
||||||
|
FontFace(const void* data, size_t length, plutovg_destroy_func_t destroy_func, void* closure);
|
||||||
|
FontFace(const char* filename);
|
||||||
|
FontFace(const FontFace& face);
|
||||||
|
FontFace(FontFace&& face);
|
||||||
|
~FontFace();
|
||||||
|
|
||||||
|
FontFace& operator=(const FontFace& face);
|
||||||
|
FontFace& operator=(FontFace&& face);
|
||||||
|
|
||||||
|
void swap(FontFace& face);
|
||||||
|
|
||||||
|
bool isNull() const { return m_face == nullptr; }
|
||||||
|
plutovg_font_face_t* get() const { return m_face; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
plutovg_font_face_t* release();
|
||||||
|
plutovg_font_face_t* m_face = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FontFaceCache {
|
||||||
|
public:
|
||||||
|
bool addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face);
|
||||||
|
FontFace getFontFace(const std::string& family, bool bold, bool italic) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FontFaceCache();
|
||||||
|
plutovg_font_face_cache_t* m_cache;
|
||||||
|
friend FontFaceCache* fontFaceCache();
|
||||||
|
};
|
||||||
|
|
||||||
|
FontFaceCache* fontFaceCache();
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
public:
|
||||||
|
Font() = default;
|
||||||
|
Font(const FontFace& face, float size);
|
||||||
|
|
||||||
|
float ascent() const { return m_ascent; }
|
||||||
|
float descent() const { return m_descent; }
|
||||||
|
float height() const { return m_ascent - m_descent; }
|
||||||
|
float lineGap() const { return m_lineGap; }
|
||||||
|
float xHeight() const;
|
||||||
|
|
||||||
|
float measureText(const std::u32string_view& text) const;
|
||||||
|
|
||||||
|
const FontFace& face() const { return m_face; }
|
||||||
|
float size() const { return m_size; }
|
||||||
|
|
||||||
|
bool isNull() const { return m_size <= 0.f || m_face.isNull(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FontFace m_face;
|
||||||
|
float m_size = 0.f;
|
||||||
|
float m_ascent = 0.f;
|
||||||
|
float m_descent = 0.f;
|
||||||
|
float m_lineGap = 0.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextureType {
|
||||||
|
Plain = PLUTOVG_TEXTURE_TYPE_PLAIN,
|
||||||
|
Tiled = PLUTOVG_TEXTURE_TYPE_TILED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BlendMode {
|
||||||
|
Src = PLUTOVG_OPERATOR_SRC,
|
||||||
|
Src_Over = PLUTOVG_OPERATOR_SRC_OVER,
|
||||||
|
Dst_In = PLUTOVG_OPERATOR_DST_IN,
|
||||||
|
Dst_Out = PLUTOVG_OPERATOR_DST_OUT
|
||||||
|
};
|
||||||
|
|
||||||
|
using DashArray = std::vector<float>;
|
||||||
|
|
||||||
|
class StrokeData {
|
||||||
|
public:
|
||||||
|
explicit StrokeData(float lineWidth = 1.f) : m_lineWidth(lineWidth) {}
|
||||||
|
|
||||||
|
void setLineWidth(float lineWidth) { m_lineWidth = lineWidth; }
|
||||||
|
float lineWidth() const { return m_lineWidth; }
|
||||||
|
|
||||||
|
void setMiterLimit(float miterLimit) { m_miterLimit = miterLimit; }
|
||||||
|
float miterLimit() const { return m_miterLimit; }
|
||||||
|
|
||||||
|
void setDashOffset(float dashOffset) { m_dashOffset = dashOffset; }
|
||||||
|
float dashOffset() const { return m_dashOffset; }
|
||||||
|
|
||||||
|
void setDashArray(DashArray dashArray) { m_dashArray = std::move(dashArray); }
|
||||||
|
const DashArray& dashArray() const { return m_dashArray; }
|
||||||
|
|
||||||
|
void setLineCap(LineCap lineCap) { m_lineCap = lineCap; }
|
||||||
|
LineCap lineCap() const { return m_lineCap; }
|
||||||
|
|
||||||
|
void setLineJoin(LineJoin lineJoin) { m_lineJoin = lineJoin; }
|
||||||
|
LineJoin lineJoin() const { return m_lineJoin; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_lineWidth;
|
||||||
|
float m_miterLimit{4.f};
|
||||||
|
float m_dashOffset{0.f};
|
||||||
|
LineCap m_lineCap{LineCap::Butt};
|
||||||
|
LineJoin m_lineJoin{LineJoin::Miter};
|
||||||
|
DashArray m_dashArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
using GradientStop = plutovg_gradient_stop_t;
|
||||||
|
using GradientStops = std::vector<GradientStop>;
|
||||||
|
|
||||||
|
class Bitmap;
|
||||||
|
|
||||||
|
class Canvas {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Canvas> create(const Bitmap& bitmap);
|
||||||
|
static std::shared_ptr<Canvas> create(float x, float y, float width, float height);
|
||||||
|
static std::shared_ptr<Canvas> create(const Rect& extents);
|
||||||
|
|
||||||
|
void setColor(const Color& color);
|
||||||
|
void setColor(float r, float g, float b, float a);
|
||||||
|
void setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
|
||||||
|
void setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
|
||||||
|
void setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform);
|
||||||
|
|
||||||
|
void fillPath(const Path& path, FillRule fillRule, const Transform& transform);
|
||||||
|
void strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform);
|
||||||
|
|
||||||
|
void fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform);
|
||||||
|
void strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform);
|
||||||
|
|
||||||
|
void clipPath(const Path& path, FillRule clipRule, const Transform& transform);
|
||||||
|
void clipRect(const Rect& rect, FillRule clipRule, const Transform& transform);
|
||||||
|
|
||||||
|
void drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform);
|
||||||
|
void blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity);
|
||||||
|
|
||||||
|
void save();
|
||||||
|
void restore();
|
||||||
|
|
||||||
|
void convertToLuminanceMask();
|
||||||
|
|
||||||
|
int x() const { return m_x; }
|
||||||
|
int y() const { return m_y; }
|
||||||
|
int width() const;
|
||||||
|
int height() const;
|
||||||
|
|
||||||
|
Rect extents() const { return Rect(m_x, m_y, width(), height()); }
|
||||||
|
|
||||||
|
plutovg_surface_t* surface() const { return m_surface; }
|
||||||
|
plutovg_canvas_t* canvas() const { return m_canvas; }
|
||||||
|
|
||||||
|
~Canvas();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Canvas(const Bitmap& bitmap);
|
||||||
|
Canvas(int x, int y, int width, int height);
|
||||||
|
plutovg_surface_t* m_surface;
|
||||||
|
plutovg_canvas_t* m_canvas;
|
||||||
|
plutovg_matrix_t m_translation;
|
||||||
|
const int m_x;
|
||||||
|
const int m_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_GRAPHICS_H
|
||||||
536
vendor/lunasvg/source/lunasvg.cpp
vendored
Normal file
536
vendor/lunasvg/source/lunasvg.cpp
vendored
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
#include "lunasvg.h"
|
||||||
|
#include "svgelement.h"
|
||||||
|
#include "svgrenderstate.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
int lunasvg_version()
|
||||||
|
{
|
||||||
|
return LUNASVG_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* lunasvg_version_string()
|
||||||
|
{
|
||||||
|
return LUNASVG_VERSION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lunasvg_add_font_face_from_file(const char* family, bool bold, bool italic, const char* filename)
|
||||||
|
{
|
||||||
|
return lunasvg::fontFaceCache()->addFontFace(family, bold, italic, lunasvg::FontFace(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lunasvg_add_font_face_from_data(const char* family, bool bold, bool italic, const void* data, size_t length, lunasvg_destroy_func_t destroy_func, void* closure)
|
||||||
|
{
|
||||||
|
return lunasvg::fontFaceCache()->addFontFace(family, bold, italic, lunasvg::FontFace(data, length, destroy_func, closure));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
Bitmap::Bitmap(int width, int height)
|
||||||
|
: m_surface(plutovg_surface_create(width, height))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(uint8_t* data, int width, int height, int stride)
|
||||||
|
: m_surface(plutovg_surface_create_for_data(data, width, height, stride))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(const Bitmap& bitmap)
|
||||||
|
: m_surface(plutovg_surface_reference(bitmap.surface()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::Bitmap(Bitmap&& bitmap)
|
||||||
|
: m_surface(bitmap.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap::~Bitmap()
|
||||||
|
{
|
||||||
|
plutovg_surface_destroy(m_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap& Bitmap::operator=(const Bitmap& bitmap)
|
||||||
|
{
|
||||||
|
Bitmap(bitmap).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::swap(Bitmap& bitmap)
|
||||||
|
{
|
||||||
|
std::swap(m_surface, bitmap.m_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* Bitmap::data() const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_get_data(m_surface);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bitmap::width() const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_get_width(m_surface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bitmap::height() const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_get_height(m_surface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bitmap::stride() const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_get_stride(m_surface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::clear(uint32_t value)
|
||||||
|
{
|
||||||
|
if(m_surface == nullptr)
|
||||||
|
return;
|
||||||
|
plutovg_color_t color;
|
||||||
|
plutovg_color_init_rgba32(&color, value);
|
||||||
|
plutovg_surface_clear(m_surface, &color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bitmap::convertToRGBA()
|
||||||
|
{
|
||||||
|
if(m_surface == nullptr)
|
||||||
|
return;
|
||||||
|
auto data = plutovg_surface_get_data(m_surface);
|
||||||
|
auto width = plutovg_surface_get_width(m_surface);
|
||||||
|
auto height = plutovg_surface_get_height(m_surface);
|
||||||
|
auto stride = plutovg_surface_get_stride(m_surface);
|
||||||
|
plutovg_convert_argb_to_rgba(data, data, width, height, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap& Bitmap::operator=(Bitmap&& bitmap)
|
||||||
|
{
|
||||||
|
Bitmap(std::move(bitmap)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bitmap::writeToPng(const std::string& filename) const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_write_to_png(m_surface, filename.data());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bitmap::writeToPng(lunasvg_write_func_t callback, void* closure) const
|
||||||
|
{
|
||||||
|
if(m_surface)
|
||||||
|
return plutovg_surface_write_to_png_stream(m_surface, callback, closure);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_surface_t* Bitmap::release()
|
||||||
|
{
|
||||||
|
return std::exchange(m_surface, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::Box(float x, float y, float w, float 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Box& Box::transform(const Matrix &matrix)
|
||||||
|
{
|
||||||
|
*this = transformed(matrix);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box Box::transformed(const Matrix& matrix) const
|
||||||
|
{
|
||||||
|
return Transform(matrix).mapRect(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix::Matrix(float a, float b, float c, float d, float e, float f)
|
||||||
|
: a(a), b(b), c(c), d(d), e(e), f(f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix::Matrix(const plutovg_matrix_t& matrix)
|
||||||
|
: a(matrix.a), b(matrix.b), c(matrix.c), d(matrix.d), e(matrix.e), f(matrix.f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix::Matrix(const Transform& transform)
|
||||||
|
: Matrix(transform.matrix())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::operator*(const Matrix& matrix) const
|
||||||
|
{
|
||||||
|
return Transform(*this) * Transform(matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::operator*=(const Matrix &matrix)
|
||||||
|
{
|
||||||
|
return (*this = *this * matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::multiply(const Matrix& matrix)
|
||||||
|
{
|
||||||
|
return (*this *= matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::scale(float sx, float sy)
|
||||||
|
{
|
||||||
|
return multiply(scaled(sx, sy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::translate(float tx, float ty)
|
||||||
|
{
|
||||||
|
return multiply(translated(tx, ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::rotate(float angle, float cx, float cy)
|
||||||
|
{
|
||||||
|
return multiply(rotated(angle, cx, cy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::shear(float shx, float shy)
|
||||||
|
{
|
||||||
|
return multiply(sheared(shx, shy));
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::inverse() const
|
||||||
|
{
|
||||||
|
return Transform(*this).inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix& Matrix::invert()
|
||||||
|
{
|
||||||
|
return (*this = inverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Matrix::reset()
|
||||||
|
{
|
||||||
|
*this = Matrix(1, 0, 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::translated(float tx, float ty)
|
||||||
|
{
|
||||||
|
return Transform::translated(tx, ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::scaled(float sx, float sy)
|
||||||
|
{
|
||||||
|
return Transform::scaled(sx, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::rotated(float angle, float cx, float cy)
|
||||||
|
{
|
||||||
|
return Transform::rotated(angle, cx, cy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Matrix::sheared(float shx, float shy)
|
||||||
|
{
|
||||||
|
return Transform::sheared(shx, shy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Node(SVGNode* node)
|
||||||
|
: m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Node::isTextNode() const
|
||||||
|
{
|
||||||
|
return m_node && m_node->isTextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Node::isElement() const
|
||||||
|
{
|
||||||
|
return m_node && m_node->isElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextNode Node::toTextNode() const
|
||||||
|
{
|
||||||
|
if(m_node && m_node->isTextNode())
|
||||||
|
return static_cast<SVGTextNode*>(m_node);
|
||||||
|
return TextNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element Node::toElement() const
|
||||||
|
{
|
||||||
|
if(m_node && m_node->isElement())
|
||||||
|
return static_cast<SVGElement*>(m_node);
|
||||||
|
return Element();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element Node::parentElement() const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return m_node->parentElement();
|
||||||
|
return Element();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextNode::TextNode(SVGTextNode* text)
|
||||||
|
: Node(text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& TextNode::data() const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return text()->data();
|
||||||
|
return emptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextNode::setData(const std::string& data)
|
||||||
|
{
|
||||||
|
if(m_node) {
|
||||||
|
text()->setData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGTextNode* TextNode::text() const
|
||||||
|
{
|
||||||
|
return static_cast<SVGTextNode*>(m_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element::Element(SVGElement* element)
|
||||||
|
: Node(element)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Element::hasAttribute(const std::string& name) const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return element()->hasAttribute(name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Element::getAttribute(const std::string& name) const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return element()->getAttribute(name);
|
||||||
|
return emptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::setAttribute(const std::string& name, const std::string& value)
|
||||||
|
{
|
||||||
|
if(m_node) {
|
||||||
|
element()->setAttribute(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Element::render(Bitmap& bitmap, const Matrix& matrix) const
|
||||||
|
{
|
||||||
|
if(m_node == nullptr || bitmap.isNull())
|
||||||
|
return;
|
||||||
|
auto canvas = Canvas::create(bitmap);
|
||||||
|
SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas);
|
||||||
|
element(true)->render(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) const
|
||||||
|
{
|
||||||
|
if(m_node == nullptr)
|
||||||
|
return Bitmap();
|
||||||
|
auto elementBounds = element(true)->localTransform().mapRect(element()->paintBoundingBox());
|
||||||
|
if(elementBounds.isEmpty())
|
||||||
|
return Bitmap();
|
||||||
|
if(width <= 0 && height <= 0) {
|
||||||
|
width = static_cast<int>(std::ceil(elementBounds.w));
|
||||||
|
height = static_cast<int>(std::ceil(elementBounds.h));
|
||||||
|
} else if(width > 0 && height <= 0) {
|
||||||
|
height = static_cast<int>(std::ceil(width * elementBounds.h / elementBounds.w));
|
||||||
|
} else if(height > 0 && width <= 0) {
|
||||||
|
width = static_cast<int>(std::ceil(height * elementBounds.w / elementBounds.h));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xScale = width / elementBounds.w;
|
||||||
|
auto yScale = height / elementBounds.h;
|
||||||
|
|
||||||
|
Matrix matrix(xScale, 0, 0, yScale, -elementBounds.x * xScale, -elementBounds.y * yScale);
|
||||||
|
Bitmap bitmap(width, height);
|
||||||
|
if(backgroundColor) bitmap.clear(backgroundColor);
|
||||||
|
render(bitmap, matrix);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Element::getLocalMatrix() const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return element(true)->localTransform();
|
||||||
|
return Matrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix Element::getGlobalMatrix() const
|
||||||
|
{
|
||||||
|
if(m_node == nullptr)
|
||||||
|
return Matrix();
|
||||||
|
auto transform = element(true)->localTransform();
|
||||||
|
for(auto parent = element()->parentElement(); parent; parent = parent->parentElement())
|
||||||
|
transform.postMultiply(parent->localTransform());
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
Box Element::getLocalBoundingBox() const
|
||||||
|
{
|
||||||
|
return getBoundingBox().transformed(getLocalMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
Box Element::getGlobalBoundingBox() const
|
||||||
|
{
|
||||||
|
return getBoundingBox().transformed(getGlobalMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
Box Element::getBoundingBox() const
|
||||||
|
{
|
||||||
|
if(m_node)
|
||||||
|
return element(true)->paintBoundingBox();
|
||||||
|
return Box();
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeList Element::children() const
|
||||||
|
{
|
||||||
|
if(m_node == nullptr)
|
||||||
|
return NodeList();
|
||||||
|
NodeList children;
|
||||||
|
for(const auto& child : element()->children())
|
||||||
|
children.push_back(child.get());
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGElement* Element::element(bool layoutIfNeeded) const
|
||||||
|
{
|
||||||
|
auto element = static_cast<SVGElement*>(m_node);
|
||||||
|
if(element && layoutIfNeeded)
|
||||||
|
element->rootElement()->layoutIfNeeded();
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return loadFromData(data, std::strlen(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Document> Document::loadFromData(const char* data, size_t length)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Document> document(new Document);
|
||||||
|
if(!document->parse(data, length))
|
||||||
|
return nullptr;
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Document::width() const
|
||||||
|
{
|
||||||
|
return rootElement(true)->intrinsicWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Document::height() const
|
||||||
|
{
|
||||||
|
return rootElement(true)->intrinsicHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
Box Document::boundingBox() const
|
||||||
|
{
|
||||||
|
return rootElement(true)->localTransform().mapRect(rootElement()->paintBoundingBox());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::updateLayout()
|
||||||
|
{
|
||||||
|
m_rootElement->layoutIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::forceLayout()
|
||||||
|
{
|
||||||
|
m_rootElement->forceLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::render(Bitmap& bitmap, const Matrix& matrix) const
|
||||||
|
{
|
||||||
|
if(bitmap.isNull())
|
||||||
|
return;
|
||||||
|
auto canvas = Canvas::create(bitmap);
|
||||||
|
SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas);
|
||||||
|
rootElement(true)->render(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap Document::renderToBitmap(int width, int height, uint32_t backgroundColor) const
|
||||||
|
{
|
||||||
|
auto intrinsicWidth = rootElement(true)->intrinsicWidth();
|
||||||
|
auto intrinsicHeight = rootElement()->intrinsicHeight();
|
||||||
|
if(intrinsicWidth == 0.f || intrinsicHeight == 0.f)
|
||||||
|
return Bitmap();
|
||||||
|
if(width <= 0 && height <= 0) {
|
||||||
|
width = static_cast<int>(std::ceil(intrinsicWidth));
|
||||||
|
height = static_cast<int>(std::ceil(intrinsicHeight));
|
||||||
|
} else if(width > 0 && height <= 0) {
|
||||||
|
height = static_cast<int>(std::ceil(width * intrinsicHeight / intrinsicWidth));
|
||||||
|
} else if(height > 0 && width <= 0) {
|
||||||
|
width = static_cast<int>(std::ceil(height * intrinsicWidth / intrinsicHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xScale = width / intrinsicWidth;
|
||||||
|
auto yScale = height / intrinsicHeight;
|
||||||
|
|
||||||
|
Matrix matrix(xScale, 0, 0, yScale, 0, 0);
|
||||||
|
Bitmap bitmap(width, height);
|
||||||
|
if(backgroundColor) bitmap.clear(backgroundColor);
|
||||||
|
render(bitmap, matrix);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element Document::elementFromPoint(float x, float y) const
|
||||||
|
{
|
||||||
|
return rootElement(true)->elementFromPoint(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element Document::getElementById(const std::string& id) const
|
||||||
|
{
|
||||||
|
return m_rootElement->getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element Document::documentElement() const
|
||||||
|
{
|
||||||
|
return m_rootElement.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGRootElement* Document::rootElement(bool layoutIfNeeded) const
|
||||||
|
{
|
||||||
|
if(layoutIfNeeded)
|
||||||
|
m_rootElement->layoutIfNeeded();
|
||||||
|
return m_rootElement.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::Document(Document&&) = default;
|
||||||
|
Document& Document::operator=(Document&&) = default;
|
||||||
|
|
||||||
|
Document::Document() = default;
|
||||||
|
Document::~Document() = default;
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
1225
vendor/lunasvg/source/svgelement.cpp
vendored
Normal file
1225
vendor/lunasvg/source/svgelement.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
498
vendor/lunasvg/source/svgelement.h
vendored
Normal file
498
vendor/lunasvg/source/svgelement.h
vendored
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
#ifndef LUNASVG_SVGELEMENT_H
|
||||||
|
#define LUNASVG_SVGELEMENT_H
|
||||||
|
|
||||||
|
#include "lunasvg.h"
|
||||||
|
#include "svgproperty.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
class Document;
|
||||||
|
class SVGElement;
|
||||||
|
class SVGRootElement;
|
||||||
|
|
||||||
|
class SVGNode {
|
||||||
|
public:
|
||||||
|
SVGNode(Document* document)
|
||||||
|
: m_document(document)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~SVGNode() = default;
|
||||||
|
virtual bool isTextNode() const { return false; }
|
||||||
|
virtual bool isElement() const { return false; }
|
||||||
|
virtual bool isPaintElement() const { return false; }
|
||||||
|
virtual bool isGraphicsElement() const { return false; }
|
||||||
|
virtual bool isGeometryElement() const { return false; }
|
||||||
|
virtual bool isTextPositioningElement() const { return false; }
|
||||||
|
|
||||||
|
Document* document() const { return m_document; }
|
||||||
|
SVGRootElement* rootElement() const { return m_document->rootElement(); }
|
||||||
|
|
||||||
|
SVGElement* parentElement() const { return m_parentElement; }
|
||||||
|
void setParentElement(SVGElement* parent) { m_parentElement = parent; }
|
||||||
|
|
||||||
|
bool isRootElement() const { return m_parentElement == nullptr; }
|
||||||
|
|
||||||
|
virtual std::unique_ptr<SVGNode> clone(bool deep) const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGNode(const SVGNode&) = delete;
|
||||||
|
SVGNode& operator=(const SVGNode&) = delete;
|
||||||
|
Document* m_document;
|
||||||
|
SVGElement* m_parentElement = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGTextNode final : public SVGNode {
|
||||||
|
public:
|
||||||
|
SVGTextNode(Document* document);
|
||||||
|
|
||||||
|
bool isTextNode() const final { return true; }
|
||||||
|
|
||||||
|
const std::string& data() const { return m_data; }
|
||||||
|
void setData(const std::string& data);
|
||||||
|
|
||||||
|
std::unique_ptr<SVGNode> clone(bool deep) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Attribute {
|
||||||
|
public:
|
||||||
|
Attribute() = default;
|
||||||
|
Attribute(int specificity, PropertyID id, std::string value)
|
||||||
|
: m_specificity(specificity), m_id(id), m_value(std::move(value))
|
||||||
|
{}
|
||||||
|
|
||||||
|
int specificity() const { return m_specificity; }
|
||||||
|
PropertyID id() const { return m_id; }
|
||||||
|
const std::string& value() const { return m_value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_specificity;
|
||||||
|
PropertyID m_id;
|
||||||
|
std::string m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using AttributeList = std::forward_list<Attribute>;
|
||||||
|
|
||||||
|
enum class ElementID : uint8_t {
|
||||||
|
Unknown = 0,
|
||||||
|
Star,
|
||||||
|
Circle,
|
||||||
|
ClipPath,
|
||||||
|
Defs,
|
||||||
|
Ellipse,
|
||||||
|
G,
|
||||||
|
Image,
|
||||||
|
Line,
|
||||||
|
LinearGradient,
|
||||||
|
Marker,
|
||||||
|
Mask,
|
||||||
|
Path,
|
||||||
|
Pattern,
|
||||||
|
Polygon,
|
||||||
|
Polyline,
|
||||||
|
RadialGradient,
|
||||||
|
Rect,
|
||||||
|
Stop,
|
||||||
|
Style,
|
||||||
|
Svg,
|
||||||
|
Symbol,
|
||||||
|
Text,
|
||||||
|
Tspan,
|
||||||
|
Use
|
||||||
|
};
|
||||||
|
|
||||||
|
ElementID elementid(std::string_view name);
|
||||||
|
|
||||||
|
using SVGNodeList = std::list<std::unique_ptr<SVGNode>>;
|
||||||
|
using SVGPropertyList = std::forward_list<SVGProperty*>;
|
||||||
|
|
||||||
|
class SVGMarkerElement;
|
||||||
|
class SVGClipPathElement;
|
||||||
|
class SVGMaskElement;
|
||||||
|
class SVGPaintElement;
|
||||||
|
class SVGLayoutState;
|
||||||
|
class SVGRenderState;
|
||||||
|
|
||||||
|
extern const std::string emptyString;
|
||||||
|
|
||||||
|
class SVGElement : public SVGNode {
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<SVGElement> create(Document* document, ElementID id);
|
||||||
|
|
||||||
|
SVGElement(Document* document, ElementID id);
|
||||||
|
virtual ~SVGElement() = default;
|
||||||
|
|
||||||
|
bool hasAttribute(std::string_view name) const;
|
||||||
|
const std::string& getAttribute(std::string_view name) const;
|
||||||
|
bool setAttribute(std::string_view name, const std::string& value);
|
||||||
|
|
||||||
|
const Attribute* findAttribute(PropertyID id) const;
|
||||||
|
bool hasAttribute(PropertyID id) const;
|
||||||
|
const std::string& getAttribute(PropertyID id) const;
|
||||||
|
bool setAttribute(int specificity, PropertyID id, const std::string& value);
|
||||||
|
void setAttributes(const AttributeList& attributes);
|
||||||
|
bool setAttribute(const Attribute& attribute);
|
||||||
|
|
||||||
|
virtual void parseAttribute(PropertyID id, const std::string& value);
|
||||||
|
|
||||||
|
SVGElement* previousElement() const;
|
||||||
|
SVGElement* nextElement() const;
|
||||||
|
|
||||||
|
SVGNode* addChild(std::unique_ptr<SVGNode> child);
|
||||||
|
SVGNode* firstChild() const;
|
||||||
|
SVGNode* lastChild() const;
|
||||||
|
|
||||||
|
ElementID id() const { return m_id; }
|
||||||
|
const AttributeList& attributes() const { return m_attributes; }
|
||||||
|
const SVGPropertyList& properties() const { return m_properties; }
|
||||||
|
const SVGNodeList& children() const { return m_children; }
|
||||||
|
|
||||||
|
virtual Transform localTransform() const { return Transform::Identity; }
|
||||||
|
virtual Rect fillBoundingBox() const;
|
||||||
|
virtual Rect strokeBoundingBox() const;
|
||||||
|
virtual Rect paintBoundingBox() const;
|
||||||
|
|
||||||
|
SVGMarkerElement* getMarker(std::string_view id) const;
|
||||||
|
SVGClipPathElement* getClipper(std::string_view id) const;
|
||||||
|
SVGMaskElement* getMasker(std::string_view id) const;
|
||||||
|
SVGPaintElement* getPainter(std::string_view id) const;
|
||||||
|
|
||||||
|
SVGElement* elementFromPoint(float x, float y);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void transverse(T callback);
|
||||||
|
|
||||||
|
void addProperty(SVGProperty& value);
|
||||||
|
SVGProperty* getProperty(PropertyID id) const;
|
||||||
|
Size currentViewportSize() const;
|
||||||
|
float font_size() const { return m_font_size; }
|
||||||
|
|
||||||
|
void cloneChildren(SVGElement* parentElement) const;
|
||||||
|
std::unique_ptr<SVGNode> clone(bool deep) const final;
|
||||||
|
|
||||||
|
virtual void build();
|
||||||
|
|
||||||
|
virtual void layoutElement(const SVGLayoutState& state);
|
||||||
|
void layoutChildren(SVGLayoutState& state);
|
||||||
|
virtual void layout(SVGLayoutState& state);
|
||||||
|
|
||||||
|
void renderChildren(SVGRenderState& state) const;
|
||||||
|
virtual void render(SVGRenderState& state) const;
|
||||||
|
|
||||||
|
bool isDisplayNone() const { return m_display == Display::None; }
|
||||||
|
bool isOverflowHidden() const { return m_overflow == Overflow::Hidden; }
|
||||||
|
bool isVisibilityHidden() const { return m_visibility != Visibility::Visible; }
|
||||||
|
|
||||||
|
bool isHiddenElement() const;
|
||||||
|
bool isPointableElement() const;
|
||||||
|
|
||||||
|
const SVGClipPathElement* clipper() const { return m_clipper; }
|
||||||
|
const SVGMaskElement* masker() const { return m_masker; }
|
||||||
|
float opacity() const { return m_opacity; }
|
||||||
|
|
||||||
|
bool isElement() const final { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable Rect m_paintBoundingBox = Rect::Invalid;
|
||||||
|
const SVGClipPathElement* m_clipper = nullptr;
|
||||||
|
const SVGMaskElement* m_masker = nullptr;
|
||||||
|
float m_opacity = 1.f;
|
||||||
|
|
||||||
|
float m_font_size = 12.f;
|
||||||
|
Display m_display = Display::Inline;
|
||||||
|
Overflow m_overflow = Overflow::Visible;
|
||||||
|
Visibility m_visibility = Visibility::Visible;
|
||||||
|
PointerEvents m_pointer_events = PointerEvents::Auto;
|
||||||
|
|
||||||
|
ElementID m_id;
|
||||||
|
AttributeList m_attributes;
|
||||||
|
SVGPropertyList m_properties;
|
||||||
|
SVGNodeList m_children;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const SVGElement* toSVGElement(const SVGNode* node)
|
||||||
|
{
|
||||||
|
if(node && node->isElement())
|
||||||
|
return static_cast<const SVGElement*>(node);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline SVGElement* toSVGElement(SVGNode* node)
|
||||||
|
{
|
||||||
|
if(node && node->isElement())
|
||||||
|
return static_cast<SVGElement*>(node);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline SVGElement* toSVGElement(const std::unique_ptr<SVGNode>& node)
|
||||||
|
{
|
||||||
|
return toSVGElement(node.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void SVGElement::transverse(T callback)
|
||||||
|
{
|
||||||
|
callback(this);
|
||||||
|
for(const auto& child : m_children) {
|
||||||
|
if(auto element = toSVGElement(child)) {
|
||||||
|
element->transverse(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SVGStyleElement final : public SVGElement {
|
||||||
|
public:
|
||||||
|
SVGStyleElement(Document* document);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGFitToViewBox {
|
||||||
|
public:
|
||||||
|
SVGFitToViewBox(SVGElement* element);
|
||||||
|
|
||||||
|
const SVGRect& viewBox() const { return m_viewBox; }
|
||||||
|
const SVGPreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio; }
|
||||||
|
Transform viewBoxToViewTransform(const Size& viewportSize) const;
|
||||||
|
Rect getClipRect(const Size& viewportSize) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGRect m_viewBox;
|
||||||
|
SVGPreserveAspectRatio m_preserveAspectRatio;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGURIReference {
|
||||||
|
public:
|
||||||
|
SVGURIReference(SVGElement* element);
|
||||||
|
|
||||||
|
const SVGString& href() const { return m_href; }
|
||||||
|
const std::string& hrefString() const { return m_href.value(); }
|
||||||
|
SVGElement* getTargetElement(const Document* document) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGString m_href;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPaintServer {
|
||||||
|
public:
|
||||||
|
SVGPaintServer() = default;
|
||||||
|
SVGPaintServer(const SVGPaintElement* element, const Color& color, float opacity)
|
||||||
|
: m_element(element), m_color(color), m_opacity(opacity)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isRenderable() const { return m_opacity > 0.f && (m_element || m_color.alpha() > 0); }
|
||||||
|
|
||||||
|
const SVGPaintElement* element() const { return m_element; }
|
||||||
|
const Color& color() const { return m_color; }
|
||||||
|
float opacity() const { return m_opacity; }
|
||||||
|
|
||||||
|
bool applyPaint(SVGRenderState& state) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGPaintElement* m_element = nullptr;
|
||||||
|
Color m_color = Color::Transparent;
|
||||||
|
float m_opacity = 0.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGGraphicsElement : public SVGElement {
|
||||||
|
public:
|
||||||
|
SVGGraphicsElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
bool isGraphicsElement() const final { return true; }
|
||||||
|
|
||||||
|
const SVGTransform& transform() const { return m_transform; }
|
||||||
|
Transform localTransform() const override { return m_transform.value(); }
|
||||||
|
|
||||||
|
SVGPaintServer getPaintServer(const Paint& paint, float opacity) const;
|
||||||
|
StrokeData getStrokeData(const SVGLayoutState& state) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGTransform m_transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGSVGElement : public SVGGraphicsElement, public SVGFitToViewBox {
|
||||||
|
public:
|
||||||
|
SVGSVGElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x; }
|
||||||
|
const SVGLength& y() const { return m_y; }
|
||||||
|
const SVGLength& width() const { return m_width; }
|
||||||
|
const SVGLength& height() const { return m_height; }
|
||||||
|
|
||||||
|
Transform localTransform() const override;
|
||||||
|
void render(SVGRenderState& state) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRootElement final : public SVGSVGElement {
|
||||||
|
public:
|
||||||
|
SVGRootElement(Document* document);
|
||||||
|
|
||||||
|
float intrinsicWidth() const { return m_intrinsicWidth; }
|
||||||
|
float intrinsicHeight() const { return m_intrinsicHeight; }
|
||||||
|
|
||||||
|
void setNeedsLayout() { m_intrinsicWidth = -1.f; }
|
||||||
|
bool needsLayout() const { return m_intrinsicWidth == -1.f; }
|
||||||
|
|
||||||
|
SVGRootElement* layoutIfNeeded();
|
||||||
|
|
||||||
|
SVGElement* getElementById(std::string_view id) const;
|
||||||
|
void addElementById(const std::string& id, SVGElement* element);
|
||||||
|
void layout(SVGLayoutState& state) final;
|
||||||
|
|
||||||
|
void forceLayout();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, SVGElement*, std::less<>> m_idCache;
|
||||||
|
float m_intrinsicWidth{-1.f};
|
||||||
|
float m_intrinsicHeight{-1.f};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGUseElement final : public SVGGraphicsElement, public SVGURIReference {
|
||||||
|
public:
|
||||||
|
SVGUseElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x; }
|
||||||
|
const SVGLength& y() const { return m_y; }
|
||||||
|
const SVGLength& width() const { return m_width; }
|
||||||
|
const SVGLength& height() const { return m_height; }
|
||||||
|
|
||||||
|
Transform localTransform() const final;
|
||||||
|
void render(SVGRenderState& state) const final;
|
||||||
|
void build() final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<SVGElement> cloneTargetElement(SVGElement* targetElement);
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGImageElement final : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGImageElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x; }
|
||||||
|
const SVGLength& y() const { return m_y; }
|
||||||
|
const SVGLength& width() const { return m_width; }
|
||||||
|
const SVGLength& height() const { return m_height; }
|
||||||
|
const SVGPreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio; }
|
||||||
|
const Bitmap& image() const { return m_image; }
|
||||||
|
|
||||||
|
Rect fillBoundingBox() const final;
|
||||||
|
Rect strokeBoundingBox() const final;
|
||||||
|
void render(SVGRenderState& state) const final;
|
||||||
|
void parseAttribute(PropertyID id, const std::string& value) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
SVGPreserveAspectRatio m_preserveAspectRatio;
|
||||||
|
Bitmap m_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGSymbolElement final : public SVGGraphicsElement, public SVGFitToViewBox {
|
||||||
|
public:
|
||||||
|
SVGSymbolElement(Document* document);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGGElement final : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGGElement(Document* document);
|
||||||
|
|
||||||
|
void render(SVGRenderState& state) const final;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGDefsElement final : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGDefsElement(Document* document);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGMarkerElement final : public SVGElement, public SVGFitToViewBox {
|
||||||
|
public:
|
||||||
|
SVGMarkerElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& refX() const { return m_refX; }
|
||||||
|
const SVGLength& refY() const { return m_refY; }
|
||||||
|
const SVGLength& markerWidth() const { return m_markerWidth; }
|
||||||
|
const SVGLength& markerHeight() const { return m_markerHeight; }
|
||||||
|
const SVGEnumeration<MarkerUnits>& markerUnits() const { return m_markerUnits; }
|
||||||
|
const SVGAngle& orient() const { return m_orient; }
|
||||||
|
|
||||||
|
Point refPoint() const;
|
||||||
|
Size markerSize() const;
|
||||||
|
|
||||||
|
Transform markerTransform(const Point& origin, float angle, float strokeWidth) const;
|
||||||
|
Rect markerBoundingBox(const Point& origin, float angle, float strokeWidth) const;
|
||||||
|
void renderMarker(SVGRenderState& state, const Point& origin, float angle, float strokeWidth) const;
|
||||||
|
|
||||||
|
Transform localTransform() const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_refX;
|
||||||
|
SVGLength m_refY;
|
||||||
|
SVGLength m_markerWidth;
|
||||||
|
SVGLength m_markerHeight;
|
||||||
|
SVGEnumeration<MarkerUnits> m_markerUnits;
|
||||||
|
SVGAngle m_orient;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGClipPathElement final : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGClipPathElement(Document* document);
|
||||||
|
|
||||||
|
const SVGEnumeration<Units>& clipPathUnits() const { return m_clipPathUnits; }
|
||||||
|
Rect clipBoundingBox(const SVGElement* element) const;
|
||||||
|
|
||||||
|
void applyClipMask(SVGRenderState& state) const;
|
||||||
|
void applyClipPath(SVGRenderState& state) const;
|
||||||
|
|
||||||
|
bool requiresMasking() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGEnumeration<Units> m_clipPathUnits;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGMaskElement final : public SVGElement {
|
||||||
|
public:
|
||||||
|
SVGMaskElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x; }
|
||||||
|
const SVGLength& y() const { return m_y; }
|
||||||
|
const SVGLength& width() const { return m_width; }
|
||||||
|
const SVGLength& height() const { return m_height; }
|
||||||
|
const SVGEnumeration<Units>& maskUnits() const { return m_maskUnits; }
|
||||||
|
const SVGEnumeration<Units>& maskContentUnits() const { return m_maskContentUnits; }
|
||||||
|
|
||||||
|
Rect maskRect(const SVGElement* element) const;
|
||||||
|
Rect maskBoundingBox(const SVGElement* element) const;
|
||||||
|
void applyMask(SVGRenderState& state) const;
|
||||||
|
|
||||||
|
void layoutElement(const SVGLayoutState& state) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
SVGEnumeration<Units> m_maskUnits;
|
||||||
|
SVGEnumeration<Units> m_maskContentUnits;
|
||||||
|
MaskType m_mask_type = MaskType::Luminance;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGELEMENT_H
|
||||||
324
vendor/lunasvg/source/svggeometryelement.cpp
vendored
Normal file
324
vendor/lunasvg/source/svggeometryelement.cpp
vendored
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
#include "svggeometryelement.h"
|
||||||
|
#include "svglayoutstate.h"
|
||||||
|
#include "svgrenderstate.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
Rect SVGMarkerPosition::markerBoundingBox(float strokeWidth) const
|
||||||
|
{
|
||||||
|
return m_element->markerBoundingBox(m_origin, m_angle, strokeWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGMarkerPosition::renderMarker(SVGRenderState& state, float strokeWidth) const
|
||||||
|
{
|
||||||
|
m_element->renderMarker(state, m_origin, m_angle, strokeWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGGeometryElement::SVGGeometryElement(Document* document, ElementID id)
|
||||||
|
: SVGGraphicsElement(document, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGGeometryElement::strokeBoundingBox() const
|
||||||
|
{
|
||||||
|
auto strokeBoundingBox = fillBoundingBox();
|
||||||
|
if(m_stroke.isRenderable()) {
|
||||||
|
float capLimit = m_strokeData.lineWidth() / 2.f;
|
||||||
|
if(m_strokeData.lineCap() == LineCap::Square)
|
||||||
|
capLimit *= PLUTOVG_SQRT2;
|
||||||
|
float joinLimit = m_strokeData.lineWidth() / 2.f;
|
||||||
|
if(m_strokeData.lineJoin() == LineJoin::Miter) {
|
||||||
|
joinLimit *= m_strokeData.miterLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
strokeBoundingBox.inflate(std::max(capLimit, joinLimit));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& markerPosition : m_markerPositions)
|
||||||
|
strokeBoundingBox.unite(markerPosition.markerBoundingBox(m_strokeData.lineWidth()));
|
||||||
|
return strokeBoundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGGeometryElement::layoutElement(const SVGLayoutState& state)
|
||||||
|
{
|
||||||
|
m_fill_rule = state.fill_rule();
|
||||||
|
m_clip_rule = state.clip_rule();
|
||||||
|
m_fill = getPaintServer(state.fill(), state.fill_opacity());
|
||||||
|
m_stroke = getPaintServer(state.stroke(), state.stroke_opacity());
|
||||||
|
m_strokeData = getStrokeData(state);
|
||||||
|
SVGGraphicsElement::layoutElement(state);
|
||||||
|
|
||||||
|
m_path.reset();
|
||||||
|
m_markerPositions.clear();
|
||||||
|
m_fillBoundingBox = updateShape(m_path);
|
||||||
|
updateMarkerPositions(m_markerPositions, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGGeometryElement::updateMarkerPositions(SVGMarkerPositionList& positions, const SVGLayoutState& state)
|
||||||
|
{
|
||||||
|
if(m_path.isEmpty())
|
||||||
|
return;
|
||||||
|
auto markerStart = getMarker(state.marker_start());
|
||||||
|
auto markerMid = getMarker(state.marker_mid());
|
||||||
|
auto markerEnd = getMarker(state.marker_end());
|
||||||
|
if(markerStart == nullptr && markerMid == nullptr && markerEnd == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point origin;
|
||||||
|
Point startPoint;
|
||||||
|
Point inslopePoints[2];
|
||||||
|
Point outslopePoints[2];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
std::array<Point, 3> points;
|
||||||
|
PathIterator it(m_path);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
it.next();
|
||||||
|
|
||||||
|
if(!it.isDone() && (markerStart || markerMid)) {
|
||||||
|
it.currentSegment(points);
|
||||||
|
outslopePoints[0] = origin;
|
||||||
|
outslopePoints[1] = points[0];
|
||||||
|
if(index == 0 && markerStart) {
|
||||||
|
auto slope = outslopePoints[1] - outslopePoints[0];
|
||||||
|
auto angle = 180.f * std::atan2(slope.y, slope.x) / PLUTOVG_PI;
|
||||||
|
const auto& orient = markerStart->orient();
|
||||||
|
if(orient.orientType() == SVGAngle::OrientType::AutoStartReverse)
|
||||||
|
angle -= 180.f;
|
||||||
|
positions.emplace_back(markerStart, origin, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index > 0 && markerMid) {
|
||||||
|
auto inslope = inslopePoints[1] - inslopePoints[0];
|
||||||
|
auto outslope = outslopePoints[1] - outslopePoints[0];
|
||||||
|
auto inangle = 180.f * std::atan2(inslope.y, inslope.x) / PLUTOVG_PI;
|
||||||
|
auto outangle = 180.f * std::atan2(outslope.y, outslope.x) / PLUTOVG_PI;
|
||||||
|
if(std::abs(inangle - outangle) > 180.f)
|
||||||
|
inangle += 360.f;
|
||||||
|
auto angle = (inangle + outangle) * 0.5f;
|
||||||
|
positions.emplace_back(markerMid, origin, angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(markerEnd && it.isDone()) {
|
||||||
|
auto slope = inslopePoints[1] - inslopePoints[0];
|
||||||
|
auto angle = 180.f * std::atan2(slope.y, slope.x) / PLUTOVG_PI;
|
||||||
|
positions.emplace_back(markerEnd, origin, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGGeometryElement::render(SVGRenderState& state) const
|
||||||
|
{
|
||||||
|
if(!isRenderable())
|
||||||
|
return;
|
||||||
|
SVGBlendInfo blendInfo(this);
|
||||||
|
SVGRenderState newState(this, state, localTransform());
|
||||||
|
newState.beginGroup(blendInfo);
|
||||||
|
if(newState.mode() == SVGRenderMode::Clipping) {
|
||||||
|
newState->setColor(Color::White);
|
||||||
|
newState->fillPath(m_path, m_clip_rule, newState.currentTransform());
|
||||||
|
} else {
|
||||||
|
if(m_fill.applyPaint(newState))
|
||||||
|
newState->fillPath(m_path, m_fill_rule, newState.currentTransform());
|
||||||
|
if(m_stroke.applyPaint(newState)) {
|
||||||
|
newState->strokePath(m_path, m_strokeData, newState.currentTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& markerPosition : m_markerPositions) {
|
||||||
|
markerPosition.renderMarker(newState, m_strokeData.lineWidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.endGroup(blendInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGLineElement::SVGLineElement(Document* document)
|
||||||
|
: SVGGeometryElement(document, ElementID::Line)
|
||||||
|
, m_x1(PropertyID::X1, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_y1(PropertyID::Y1, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_x2(PropertyID::X2, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_y2(PropertyID::Y2, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
{
|
||||||
|
addProperty(m_x1);
|
||||||
|
addProperty(m_y1);
|
||||||
|
addProperty(m_x2);
|
||||||
|
addProperty(m_y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGLineElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
LengthContext lengthContext(this);
|
||||||
|
auto x1 = lengthContext.valueForLength(m_x1);
|
||||||
|
auto y1 = lengthContext.valueForLength(m_y1);
|
||||||
|
auto x2 = lengthContext.valueForLength(m_x2);
|
||||||
|
auto y2 = lengthContext.valueForLength(m_y2);
|
||||||
|
|
||||||
|
path.moveTo(x1, y1);
|
||||||
|
path.lineTo(x2, y2);
|
||||||
|
return Rect(x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGRectElement::SVGRectElement(Document* document)
|
||||||
|
: SVGGeometryElement(document, ElementID::Rect)
|
||||||
|
, m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid)
|
||||||
|
, m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid)
|
||||||
|
, m_rx(PropertyID::Rx, LengthDirection::Horizontal, LengthNegativeMode::Forbid)
|
||||||
|
, m_ry(PropertyID::Ry, LengthDirection::Vertical, LengthNegativeMode::Forbid)
|
||||||
|
{
|
||||||
|
addProperty(m_x);
|
||||||
|
addProperty(m_y);
|
||||||
|
addProperty(m_width);
|
||||||
|
addProperty(m_height);
|
||||||
|
addProperty(m_rx);
|
||||||
|
addProperty(m_ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGRectElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
LengthContext lengthContext(this);
|
||||||
|
auto width = lengthContext.valueForLength(m_width);
|
||||||
|
auto height = lengthContext.valueForLength(m_height);
|
||||||
|
if(width <= 0.f || height <= 0.f) {
|
||||||
|
return Rect::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto x = lengthContext.valueForLength(m_x);
|
||||||
|
auto y = lengthContext.valueForLength(m_y);
|
||||||
|
|
||||||
|
auto rx = lengthContext.valueForLength(m_rx);
|
||||||
|
auto ry = lengthContext.valueForLength(m_ry);
|
||||||
|
|
||||||
|
if(rx <= 0.f) rx = ry;
|
||||||
|
if(ry <= 0.f) ry = rx;
|
||||||
|
|
||||||
|
rx = std::min(rx, width / 2.f);
|
||||||
|
ry = std::min(ry, height / 2.f);
|
||||||
|
|
||||||
|
path.addRoundRect(x, y, width, height, rx, ry);
|
||||||
|
return Rect(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGEllipseElement::SVGEllipseElement(Document* document)
|
||||||
|
: SVGGeometryElement(document, ElementID::Ellipse)
|
||||||
|
, m_cx(PropertyID::Cx, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_cy(PropertyID::Cy, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_rx(PropertyID::Rx, LengthDirection::Diagonal, LengthNegativeMode::Forbid)
|
||||||
|
, m_ry(PropertyID::Ry, LengthDirection::Diagonal, LengthNegativeMode::Forbid)
|
||||||
|
{
|
||||||
|
addProperty(m_cx);
|
||||||
|
addProperty(m_cy);
|
||||||
|
addProperty(m_rx);
|
||||||
|
addProperty(m_ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGEllipseElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
LengthContext lengthContext(this);
|
||||||
|
auto rx = lengthContext.valueForLength(m_rx);
|
||||||
|
auto ry = lengthContext.valueForLength(m_ry);
|
||||||
|
if(rx <= 0.f || ry <= 0.f) {
|
||||||
|
return Rect::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cx = lengthContext.valueForLength(m_cx);
|
||||||
|
auto cy = lengthContext.valueForLength(m_cy);
|
||||||
|
path.addEllipse(cx, cy, rx, ry);
|
||||||
|
return Rect(cx - rx, cy - ry, rx + rx, ry + ry);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGCircleElement::SVGCircleElement(Document* document)
|
||||||
|
: SVGGeometryElement(document, ElementID::Circle)
|
||||||
|
, m_cx(PropertyID::Cx, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_cy(PropertyID::Cy, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_r(PropertyID::R, LengthDirection::Diagonal, LengthNegativeMode::Forbid)
|
||||||
|
{
|
||||||
|
addProperty(m_cx);
|
||||||
|
addProperty(m_cy);
|
||||||
|
addProperty(m_r);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGCircleElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
LengthContext lengthContext(this);
|
||||||
|
auto r = lengthContext.valueForLength(m_r);
|
||||||
|
if(r <= 0.f) {
|
||||||
|
return Rect::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cx = lengthContext.valueForLength(m_cx);
|
||||||
|
auto cy = lengthContext.valueForLength(m_cy);
|
||||||
|
path.addEllipse(cx, cy, r, r);
|
||||||
|
return Rect(cx - r, cy - r, r + r, r + r);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGPolyElement::SVGPolyElement(Document* document, ElementID id)
|
||||||
|
: SVGGeometryElement(document, id)
|
||||||
|
, m_points(PropertyID::Points)
|
||||||
|
{
|
||||||
|
addProperty(m_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGPolyElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
const auto& points = m_points.values();
|
||||||
|
if(points.empty()) {
|
||||||
|
return Rect::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.moveTo(points[0].x, points[0].y);
|
||||||
|
for(size_t i = 1; i < points.size(); i++) {
|
||||||
|
path.lineTo(points[i].x, points[i].y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(id() == ElementID::Polygon)
|
||||||
|
path.close();
|
||||||
|
return path.boundingRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGPathElement::SVGPathElement(Document* document)
|
||||||
|
: SVGGeometryElement(document, ElementID::Path)
|
||||||
|
, m_d(PropertyID::D)
|
||||||
|
{
|
||||||
|
addProperty(m_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGPathElement::updateShape(Path& path)
|
||||||
|
{
|
||||||
|
path = m_d.value();
|
||||||
|
return path.boundingRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
139
vendor/lunasvg/source/svggeometryelement.h
vendored
Normal file
139
vendor/lunasvg/source/svggeometryelement.h
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#ifndef LUNASVG_SVGGEOMETRYELEMENT_H
|
||||||
|
#define LUNASVG_SVGGEOMETRYELEMENT_H
|
||||||
|
|
||||||
|
#include "svgelement.h"
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
class SVGMarkerPosition {
|
||||||
|
public:
|
||||||
|
SVGMarkerPosition(const SVGMarkerElement* element, const Point& origin, float angle)
|
||||||
|
: m_element(element), m_origin(origin), m_angle(angle)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const SVGMarkerElement* element() const { return m_element; }
|
||||||
|
const Point& origin() const { return m_origin; }
|
||||||
|
float angle() const { return m_angle; }
|
||||||
|
|
||||||
|
Rect markerBoundingBox(float strokeWidth) const;
|
||||||
|
void renderMarker(SVGRenderState& state, float strokeWidth) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGMarkerElement* m_element;
|
||||||
|
Point m_origin;
|
||||||
|
float m_angle;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SVGMarkerPositionList = std::vector<SVGMarkerPosition>;
|
||||||
|
|
||||||
|
class SVGGeometryElement : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGGeometryElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
bool isGeometryElement() const final { return true; }
|
||||||
|
|
||||||
|
Rect fillBoundingBox() const override { return m_fillBoundingBox; }
|
||||||
|
Rect strokeBoundingBox() const override;
|
||||||
|
void layoutElement(const SVGLayoutState& state) override;
|
||||||
|
|
||||||
|
bool isRenderable() const { return !m_path.isNull() && !isDisplayNone() && !isVisibilityHidden(); }
|
||||||
|
|
||||||
|
FillRule fill_rule() const { return m_fill_rule; }
|
||||||
|
FillRule clip_rule() const { return m_clip_rule; }
|
||||||
|
|
||||||
|
virtual Rect updateShape(Path& path) = 0;
|
||||||
|
|
||||||
|
void updateMarkerPositions(SVGMarkerPositionList& positions, const SVGLayoutState& state);
|
||||||
|
void render(SVGRenderState& state) const override;
|
||||||
|
|
||||||
|
const Path& path() const { return m_path; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Path m_path;
|
||||||
|
Rect m_fillBoundingBox;
|
||||||
|
StrokeData m_strokeData;
|
||||||
|
|
||||||
|
SVGPaintServer m_fill;
|
||||||
|
SVGPaintServer m_stroke;
|
||||||
|
SVGMarkerPositionList m_markerPositions;
|
||||||
|
|
||||||
|
FillRule m_fill_rule = FillRule::NonZero;
|
||||||
|
FillRule m_clip_rule = FillRule::NonZero;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGLineElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGLineElement(Document* document);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_x1;
|
||||||
|
SVGLength m_y1;
|
||||||
|
SVGLength m_x2;
|
||||||
|
SVGLength m_y2;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRectElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGRectElement(Document* document);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
SVGLength m_rx;
|
||||||
|
SVGLength m_ry;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGEllipseElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGEllipseElement(Document* document);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_cx;
|
||||||
|
SVGLength m_cy;
|
||||||
|
SVGLength m_rx;
|
||||||
|
SVGLength m_ry;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGCircleElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGCircleElement(Document* document);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLength m_cx;
|
||||||
|
SVGLength m_cy;
|
||||||
|
SVGLength m_r;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPolyElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGPolyElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGPointList m_points;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPathElement final : public SVGGeometryElement {
|
||||||
|
public:
|
||||||
|
SVGPathElement(Document* document);
|
||||||
|
|
||||||
|
Rect updateShape(Path& path) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGPath m_d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGGEOMETRYELEMENT_H
|
||||||
607
vendor/lunasvg/source/svglayoutstate.cpp
vendored
Normal file
607
vendor/lunasvg/source/svglayoutstate.cpp
vendored
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
#include "svglayoutstate.h"
|
||||||
|
#include "svgelement.h"
|
||||||
|
#include "svgparserutils.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
static std::optional<Color> parseColorValue(std::string_view& input, const SVGLayoutState* state)
|
||||||
|
{
|
||||||
|
if(skipString(input, "currentColor")) {
|
||||||
|
return state->color();
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_color_t color;
|
||||||
|
int length = plutovg_color_parse(&color, input.data(), input.length());
|
||||||
|
if(length == 0)
|
||||||
|
return std::nullopt;
|
||||||
|
input.remove_prefix(length);
|
||||||
|
return Color(plutovg_color_to_argb32(&color));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color parseColor(std::string_view input, const SVGLayoutState* state, const Color& defaultValue)
|
||||||
|
{
|
||||||
|
auto color = parseColorValue(input, state);
|
||||||
|
if(!color || !input.empty())
|
||||||
|
color = defaultValue;
|
||||||
|
return color.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Color parseColorOrNone(std::string_view input, const SVGLayoutState* state, const Color& defaultValue)
|
||||||
|
{
|
||||||
|
if(input.compare("none") == 0)
|
||||||
|
return Color::Transparent;
|
||||||
|
return parseColor(input, state, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseUrlValue(std::string_view& input, std::string& value)
|
||||||
|
{
|
||||||
|
if(!skipString(input, "url")
|
||||||
|
|| !skipOptionalSpaces(input)
|
||||||
|
|| !skipDelimiter(input, '(')
|
||||||
|
|| !skipOptionalSpaces(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(input.front()) {
|
||||||
|
case '\'':
|
||||||
|
case '\"': {
|
||||||
|
auto delim = input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, '#'))
|
||||||
|
return false;
|
||||||
|
while(!input.empty() && input.front() != delim) {
|
||||||
|
value += input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, delim))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
} case '#': {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
while(!input.empty() && input.front() != ')') {
|
||||||
|
value += input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return skipOptionalSpaces(input) && skipDelimiter(input, ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string parseUrl(std::string_view input)
|
||||||
|
{
|
||||||
|
std::string value;
|
||||||
|
if(!parseUrlValue(input, value) || !input.empty())
|
||||||
|
value.clear();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Paint parsePaint(std::string_view input, const SVGLayoutState* state, const Color& defaultValue)
|
||||||
|
{
|
||||||
|
std::string id;
|
||||||
|
if(!parseUrlValue(input, id))
|
||||||
|
return Paint(parseColorOrNone(input, state, defaultValue));
|
||||||
|
if(skipOptionalSpaces(input))
|
||||||
|
return Paint(id, parseColorOrNone(input, state, defaultValue));
|
||||||
|
return Paint(id, Color::Transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float parseNumberOrPercentage(std::string_view input, bool allowPercentage, float defaultValue)
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return defaultValue;
|
||||||
|
if(allowPercentage) {
|
||||||
|
if(skipDelimiter(input, '%'))
|
||||||
|
value /= 100.f;
|
||||||
|
value = std::clamp(value, 0.f, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!input.empty())
|
||||||
|
return defaultValue;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Length parseLength(std::string_view input, LengthNegativeMode mode, const Length& defaultValue)
|
||||||
|
{
|
||||||
|
Length value;
|
||||||
|
if(!value.parse(input, mode))
|
||||||
|
value = defaultValue;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BaselineShift parseBaselineShift(std::string_view input)
|
||||||
|
{
|
||||||
|
if(input.compare("baseline") == 0)
|
||||||
|
return BaselineShift::Type::Baseline;
|
||||||
|
if(input.compare("sub") == 0)
|
||||||
|
return BaselineShift::Type::Sub;
|
||||||
|
if(input.compare("super") == 0)
|
||||||
|
return BaselineShift::Type::Super;
|
||||||
|
return parseLength(input, LengthNegativeMode::Allow, Length(0.f, LengthUnits::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
static LengthList parseDashArray(std::string_view input)
|
||||||
|
{
|
||||||
|
if(input.compare("none") == 0)
|
||||||
|
return LengthList();
|
||||||
|
LengthList values;
|
||||||
|
do {
|
||||||
|
size_t count = 0;
|
||||||
|
while(count < input.length() && input[count] != ',' && !IS_WS(input[count]))
|
||||||
|
++count;
|
||||||
|
Length value(0, LengthUnits::None);
|
||||||
|
if(!value.parse(input.substr(0, count), LengthNegativeMode::Forbid))
|
||||||
|
return LengthList();
|
||||||
|
input.remove_prefix(count);
|
||||||
|
values.push_back(std::move(value));
|
||||||
|
} while(skipOptionalSpacesOrComma(input));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Length parseLengthOrNormal(std::string_view input)
|
||||||
|
{
|
||||||
|
if(input.compare("normal") == 0)
|
||||||
|
return Length(0, LengthUnits::None);
|
||||||
|
return parseLength(input, LengthNegativeMode::Allow, Length(0, LengthUnits::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
static float parseFontSize(std::string_view input, const SVGLayoutState* state)
|
||||||
|
{
|
||||||
|
auto length = parseLength(input, LengthNegativeMode::Forbid, Length(12, LengthUnits::None));
|
||||||
|
if(length.units() == LengthUnits::Percent)
|
||||||
|
return length.value() * state->font_size() / 100.f;
|
||||||
|
if(length.units() == LengthUnits::Ex)
|
||||||
|
return length.value() * state->font_size() / 2.f;
|
||||||
|
if(length.units() == LengthUnits::Em)
|
||||||
|
return length.value() * state->font_size();
|
||||||
|
return length.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Enum, unsigned int N>
|
||||||
|
static Enum parseEnumValue(std::string_view input, const SVGEnumerationEntry<Enum>(&entries)[N], Enum defaultValue)
|
||||||
|
{
|
||||||
|
for(const auto& entry : entries) {
|
||||||
|
if(input == entry.second) {
|
||||||
|
return entry.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Display parseDisplay(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<Display> entries[] = {
|
||||||
|
{Display::Inline, "inline"},
|
||||||
|
{Display::None, "none"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, Display::Inline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Visibility parseVisibility(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<Visibility> entries[] = {
|
||||||
|
{Visibility::Visible, "visible"},
|
||||||
|
{Visibility::Hidden, "hidden"},
|
||||||
|
{Visibility::Collapse, "collapse"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, Visibility::Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Overflow parseOverflow(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<Overflow> entries[] = {
|
||||||
|
{Overflow::Visible, "visible"},
|
||||||
|
{Overflow::Hidden, "hidden"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, Overflow::Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PointerEvents parsePointerEvents(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<PointerEvents> entries[] = {
|
||||||
|
{PointerEvents::None,"none"},
|
||||||
|
{PointerEvents::Auto,"auto"},
|
||||||
|
{PointerEvents::Stroke,"stroke"},
|
||||||
|
{PointerEvents::Fill,"fill"},
|
||||||
|
{PointerEvents::Painted,"painted"},
|
||||||
|
{PointerEvents::Visible,"visible"},
|
||||||
|
{PointerEvents::VisibleStroke,"visibleStroke"},
|
||||||
|
{PointerEvents::VisibleFill,"visibleFill"},
|
||||||
|
{PointerEvents::VisiblePainted,"visiblePainted"},
|
||||||
|
{PointerEvents::BoundingBox,"bounding-box"},
|
||||||
|
{PointerEvents::All,"all"},
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, PointerEvents::Auto);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FontWeight parseFontWeight(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<FontWeight> entries[] = {
|
||||||
|
{FontWeight::Normal, "normal"},
|
||||||
|
{FontWeight::Bold, "bold"},
|
||||||
|
{FontWeight::Bold, "bolder"},
|
||||||
|
{FontWeight::Normal, "lighter"},
|
||||||
|
{FontWeight::Normal, "100"},
|
||||||
|
{FontWeight::Normal, "200"},
|
||||||
|
{FontWeight::Normal, "300"},
|
||||||
|
{FontWeight::Normal, "400"},
|
||||||
|
{FontWeight::Normal, "500"},
|
||||||
|
{FontWeight::Bold, "600"},
|
||||||
|
{FontWeight::Bold, "700"},
|
||||||
|
{FontWeight::Bold, "800"},
|
||||||
|
{FontWeight::Bold, "900"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, FontWeight::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FontStyle parseFontStyle(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<FontStyle> entries[] = {
|
||||||
|
{FontStyle::Normal, "normal"},
|
||||||
|
{FontStyle::Italic, "italic"},
|
||||||
|
{FontStyle::Italic, "oblique"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, FontStyle::Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlignmentBaseline parseAlignmentBaseline(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<AlignmentBaseline> entries[] = {
|
||||||
|
{AlignmentBaseline::Auto, "auto"},
|
||||||
|
{AlignmentBaseline::Baseline, "baseline"},
|
||||||
|
{AlignmentBaseline::BeforeEdge, "before-edge"},
|
||||||
|
{AlignmentBaseline::TextBeforeEdge, "text-before-edge"},
|
||||||
|
{AlignmentBaseline::Middle, "middle"},
|
||||||
|
{AlignmentBaseline::Central, "central"},
|
||||||
|
{AlignmentBaseline::AfterEdge, "after-edge"},
|
||||||
|
{AlignmentBaseline::TextAfterEdge, "text-after-edge"},
|
||||||
|
{AlignmentBaseline::Ideographic, "ideographic"},
|
||||||
|
{AlignmentBaseline::Alphabetic, "alphabetic"},
|
||||||
|
{AlignmentBaseline::Hanging, "hanging"},
|
||||||
|
{AlignmentBaseline::Mathematical, "mathematical"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, AlignmentBaseline::Auto);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DominantBaseline parseDominantBaseline(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<DominantBaseline> entries[] = {
|
||||||
|
{DominantBaseline::Auto, "auto"},
|
||||||
|
{DominantBaseline::UseScript, "use-script"},
|
||||||
|
{DominantBaseline::NoChange, "no-change"},
|
||||||
|
{DominantBaseline::ResetSize, "reset-size"},
|
||||||
|
{DominantBaseline::Ideographic, "ideographic"},
|
||||||
|
{DominantBaseline::Alphabetic, "alphabetic"},
|
||||||
|
{DominantBaseline::Hanging, "hanging"},
|
||||||
|
{DominantBaseline::Mathematical, "mathematical"},
|
||||||
|
{DominantBaseline::Central, "central"},
|
||||||
|
{DominantBaseline::Middle, "middle"},
|
||||||
|
{DominantBaseline::TextAfterEdge, "text-after-edge"},
|
||||||
|
{DominantBaseline::TextBeforeEdge, "text-before-edge"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, DominantBaseline::Auto);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Direction parseDirection(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<Direction> entries[] = {
|
||||||
|
{Direction::Ltr, "ltr"},
|
||||||
|
{Direction::Rtl, "rtl"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, Direction::Ltr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WritingMode parseWritingMode(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<WritingMode> entries[] = {
|
||||||
|
{WritingMode::Horizontal, "horizontal-tb"},
|
||||||
|
{WritingMode::Vertical, "vertical-rl"},
|
||||||
|
{WritingMode::Vertical, "vertical-lr"},
|
||||||
|
{WritingMode::Horizontal, "lr-tb"},
|
||||||
|
{WritingMode::Horizontal, "lr"},
|
||||||
|
{WritingMode::Horizontal, "rl-tb"},
|
||||||
|
{WritingMode::Horizontal, "rl"},
|
||||||
|
{WritingMode::Vertical, "tb-rl"},
|
||||||
|
{WritingMode::Vertical, "tb"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, WritingMode::Horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TextOrientation parseTextOrientation(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<TextOrientation> entries[] = {
|
||||||
|
{TextOrientation::Mixed, "mixed"},
|
||||||
|
{TextOrientation::Upright, "upright"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, TextOrientation::Mixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TextAnchor parseTextAnchor(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<TextAnchor> entries[] = {
|
||||||
|
{TextAnchor::Start, "start"},
|
||||||
|
{TextAnchor::Middle, "middle"},
|
||||||
|
{TextAnchor::End, "end"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, TextAnchor::Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WhiteSpace parseWhiteSpace(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<WhiteSpace> entries[] = {
|
||||||
|
{WhiteSpace::Default, "default"},
|
||||||
|
{WhiteSpace::Preserve, "preserve"},
|
||||||
|
{WhiteSpace::Default, "normal"},
|
||||||
|
{WhiteSpace::Default, "nowrap"},
|
||||||
|
{WhiteSpace::Default, "pre-line"},
|
||||||
|
{WhiteSpace::Preserve, "pre-wrap"},
|
||||||
|
{WhiteSpace::Preserve, "pre"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, WhiteSpace::Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MaskType parseMaskType(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<MaskType> entries[] = {
|
||||||
|
{MaskType::Luminance, "luminance"},
|
||||||
|
{MaskType::Alpha, "alpha"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, MaskType::Luminance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FillRule parseFillRule(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<FillRule> entries[] = {
|
||||||
|
{FillRule::NonZero, "nonzero"},
|
||||||
|
{FillRule::EvenOdd, "evenodd"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, FillRule::NonZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LineCap parseLineCap(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<LineCap> entries[] = {
|
||||||
|
{LineCap::Butt, "butt"},
|
||||||
|
{LineCap::Round, "round"},
|
||||||
|
{LineCap::Square, "square"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, LineCap::Butt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LineJoin parseLineJoin(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<LineJoin> entries[] = {
|
||||||
|
{LineJoin::Miter, "miter"},
|
||||||
|
{LineJoin::Round, "round"},
|
||||||
|
{LineJoin::Bevel, "bevel"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnumValue(input, entries, LineJoin::Miter);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* element)
|
||||||
|
: m_parent(&parent)
|
||||||
|
, m_element(element)
|
||||||
|
, m_fill(parent.fill())
|
||||||
|
, m_stroke(parent.stroke())
|
||||||
|
, m_color(parent.color())
|
||||||
|
, m_fill_opacity(parent.fill_opacity())
|
||||||
|
, m_stroke_opacity(parent.stroke_opacity())
|
||||||
|
, m_stroke_miterlimit(parent.stroke_miterlimit())
|
||||||
|
, m_font_size(parent.font_size())
|
||||||
|
, m_letter_spacing(parent.letter_spacing())
|
||||||
|
, m_word_spacing(parent.word_spacing())
|
||||||
|
, m_stroke_width(parent.stroke_width())
|
||||||
|
, m_stroke_dashoffset(parent.stroke_dashoffset())
|
||||||
|
, m_stroke_dasharray(parent.stroke_dasharray())
|
||||||
|
, m_stroke_linecap(parent.stroke_linecap())
|
||||||
|
, m_stroke_linejoin(parent.stroke_linejoin())
|
||||||
|
, m_fill_rule(parent.fill_rule())
|
||||||
|
, m_clip_rule(parent.clip_rule())
|
||||||
|
, m_font_weight(parent.font_weight())
|
||||||
|
, m_font_style(parent.font_style())
|
||||||
|
, m_dominant_baseline(parent.dominant_baseline())
|
||||||
|
, m_text_anchor(parent.text_anchor())
|
||||||
|
, m_white_space(parent.white_space())
|
||||||
|
, m_writing_mode(parent.writing_mode())
|
||||||
|
, m_text_orientation(parent.text_orientation())
|
||||||
|
, m_direction(parent.direction())
|
||||||
|
, m_visibility(parent.visibility())
|
||||||
|
, m_overflow(element->isRootElement() ? Overflow::Visible : Overflow::Hidden)
|
||||||
|
, m_pointer_events(parent.pointer_events())
|
||||||
|
, m_marker_start(parent.marker_start())
|
||||||
|
, m_marker_mid(parent.marker_mid())
|
||||||
|
, m_marker_end(parent.marker_end())
|
||||||
|
, m_font_family(parent.font_family())
|
||||||
|
{
|
||||||
|
for(const auto& attribute : element->attributes()) {
|
||||||
|
std::string_view input(attribute.value());
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(input.empty() || input.compare("inherit") == 0)
|
||||||
|
continue;
|
||||||
|
switch(attribute.id()) {
|
||||||
|
case PropertyID::Fill:
|
||||||
|
m_fill = parsePaint(input, this, Color::Black);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke:
|
||||||
|
m_stroke = parsePaint(input, this, Color::Transparent);
|
||||||
|
break;
|
||||||
|
case PropertyID::Color:
|
||||||
|
m_color = parseColor(input, this, Color::Black);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stop_Color:
|
||||||
|
m_stop_color = parseColor(input, this, Color::Black);
|
||||||
|
break;
|
||||||
|
case PropertyID::Opacity:
|
||||||
|
m_opacity = parseNumberOrPercentage(input, true, 1.f);
|
||||||
|
break;
|
||||||
|
case PropertyID::Fill_Opacity:
|
||||||
|
m_fill_opacity = parseNumberOrPercentage(input, true, 1.f);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Opacity:
|
||||||
|
m_stroke_opacity = parseNumberOrPercentage(input, true, 1.f);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stop_Opacity:
|
||||||
|
m_stop_opacity = parseNumberOrPercentage(input, true, 1.f);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Miterlimit:
|
||||||
|
m_stroke_miterlimit = parseNumberOrPercentage(input, false, 4.f);
|
||||||
|
break;
|
||||||
|
case PropertyID::Font_Size:
|
||||||
|
m_font_size = parseFontSize(input, this);
|
||||||
|
break;
|
||||||
|
case PropertyID::Letter_Spacing:
|
||||||
|
m_letter_spacing = parseLengthOrNormal(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Word_Spacing:
|
||||||
|
m_word_spacing = parseLengthOrNormal(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Baseline_Shift:
|
||||||
|
m_baseline_shift = parseBaselineShift(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Width:
|
||||||
|
m_stroke_width = parseLength(input, LengthNegativeMode::Forbid, Length(1.f, LengthUnits::None));
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Dashoffset:
|
||||||
|
m_stroke_dashoffset = parseLength(input, LengthNegativeMode::Allow, Length(0.f, LengthUnits::None));
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Dasharray:
|
||||||
|
m_stroke_dasharray = parseDashArray(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Linecap:
|
||||||
|
m_stroke_linecap = parseLineCap(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Stroke_Linejoin:
|
||||||
|
m_stroke_linejoin = parseLineJoin(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Fill_Rule:
|
||||||
|
m_fill_rule = parseFillRule(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Clip_Rule:
|
||||||
|
m_clip_rule = parseFillRule(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Font_Weight:
|
||||||
|
m_font_weight = parseFontWeight(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Font_Style:
|
||||||
|
m_font_style = parseFontStyle(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Alignment_Baseline:
|
||||||
|
m_alignment_baseline = parseAlignmentBaseline(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Dominant_Baseline:
|
||||||
|
m_dominant_baseline = parseDominantBaseline(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Direction:
|
||||||
|
m_direction = parseDirection(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Text_Anchor:
|
||||||
|
m_text_anchor = parseTextAnchor(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::White_Space:
|
||||||
|
m_white_space = parseWhiteSpace(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Writing_Mode:
|
||||||
|
m_writing_mode = parseWritingMode(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Text_Orientation:
|
||||||
|
m_text_orientation = parseTextOrientation(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Display:
|
||||||
|
m_display = parseDisplay(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Visibility:
|
||||||
|
m_visibility = parseVisibility(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Overflow:
|
||||||
|
m_overflow = parseOverflow(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Pointer_Events:
|
||||||
|
m_pointer_events = parsePointerEvents(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Mask_Type:
|
||||||
|
m_mask_type = parseMaskType(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Mask:
|
||||||
|
m_mask = parseUrl(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Clip_Path:
|
||||||
|
m_clip_path = parseUrl(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Marker_Start:
|
||||||
|
m_marker_start = parseUrl(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Marker_Mid:
|
||||||
|
m_marker_mid = parseUrl(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Marker_End:
|
||||||
|
m_marker_end = parseUrl(input);
|
||||||
|
break;
|
||||||
|
case PropertyID::Font_Family:
|
||||||
|
m_font_family.assign(input);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Font SVGLayoutState::font() const
|
||||||
|
{
|
||||||
|
auto bold = m_font_weight == FontWeight::Bold;
|
||||||
|
auto italic = m_font_style == FontStyle::Italic;
|
||||||
|
|
||||||
|
FontFace face;
|
||||||
|
std::string_view input(m_font_family);
|
||||||
|
while(!input.empty() && face.isNull()) {
|
||||||
|
auto family = input.substr(0, input.find(','));
|
||||||
|
input.remove_prefix(family.length());
|
||||||
|
if(!input.empty() && input.front() == ',')
|
||||||
|
input.remove_prefix(1);
|
||||||
|
stripLeadingAndTrailingSpaces(family);
|
||||||
|
if(!family.empty() && (family.front() == '\'' || family.front() == '"')) {
|
||||||
|
auto quote = family.front();
|
||||||
|
family.remove_prefix(1);
|
||||||
|
if(!family.empty() && family.back() == quote)
|
||||||
|
family.remove_suffix(1);
|
||||||
|
stripLeadingAndTrailingSpaces(family);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string font_family(family);
|
||||||
|
if(!font_family.empty()) {
|
||||||
|
face = fontFaceCache()->getFontFace(font_family, bold, italic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(face.isNull())
|
||||||
|
face = fontFaceCache()->getFontFace(emptyString, bold, italic);
|
||||||
|
return Font(face, m_font_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
129
vendor/lunasvg/source/svglayoutstate.h
vendored
Normal file
129
vendor/lunasvg/source/svglayoutstate.h
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#ifndef LUNASVG_SVGLAYOUTSTATE_H
|
||||||
|
#define LUNASVG_SVGLAYOUTSTATE_H
|
||||||
|
|
||||||
|
#include "svgproperty.h"
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
class SVGLayoutState {
|
||||||
|
public:
|
||||||
|
SVGLayoutState() = default;
|
||||||
|
SVGLayoutState(const SVGLayoutState& parent, const SVGElement* element);
|
||||||
|
|
||||||
|
const SVGLayoutState* parent() const { return m_parent; }
|
||||||
|
const SVGElement* element() const { return m_element; }
|
||||||
|
|
||||||
|
const Paint& fill() const { return m_fill; }
|
||||||
|
const Paint& stroke() const { return m_stroke; }
|
||||||
|
|
||||||
|
const Color& color() const { return m_color; }
|
||||||
|
const Color& stop_color() const { return m_stop_color; }
|
||||||
|
|
||||||
|
float opacity() const { return m_opacity; }
|
||||||
|
float stop_opacity() const { return m_stop_opacity; }
|
||||||
|
float fill_opacity() const { return m_fill_opacity; }
|
||||||
|
float stroke_opacity() const { return m_stroke_opacity; }
|
||||||
|
float stroke_miterlimit() const { return m_stroke_miterlimit; }
|
||||||
|
float font_size() const { return m_font_size; }
|
||||||
|
|
||||||
|
const Length& letter_spacing() const { return m_letter_spacing; }
|
||||||
|
const Length& word_spacing() const { return m_word_spacing; }
|
||||||
|
|
||||||
|
const BaselineShift& baseline_shift() const { return m_baseline_shift; }
|
||||||
|
const Length& stroke_width() const { return m_stroke_width; }
|
||||||
|
const Length& stroke_dashoffset() const { return m_stroke_dashoffset; }
|
||||||
|
const LengthList& stroke_dasharray() const { return m_stroke_dasharray; }
|
||||||
|
|
||||||
|
LineCap stroke_linecap() const { return m_stroke_linecap; }
|
||||||
|
LineJoin stroke_linejoin() const { return m_stroke_linejoin; }
|
||||||
|
|
||||||
|
FillRule fill_rule() const { return m_fill_rule; }
|
||||||
|
FillRule clip_rule() const { return m_clip_rule; }
|
||||||
|
|
||||||
|
FontWeight font_weight() const { return m_font_weight; }
|
||||||
|
FontStyle font_style() const { return m_font_style; }
|
||||||
|
|
||||||
|
AlignmentBaseline alignment_baseline() const { return m_alignment_baseline; }
|
||||||
|
DominantBaseline dominant_baseline() const { return m_dominant_baseline; }
|
||||||
|
|
||||||
|
TextAnchor text_anchor() const { return m_text_anchor; }
|
||||||
|
WhiteSpace white_space() const { return m_white_space; }
|
||||||
|
WritingMode writing_mode() const { return m_writing_mode; }
|
||||||
|
TextOrientation text_orientation() const { return m_text_orientation; }
|
||||||
|
Direction direction() const { return m_direction; }
|
||||||
|
|
||||||
|
Display display() const { return m_display; }
|
||||||
|
Visibility visibility() const { return m_visibility; }
|
||||||
|
Overflow overflow() const { return m_overflow; }
|
||||||
|
PointerEvents pointer_events() const { return m_pointer_events; }
|
||||||
|
MaskType mask_type() const { return m_mask_type; }
|
||||||
|
|
||||||
|
const std::string& mask() const { return m_mask; }
|
||||||
|
const std::string& clip_path() const { return m_clip_path; }
|
||||||
|
const std::string& marker_start() const { return m_marker_start; }
|
||||||
|
const std::string& marker_mid() const { return m_marker_mid; }
|
||||||
|
const std::string& marker_end() const { return m_marker_end; }
|
||||||
|
const std::string& font_family() const { return m_font_family; }
|
||||||
|
|
||||||
|
Font font() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGLayoutState* m_parent = nullptr;
|
||||||
|
const SVGElement* m_element = nullptr;
|
||||||
|
|
||||||
|
Paint m_fill{Color::Black};
|
||||||
|
Paint m_stroke{Color::Transparent};
|
||||||
|
|
||||||
|
Color m_color = Color::Black;
|
||||||
|
Color m_stop_color = Color::Black;
|
||||||
|
|
||||||
|
float m_opacity = 1.f;
|
||||||
|
float m_fill_opacity = 1.f;
|
||||||
|
float m_stroke_opacity = 1.f;
|
||||||
|
float m_stop_opacity = 1.f;
|
||||||
|
float m_stroke_miterlimit = 4.f;
|
||||||
|
float m_font_size = 12.f;
|
||||||
|
|
||||||
|
Length m_letter_spacing{0.f, LengthUnits::None};
|
||||||
|
Length m_word_spacing{0.f, LengthUnits::None};
|
||||||
|
|
||||||
|
BaselineShift m_baseline_shift;
|
||||||
|
Length m_stroke_width{1.f, LengthUnits::None};
|
||||||
|
Length m_stroke_dashoffset{0.f, LengthUnits::None};
|
||||||
|
LengthList m_stroke_dasharray;
|
||||||
|
|
||||||
|
LineCap m_stroke_linecap = LineCap::Butt;
|
||||||
|
LineJoin m_stroke_linejoin = LineJoin::Miter;
|
||||||
|
|
||||||
|
FillRule m_fill_rule = FillRule::NonZero;
|
||||||
|
FillRule m_clip_rule = FillRule::NonZero;
|
||||||
|
|
||||||
|
FontWeight m_font_weight = FontWeight::Normal;
|
||||||
|
FontStyle m_font_style = FontStyle::Normal;
|
||||||
|
|
||||||
|
AlignmentBaseline m_alignment_baseline = AlignmentBaseline::Auto;
|
||||||
|
DominantBaseline m_dominant_baseline = DominantBaseline::Auto;
|
||||||
|
|
||||||
|
TextAnchor m_text_anchor = TextAnchor::Start;
|
||||||
|
WhiteSpace m_white_space = WhiteSpace::Default;
|
||||||
|
WritingMode m_writing_mode = WritingMode::Horizontal;
|
||||||
|
TextOrientation m_text_orientation = TextOrientation::Mixed;
|
||||||
|
Direction m_direction = Direction::Ltr;
|
||||||
|
|
||||||
|
Display m_display = Display::Inline;
|
||||||
|
Visibility m_visibility = Visibility::Visible;
|
||||||
|
Overflow m_overflow = Overflow::Visible;
|
||||||
|
PointerEvents m_pointer_events = PointerEvents::Auto;
|
||||||
|
MaskType m_mask_type = MaskType::Luminance;
|
||||||
|
|
||||||
|
std::string m_mask;
|
||||||
|
std::string m_clip_path;
|
||||||
|
std::string m_marker_start;
|
||||||
|
std::string m_marker_mid;
|
||||||
|
std::string m_marker_end;
|
||||||
|
std::string m_font_family;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGLAYOUTSTATE_H
|
||||||
364
vendor/lunasvg/source/svgpaintelement.cpp
vendored
Normal file
364
vendor/lunasvg/source/svgpaintelement.cpp
vendored
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#include "svgpaintelement.h"
|
||||||
|
#include "svglayoutstate.h"
|
||||||
|
#include "svgrenderstate.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
SVGPaintElement::SVGPaintElement(Document* document, ElementID id)
|
||||||
|
: SVGElement(document, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGStopElement::SVGStopElement(Document* document)
|
||||||
|
: SVGElement(document, ElementID::Stop)
|
||||||
|
, m_offset(PropertyID::Offset, 0.f)
|
||||||
|
{
|
||||||
|
addProperty(m_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGStopElement::layoutElement(const SVGLayoutState& state)
|
||||||
|
{
|
||||||
|
m_stop_color = state.stop_color();
|
||||||
|
m_stop_opacity = state.stop_opacity();
|
||||||
|
SVGElement::layoutElement(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientStop SVGStopElement::gradientStop(float opacity) const
|
||||||
|
{
|
||||||
|
Color stopColor = m_stop_color.colorWithAlpha(m_stop_opacity * opacity);
|
||||||
|
GradientStop gradientStop = {
|
||||||
|
m_offset.value(), { stopColor.redF(), stopColor.greenF(), stopColor.blueF(), stopColor.alphaF() }
|
||||||
|
};
|
||||||
|
|
||||||
|
return gradientStop;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGGradientElement::SVGGradientElement(Document* document, ElementID id)
|
||||||
|
: SVGPaintElement(document, id)
|
||||||
|
, SVGURIReference(this)
|
||||||
|
, m_gradientTransform(PropertyID::GradientTransform)
|
||||||
|
, m_gradientUnits(PropertyID::GradientUnits, Units::ObjectBoundingBox)
|
||||||
|
, m_spreadMethod(PropertyID::SpreadMethod, SpreadMethod::Pad)
|
||||||
|
{
|
||||||
|
addProperty(m_gradientTransform);
|
||||||
|
addProperty(m_gradientUnits);
|
||||||
|
addProperty(m_spreadMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGGradientElement::collectGradientAttributes(SVGGradientAttributes& attributes) const
|
||||||
|
{
|
||||||
|
if(!attributes.hasGradientTransform() && hasAttribute(PropertyID::GradientTransform))
|
||||||
|
attributes.setGradientTransform(this);
|
||||||
|
if(!attributes.hasSpreadMethod() && hasAttribute(PropertyID::SpreadMethod))
|
||||||
|
attributes.setSpreadMethod(this);
|
||||||
|
if(!attributes.hasGradientUnits() && hasAttribute(PropertyID::GradientUnits))
|
||||||
|
attributes.setGradientUnits(this);
|
||||||
|
if(!attributes.hasGradientContentElement()) {
|
||||||
|
for(const auto& child : children()) {
|
||||||
|
if(auto element = toSVGElement(child); element && element->id() == ElementID::Stop) {
|
||||||
|
attributes.setGradientContentElement(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGLinearGradientElement::SVGLinearGradientElement(Document* document)
|
||||||
|
: SVGGradientElement(document, ElementID::LinearGradient)
|
||||||
|
, m_x1(PropertyID::X1, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::Percent)
|
||||||
|
, m_y1(PropertyID::Y1, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::Percent)
|
||||||
|
, m_x2(PropertyID::X2, LengthDirection::Horizontal, LengthNegativeMode::Allow, 100.f, LengthUnits::Percent)
|
||||||
|
, m_y2(PropertyID::Y2, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::Percent)
|
||||||
|
{
|
||||||
|
addProperty(m_x1);
|
||||||
|
addProperty(m_y1);
|
||||||
|
addProperty(m_x2);
|
||||||
|
addProperty(m_y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGLinearGradientAttributes SVGLinearGradientElement::collectGradientAttributes() const
|
||||||
|
{
|
||||||
|
SVGLinearGradientAttributes attributes;
|
||||||
|
std::set<const SVGGradientElement*> processedGradients;
|
||||||
|
const SVGGradientElement* current = this;
|
||||||
|
while(true) {
|
||||||
|
current->collectGradientAttributes(attributes);
|
||||||
|
if(current->id() == ElementID::LinearGradient) {
|
||||||
|
auto element = static_cast<const SVGLinearGradientElement*>(current);
|
||||||
|
if(!attributes.hasX1() && element->hasAttribute(PropertyID::X1))
|
||||||
|
attributes.setX1(element);
|
||||||
|
if(!attributes.hasY1() && element->hasAttribute(PropertyID::Y1))
|
||||||
|
attributes.setY1(element);
|
||||||
|
if(!attributes.hasX2() && element->hasAttribute(PropertyID::X2))
|
||||||
|
attributes.setX2(element);
|
||||||
|
if(!attributes.hasY2() && element->hasAttribute(PropertyID::Y2)) {
|
||||||
|
attributes.setY2(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto targetElement = current->getTargetElement(document());
|
||||||
|
if(!targetElement || !(targetElement->id() == ElementID::LinearGradient || targetElement->id() == ElementID::RadialGradient))
|
||||||
|
break;
|
||||||
|
processedGradients.insert(current);
|
||||||
|
current = static_cast<const SVGGradientElement*>(targetElement);
|
||||||
|
if(processedGradients.count(current) > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.setDefaultValues(this);
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GradientStops buildGradientStops(const SVGGradientElement* element, float opacity)
|
||||||
|
{
|
||||||
|
GradientStops gradientStops;
|
||||||
|
|
||||||
|
const auto& children = element->children();
|
||||||
|
gradientStops.reserve(children.size());
|
||||||
|
for(const auto& child : children) {
|
||||||
|
auto childElement = toSVGElement(child);
|
||||||
|
if(childElement && childElement->id() == ElementID::Stop) {
|
||||||
|
auto stopElement = static_cast<SVGStopElement*>(childElement);
|
||||||
|
gradientStops.push_back(stopElement->gradientStop(opacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gradientStops;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGLinearGradientElement::applyPaint(SVGRenderState& state, float opacity) const
|
||||||
|
{
|
||||||
|
auto attributes = collectGradientAttributes();
|
||||||
|
auto gradientContentElement = attributes.gradientContentElement();
|
||||||
|
auto gradientStops = buildGradientStops(gradientContentElement, opacity);
|
||||||
|
if(gradientStops.empty())
|
||||||
|
return false;
|
||||||
|
LengthContext lengthContext(this, attributes.gradientUnits());
|
||||||
|
auto x1 = lengthContext.valueForLength(attributes.x1());
|
||||||
|
auto y1 = lengthContext.valueForLength(attributes.y1());
|
||||||
|
auto x2 = lengthContext.valueForLength(attributes.x2());
|
||||||
|
auto y2 = lengthContext.valueForLength(attributes.y2());
|
||||||
|
if(gradientStops.size() == 1 || (x1 == x2 && y1 == y2)) {
|
||||||
|
const auto& lastStop = gradientStops.back();
|
||||||
|
state->setColor(lastStop.color.r, lastStop.color.g, lastStop.color.b, lastStop.color.a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spreadMethod = attributes.spreadMethod();
|
||||||
|
auto gradientUnits = attributes.gradientUnits();
|
||||||
|
auto gradientTransform = attributes.gradientTransform();
|
||||||
|
if(gradientUnits == Units::ObjectBoundingBox) {
|
||||||
|
auto bbox = state.fillBoundingBox();
|
||||||
|
gradientTransform.postMultiply(Transform(bbox.w, 0, 0, bbox.h, bbox.x, bbox.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
state->setLinearGradient(x1, y1, x2, y2, spreadMethod, gradientStops, gradientTransform);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGRadialGradientElement::SVGRadialGradientElement(Document* document)
|
||||||
|
: SVGGradientElement(document, ElementID::RadialGradient)
|
||||||
|
, m_cx(PropertyID::Cx, LengthDirection::Horizontal, LengthNegativeMode::Allow, 50.f, LengthUnits::Percent)
|
||||||
|
, m_cy(PropertyID::Cy, LengthDirection::Vertical, LengthNegativeMode::Allow, 50.f, LengthUnits::Percent)
|
||||||
|
, m_r(PropertyID::R, LengthDirection::Diagonal, LengthNegativeMode::Forbid, 50.f, LengthUnits::Percent)
|
||||||
|
, m_fx(PropertyID::Fx, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::None)
|
||||||
|
, m_fy(PropertyID::Fy, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::None)
|
||||||
|
{
|
||||||
|
addProperty(m_cx);
|
||||||
|
addProperty(m_cy);
|
||||||
|
addProperty(m_r);
|
||||||
|
addProperty(m_fx);
|
||||||
|
addProperty(m_fy);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGRadialGradientElement::applyPaint(SVGRenderState& state, float opacity) const
|
||||||
|
{
|
||||||
|
auto attributes = collectGradientAttributes();
|
||||||
|
auto gradientContentElement = attributes.gradientContentElement();
|
||||||
|
auto gradientStops = buildGradientStops(gradientContentElement, opacity);
|
||||||
|
if(gradientStops.empty())
|
||||||
|
return false;
|
||||||
|
LengthContext lengthContext(this, attributes.gradientUnits());
|
||||||
|
auto r = lengthContext.valueForLength(attributes.r());
|
||||||
|
if(r == 0.f || gradientStops.size() == 1) {
|
||||||
|
const auto& lastStop = gradientStops.back();
|
||||||
|
state->setColor(lastStop.color.r, lastStop.color.g, lastStop.color.b, lastStop.color.a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fx = lengthContext.valueForLength(attributes.fx());
|
||||||
|
auto fy = lengthContext.valueForLength(attributes.fy());
|
||||||
|
auto cx = lengthContext.valueForLength(attributes.cx());
|
||||||
|
auto cy = lengthContext.valueForLength(attributes.cy());
|
||||||
|
|
||||||
|
auto spreadMethod = attributes.spreadMethod();
|
||||||
|
auto gradientUnits = attributes.gradientUnits();
|
||||||
|
auto gradientTransform = attributes.gradientTransform();
|
||||||
|
if(gradientUnits == Units::ObjectBoundingBox) {
|
||||||
|
auto bbox = state.fillBoundingBox();
|
||||||
|
gradientTransform.postMultiply(Transform(bbox.w, 0, 0, bbox.h, bbox.x, bbox.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
state->setRadialGradient(cx, cy, r, fx, fy, spreadMethod, gradientStops, gradientTransform);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGRadialGradientAttributes SVGRadialGradientElement::collectGradientAttributes() const
|
||||||
|
{
|
||||||
|
SVGRadialGradientAttributes attributes;
|
||||||
|
std::set<const SVGGradientElement*> processedGradients;
|
||||||
|
const SVGGradientElement* current = this;
|
||||||
|
while(true) {
|
||||||
|
current->collectGradientAttributes(attributes);
|
||||||
|
if(current->id() == ElementID::RadialGradient) {
|
||||||
|
auto element = static_cast<const SVGRadialGradientElement*>(current);
|
||||||
|
if(!attributes.hasCx() && element->hasAttribute(PropertyID::Cx))
|
||||||
|
attributes.setCx(element);
|
||||||
|
if(!attributes.hasCy() && element->hasAttribute(PropertyID::Cy))
|
||||||
|
attributes.setCy(element);
|
||||||
|
if(!attributes.hasR() && element->hasAttribute(PropertyID::R))
|
||||||
|
attributes.setR(element);
|
||||||
|
if(!attributes.hasFx() && element->hasAttribute(PropertyID::Fx))
|
||||||
|
attributes.setFx(element);
|
||||||
|
if(!attributes.hasFy() && element->hasAttribute(PropertyID::Fy)) {
|
||||||
|
attributes.setFy(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto targetElement = current->getTargetElement(document());
|
||||||
|
if(!targetElement || !(targetElement->id() == ElementID::LinearGradient || targetElement->id() == ElementID::RadialGradient))
|
||||||
|
break;
|
||||||
|
processedGradients.insert(current);
|
||||||
|
current = static_cast<const SVGGradientElement*>(targetElement);
|
||||||
|
if(processedGradients.count(current) > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.setDefaultValues(this);
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGPatternElement::SVGPatternElement(Document* document)
|
||||||
|
: SVGPaintElement(document, ElementID::Pattern)
|
||||||
|
, SVGURIReference(this)
|
||||||
|
, SVGFitToViewBox(this)
|
||||||
|
, m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid)
|
||||||
|
, m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid)
|
||||||
|
, m_patternTransform(PropertyID::PatternTransform)
|
||||||
|
, m_patternUnits(PropertyID::PatternUnits, Units::ObjectBoundingBox)
|
||||||
|
, m_patternContentUnits(PropertyID::PatternContentUnits, Units::UserSpaceOnUse)
|
||||||
|
{
|
||||||
|
addProperty(m_x);
|
||||||
|
addProperty(m_y);
|
||||||
|
addProperty(m_width);
|
||||||
|
addProperty(m_height);
|
||||||
|
addProperty(m_patternTransform);
|
||||||
|
addProperty(m_patternUnits);
|
||||||
|
addProperty(m_patternContentUnits);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGPatternElement::applyPaint(SVGRenderState& state, float opacity) const
|
||||||
|
{
|
||||||
|
if(state.hasCycleReference(this))
|
||||||
|
return false;
|
||||||
|
auto attributes = collectPatternAttributes();
|
||||||
|
auto patternContentElement = attributes.patternContentElement();
|
||||||
|
if(patternContentElement == nullptr)
|
||||||
|
return false;
|
||||||
|
LengthContext lengthContext(this, attributes.patternUnits());
|
||||||
|
Rect patternRect = {
|
||||||
|
lengthContext.valueForLength(attributes.x()),
|
||||||
|
lengthContext.valueForLength(attributes.y()),
|
||||||
|
lengthContext.valueForLength(attributes.width()),
|
||||||
|
lengthContext.valueForLength(attributes.height())
|
||||||
|
};
|
||||||
|
|
||||||
|
if(attributes.patternUnits() == Units::ObjectBoundingBox) {
|
||||||
|
auto bbox = state.fillBoundingBox();
|
||||||
|
patternRect.x = patternRect.x * bbox.w + bbox.x;
|
||||||
|
patternRect.y = patternRect.y * bbox.h + bbox.y;
|
||||||
|
patternRect.w = patternRect.w * bbox.w;
|
||||||
|
patternRect.h = patternRect.h * bbox.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentTransform = attributes.patternTransform() * state.currentTransform();
|
||||||
|
auto xScale = currentTransform.xScale();
|
||||||
|
auto yScale = currentTransform.yScale();
|
||||||
|
|
||||||
|
auto patternImage = Canvas::create(0, 0, patternRect.w * xScale, patternRect.h * yScale);
|
||||||
|
auto patternImageTransform = Transform::scaled(xScale, yScale);
|
||||||
|
|
||||||
|
const auto& viewBoxRect = attributes.viewBox();
|
||||||
|
if(viewBoxRect.isValid()) {
|
||||||
|
const auto& preserveAspectRatio = attributes.preserveAspectRatio();
|
||||||
|
patternImageTransform.multiply(preserveAspectRatio.getTransform(viewBoxRect, patternRect.size()));
|
||||||
|
} else if(attributes.patternContentUnits() == Units::ObjectBoundingBox) {
|
||||||
|
auto bbox = state.fillBoundingBox();
|
||||||
|
patternImageTransform.scale(bbox.w, bbox.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGRenderState newState(this, &state, patternImageTransform, SVGRenderMode::Painting, patternImage);
|
||||||
|
patternContentElement->renderChildren(newState);
|
||||||
|
|
||||||
|
auto patternTransform = attributes.patternTransform();
|
||||||
|
patternTransform.translate(patternRect.x, patternRect.y);
|
||||||
|
patternTransform.scale(patternRect.w / patternImage->width(), patternRect.h / patternImage->height());
|
||||||
|
state->setTexture(*patternImage, TextureType::Tiled, opacity, patternTransform);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGPatternAttributes SVGPatternElement::collectPatternAttributes() const
|
||||||
|
{
|
||||||
|
SVGPatternAttributes attributes;
|
||||||
|
std::set<const SVGPatternElement*> processedPatterns;
|
||||||
|
const SVGPatternElement* current = this;
|
||||||
|
while(true) {
|
||||||
|
if(!attributes.hasX() && current->hasAttribute(PropertyID::X))
|
||||||
|
attributes.setX(current);
|
||||||
|
if(!attributes.hasY() && current->hasAttribute(PropertyID::Y))
|
||||||
|
attributes.setY(current);
|
||||||
|
if(!attributes.hasWidth() && current->hasAttribute(PropertyID::Width))
|
||||||
|
attributes.setWidth(current);
|
||||||
|
if(!attributes.hasHeight() && current->hasAttribute(PropertyID::Height))
|
||||||
|
attributes.setHeight(current);
|
||||||
|
if(!attributes.hasPatternTransform() && current->hasAttribute(PropertyID::PatternTransform))
|
||||||
|
attributes.setPatternTransform(current);
|
||||||
|
if(!attributes.hasPatternUnits() && current->hasAttribute(PropertyID::PatternUnits))
|
||||||
|
attributes.setPatternUnits(current);
|
||||||
|
if(!attributes.hasPatternContentUnits() && current->hasAttribute(PropertyID::PatternContentUnits))
|
||||||
|
attributes.setPatternContentUnits(current);
|
||||||
|
if(!attributes.hasViewBox() && current->hasAttribute(PropertyID::ViewBox))
|
||||||
|
attributes.setViewBox(current);
|
||||||
|
if(!attributes.hasPreserveAspectRatio() && current->hasAttribute(PropertyID::PreserveAspectRatio))
|
||||||
|
attributes.setPreserveAspectRatio(current);
|
||||||
|
if(!attributes.hasPatternContentElement()) {
|
||||||
|
for(const auto& child : current->children()) {
|
||||||
|
if(child->isElement()) {
|
||||||
|
attributes.setPatternContentElement(current);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto targetElement = current->getTargetElement(document());
|
||||||
|
if(!targetElement || targetElement->id() != ElementID::Pattern)
|
||||||
|
break;
|
||||||
|
processedPatterns.insert(current);
|
||||||
|
current = static_cast<const SVGPatternElement*>(targetElement);
|
||||||
|
if(processedPatterns.count(current) > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes.setDefaultValues(this);
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
288
vendor/lunasvg/source/svgpaintelement.h
vendored
Normal file
288
vendor/lunasvg/source/svgpaintelement.h
vendored
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
#ifndef LUNASVG_SVGPAINTELEMENT_H
|
||||||
|
#define LUNASVG_SVGPAINTELEMENT_H
|
||||||
|
|
||||||
|
#include "svgelement.h"
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
class SVGPaintElement : public SVGElement {
|
||||||
|
public:
|
||||||
|
SVGPaintElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
bool isPaintElement() const final { return true; }
|
||||||
|
|
||||||
|
virtual bool applyPaint(SVGRenderState& state, float opacity) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGStopElement final : public SVGElement {
|
||||||
|
public:
|
||||||
|
SVGStopElement(Document* document);
|
||||||
|
|
||||||
|
void layoutElement(const SVGLayoutState& state) final;
|
||||||
|
const SVGNumberPercentage& offset() const { return m_offset; }
|
||||||
|
GradientStop gradientStop(float opacity) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGNumberPercentage m_offset;
|
||||||
|
Color m_stop_color = Color::Black;
|
||||||
|
float m_stop_opacity = 1.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGGradientAttributes;
|
||||||
|
|
||||||
|
class SVGGradientElement : public SVGPaintElement, public SVGURIReference {
|
||||||
|
public:
|
||||||
|
SVGGradientElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
const SVGTransform& gradientTransform() const { return m_gradientTransform; }
|
||||||
|
const SVGEnumeration<Units>& gradientUnits() const { return m_gradientUnits; }
|
||||||
|
const SVGEnumeration<SpreadMethod>& spreadMethod() const { return m_spreadMethod; }
|
||||||
|
void collectGradientAttributes(SVGGradientAttributes& attributes) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGTransform m_gradientTransform;
|
||||||
|
SVGEnumeration<Units> m_gradientUnits;
|
||||||
|
SVGEnumeration<SpreadMethod> m_spreadMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGGradientAttributes {
|
||||||
|
public:
|
||||||
|
SVGGradientAttributes() = default;
|
||||||
|
|
||||||
|
const Transform& gradientTransform() const { return m_gradientTransform->gradientTransform().value(); }
|
||||||
|
SpreadMethod spreadMethod() const { return m_spreadMethod->spreadMethod().value(); }
|
||||||
|
Units gradientUnits() const { return m_gradientUnits->gradientUnits().value(); }
|
||||||
|
const SVGGradientElement* gradientContentElement() const { return m_gradientContentElement; }
|
||||||
|
|
||||||
|
bool hasGradientTransform() const { return m_gradientTransform; }
|
||||||
|
bool hasSpreadMethod() const { return m_spreadMethod; }
|
||||||
|
bool hasGradientUnits() const { return m_gradientUnits; }
|
||||||
|
bool hasGradientContentElement() const { return m_gradientContentElement; }
|
||||||
|
|
||||||
|
void setGradientTransform(const SVGGradientElement* value) { m_gradientTransform = value; }
|
||||||
|
void setSpreadMethod(const SVGGradientElement* value) { m_spreadMethod = value; }
|
||||||
|
void setGradientUnits(const SVGGradientElement* value) { m_gradientUnits = value; }
|
||||||
|
void setGradientContentElement(const SVGGradientElement* value) { m_gradientContentElement = value; }
|
||||||
|
|
||||||
|
void setDefaultValues(const SVGGradientElement* element) {
|
||||||
|
if(!m_gradientTransform) { m_gradientTransform = element; }
|
||||||
|
if(!m_spreadMethod) { m_spreadMethod = element; }
|
||||||
|
if(!m_gradientUnits) { m_gradientUnits = element; }
|
||||||
|
if(!m_gradientContentElement) { m_gradientContentElement = element; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGGradientElement* m_gradientTransform{nullptr};
|
||||||
|
const SVGGradientElement* m_spreadMethod{nullptr};
|
||||||
|
const SVGGradientElement* m_gradientUnits{nullptr};
|
||||||
|
const SVGGradientElement* m_gradientContentElement{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGLinearGradientAttributes;
|
||||||
|
|
||||||
|
class SVGLinearGradientElement final : public SVGGradientElement {
|
||||||
|
public:
|
||||||
|
SVGLinearGradientElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x1() const { return m_x1; }
|
||||||
|
const SVGLength& y1() const { return m_y1; }
|
||||||
|
const SVGLength& x2() const { return m_x2; }
|
||||||
|
const SVGLength& y2() const { return m_y2; }
|
||||||
|
|
||||||
|
bool applyPaint(SVGRenderState& state, float opacity) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGLinearGradientAttributes collectGradientAttributes() const;
|
||||||
|
SVGLength m_x1;
|
||||||
|
SVGLength m_y1;
|
||||||
|
SVGLength m_x2;
|
||||||
|
SVGLength m_y2;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGLinearGradientAttributes : public SVGGradientAttributes {
|
||||||
|
public:
|
||||||
|
SVGLinearGradientAttributes() = default;
|
||||||
|
|
||||||
|
const SVGLength& x1() const { return m_x1->x1(); }
|
||||||
|
const SVGLength& y1() const { return m_y1->y1(); }
|
||||||
|
const SVGLength& x2() const { return m_x2->x2(); }
|
||||||
|
const SVGLength& y2() const { return m_y2->y2(); }
|
||||||
|
|
||||||
|
bool hasX1() const { return m_x1; }
|
||||||
|
bool hasY1() const { return m_y1; }
|
||||||
|
bool hasX2() const { return m_x2; }
|
||||||
|
bool hasY2() const { return m_y2; }
|
||||||
|
|
||||||
|
void setX1(const SVGLinearGradientElement* value) { m_x1 = value; }
|
||||||
|
void setY1(const SVGLinearGradientElement* value) { m_y1 = value; }
|
||||||
|
void setX2(const SVGLinearGradientElement* value) { m_x2 = value; }
|
||||||
|
void setY2(const SVGLinearGradientElement* value) { m_y2 = value; }
|
||||||
|
|
||||||
|
void setDefaultValues(const SVGLinearGradientElement* element) {
|
||||||
|
SVGGradientAttributes::setDefaultValues(element);
|
||||||
|
if(!m_x1) { m_x1 = element; }
|
||||||
|
if(!m_y1) { m_y1 = element; }
|
||||||
|
if(!m_x2) { m_x2 = element; }
|
||||||
|
if(!m_y2) { m_y2 = element; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGLinearGradientElement* m_x1{nullptr};
|
||||||
|
const SVGLinearGradientElement* m_y1{nullptr};
|
||||||
|
const SVGLinearGradientElement* m_x2{nullptr};
|
||||||
|
const SVGLinearGradientElement* m_y2{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRadialGradientAttributes;
|
||||||
|
|
||||||
|
class SVGRadialGradientElement final : public SVGGradientElement {
|
||||||
|
public:
|
||||||
|
SVGRadialGradientElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& cx() const { return m_cx; }
|
||||||
|
const SVGLength& cy() const { return m_cy; }
|
||||||
|
const SVGLength& r() const { return m_r; }
|
||||||
|
const SVGLength& fx() const { return m_fx; }
|
||||||
|
const SVGLength& fy() const { return m_fy; }
|
||||||
|
|
||||||
|
bool applyPaint(SVGRenderState& state, float opacity) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGRadialGradientAttributes collectGradientAttributes() const;
|
||||||
|
SVGLength m_cx;
|
||||||
|
SVGLength m_cy;
|
||||||
|
SVGLength m_r;
|
||||||
|
SVGLength m_fx;
|
||||||
|
SVGLength m_fy;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRadialGradientAttributes : public SVGGradientAttributes {
|
||||||
|
public:
|
||||||
|
SVGRadialGradientAttributes() = default;
|
||||||
|
|
||||||
|
const SVGLength& cx() const { return m_cx->cx(); }
|
||||||
|
const SVGLength& cy() const { return m_cy->cy(); }
|
||||||
|
const SVGLength& r() const { return m_r->r(); }
|
||||||
|
const SVGLength& fx() const { return m_fx ? m_fx->fx() : m_cx->cx(); }
|
||||||
|
const SVGLength& fy() const { return m_fy ? m_fy->fy() : m_cy->cy(); }
|
||||||
|
|
||||||
|
bool hasCx() const { return m_cx; }
|
||||||
|
bool hasCy() const { return m_cy; }
|
||||||
|
bool hasR() const { return m_r; }
|
||||||
|
bool hasFx() const { return m_fx; }
|
||||||
|
bool hasFy() const { return m_fy; }
|
||||||
|
|
||||||
|
void setCx(const SVGRadialGradientElement* value) { m_cx = value; }
|
||||||
|
void setCy(const SVGRadialGradientElement* value) { m_cy = value; }
|
||||||
|
void setR(const SVGRadialGradientElement* value) { m_r = value; }
|
||||||
|
void setFx(const SVGRadialGradientElement* value) { m_fx = value; }
|
||||||
|
void setFy(const SVGRadialGradientElement* value) { m_fy = value; }
|
||||||
|
|
||||||
|
void setDefaultValues(const SVGRadialGradientElement* element) {
|
||||||
|
SVGGradientAttributes::setDefaultValues(element);
|
||||||
|
if(!m_cx) { m_cx = element; }
|
||||||
|
if(!m_cy) { m_cy = element; }
|
||||||
|
if(!m_r) { m_r = element; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGRadialGradientElement* m_cx{nullptr};
|
||||||
|
const SVGRadialGradientElement* m_cy{nullptr};
|
||||||
|
const SVGRadialGradientElement* m_r{nullptr};
|
||||||
|
const SVGRadialGradientElement* m_fx{nullptr};
|
||||||
|
const SVGRadialGradientElement* m_fy{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPatternAttributes;
|
||||||
|
|
||||||
|
class SVGPatternElement final : public SVGPaintElement, public SVGURIReference, public SVGFitToViewBox {
|
||||||
|
public:
|
||||||
|
SVGPatternElement(Document* document);
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x; }
|
||||||
|
const SVGLength& y() const { return m_y; }
|
||||||
|
const SVGLength& width() const { return m_width; }
|
||||||
|
const SVGLength& height() const { return m_height; }
|
||||||
|
const SVGTransform& patternTransform() const { return m_patternTransform; }
|
||||||
|
const SVGEnumeration<Units>& patternUnits() const { return m_patternUnits; }
|
||||||
|
const SVGEnumeration<Units>& patternContentUnits() const { return m_patternContentUnits; }
|
||||||
|
|
||||||
|
bool applyPaint(SVGRenderState& state, float opacity) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGPatternAttributes collectPatternAttributes() const;
|
||||||
|
SVGLength m_x;
|
||||||
|
SVGLength m_y;
|
||||||
|
SVGLength m_width;
|
||||||
|
SVGLength m_height;
|
||||||
|
SVGTransform m_patternTransform;
|
||||||
|
SVGEnumeration<Units> m_patternUnits;
|
||||||
|
SVGEnumeration<Units> m_patternContentUnits;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPatternAttributes {
|
||||||
|
public:
|
||||||
|
SVGPatternAttributes() = default;
|
||||||
|
|
||||||
|
const SVGLength& x() const { return m_x->x(); }
|
||||||
|
const SVGLength& y() const { return m_y->y(); }
|
||||||
|
const SVGLength& width() const { return m_width->width(); }
|
||||||
|
const SVGLength& height() const { return m_height->height(); }
|
||||||
|
const Transform& patternTransform() const { return m_patternTransform->patternTransform().value(); }
|
||||||
|
Units patternUnits() const { return m_patternUnits->patternUnits().value(); }
|
||||||
|
Units patternContentUnits() const { return m_patternContentUnits->patternContentUnits().value(); }
|
||||||
|
const Rect& viewBox() const { return m_viewBox->viewBox().value(); }
|
||||||
|
const SVGPreserveAspectRatio& preserveAspectRatio() const { return m_preserveAspectRatio->preserveAspectRatio(); }
|
||||||
|
const SVGPatternElement* patternContentElement() const { return m_patternContentElement; }
|
||||||
|
|
||||||
|
bool hasX() const { return m_x; }
|
||||||
|
bool hasY() const { return m_y; }
|
||||||
|
bool hasWidth() const { return m_width; }
|
||||||
|
bool hasHeight() const { return m_height; }
|
||||||
|
bool hasPatternTransform() const { return m_patternTransform; }
|
||||||
|
bool hasPatternUnits() const { return m_patternUnits; }
|
||||||
|
bool hasPatternContentUnits() const { return m_patternContentUnits; }
|
||||||
|
bool hasViewBox() const { return m_viewBox; }
|
||||||
|
bool hasPreserveAspectRatio() const { return m_preserveAspectRatio; }
|
||||||
|
bool hasPatternContentElement() const { return m_patternContentElement; }
|
||||||
|
|
||||||
|
void setX(const SVGPatternElement* value) { m_x = value; }
|
||||||
|
void setY(const SVGPatternElement* value) { m_y = value; }
|
||||||
|
void setWidth(const SVGPatternElement* value) { m_width = value; }
|
||||||
|
void setHeight(const SVGPatternElement* value) { m_height = value; }
|
||||||
|
void setPatternTransform(const SVGPatternElement* value) { m_patternTransform = value; }
|
||||||
|
void setPatternUnits(const SVGPatternElement* value) { m_patternUnits = value; }
|
||||||
|
void setPatternContentUnits(const SVGPatternElement* value) { m_patternContentUnits = value; }
|
||||||
|
void setViewBox(const SVGPatternElement* value) { m_viewBox = value; }
|
||||||
|
void setPreserveAspectRatio(const SVGPatternElement* value) { m_preserveAspectRatio = value; }
|
||||||
|
void setPatternContentElement(const SVGPatternElement* value) { m_patternContentElement = value; }
|
||||||
|
|
||||||
|
void setDefaultValues(const SVGPatternElement* element) {
|
||||||
|
if(!m_x) { m_x = element; }
|
||||||
|
if(!m_y) { m_y = element; }
|
||||||
|
if(!m_width) { m_width = element; }
|
||||||
|
if(!m_height) { m_height = element; }
|
||||||
|
if(!m_patternTransform) { m_patternTransform = element; }
|
||||||
|
if(!m_patternUnits) { m_patternUnits = element; }
|
||||||
|
if(!m_patternContentUnits) { m_patternContentUnits = element; }
|
||||||
|
if(!m_viewBox) { m_viewBox = element; }
|
||||||
|
if(!m_preserveAspectRatio) { m_preserveAspectRatio = element; }
|
||||||
|
if(!m_patternContentElement) { m_patternContentElement = element; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGPatternElement* m_x{nullptr};
|
||||||
|
const SVGPatternElement* m_y{nullptr};
|
||||||
|
const SVGPatternElement* m_width{nullptr};
|
||||||
|
const SVGPatternElement* m_height{nullptr};
|
||||||
|
const SVGPatternElement* m_patternTransform{nullptr};
|
||||||
|
const SVGPatternElement* m_patternUnits{nullptr};
|
||||||
|
const SVGPatternElement* m_patternContentUnits{nullptr};
|
||||||
|
const SVGPatternElement* m_viewBox{nullptr};
|
||||||
|
const SVGPatternElement* m_preserveAspectRatio{nullptr};
|
||||||
|
const SVGPatternElement* m_patternContentElement{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGPAINTELEMENT_H
|
||||||
956
vendor/lunasvg/source/svgparser.cpp
vendored
Normal file
956
vendor/lunasvg/source/svgparser.cpp
vendored
Normal file
@@ -0,0 +1,956 @@
|
|||||||
|
#include "lunasvg.h"
|
||||||
|
#include "svgelement.h"
|
||||||
|
#include "svgparserutils.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
struct SimpleSelector;
|
||||||
|
|
||||||
|
using Selector = std::vector<SimpleSelector>;
|
||||||
|
using SelectorList = std::vector<Selector>;
|
||||||
|
|
||||||
|
struct AttributeSelector {
|
||||||
|
enum class MatchType {
|
||||||
|
None,
|
||||||
|
Equals,
|
||||||
|
Contains,
|
||||||
|
Includes,
|
||||||
|
StartsWith,
|
||||||
|
EndsWith,
|
||||||
|
DashEquals
|
||||||
|
};
|
||||||
|
|
||||||
|
MatchType matchType{MatchType::None};
|
||||||
|
PropertyID id{PropertyID::Unknown};
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PseudoClassSelector {
|
||||||
|
enum class Type {
|
||||||
|
Unknown,
|
||||||
|
Empty,
|
||||||
|
Root,
|
||||||
|
Is,
|
||||||
|
Not,
|
||||||
|
FirstChild,
|
||||||
|
LastChild,
|
||||||
|
OnlyChild,
|
||||||
|
FirstOfType,
|
||||||
|
LastOfType,
|
||||||
|
OnlyOfType
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type{Type::Unknown};
|
||||||
|
SelectorList subSelectors;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SimpleSelector {
|
||||||
|
enum class Combinator {
|
||||||
|
None,
|
||||||
|
Descendant,
|
||||||
|
Child,
|
||||||
|
DirectAdjacent,
|
||||||
|
InDirectAdjacent
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit SimpleSelector(Combinator combinator) : combinator(combinator) {}
|
||||||
|
|
||||||
|
Combinator combinator{Combinator::Descendant};
|
||||||
|
ElementID id{ElementID::Star};
|
||||||
|
std::vector<AttributeSelector> attributeSelectors;
|
||||||
|
std::vector<PseudoClassSelector> pseudoClassSelectors;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Declaration {
|
||||||
|
int specificity;
|
||||||
|
PropertyID id;
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using DeclarationList = std::vector<Declaration>;
|
||||||
|
|
||||||
|
struct Rule {
|
||||||
|
SelectorList selectors;
|
||||||
|
DeclarationList declarations;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RuleData {
|
||||||
|
public:
|
||||||
|
RuleData(const Selector& selector, const DeclarationList& declarations, size_t specificity, size_t position)
|
||||||
|
: m_selector(selector), m_declarations(declarations), m_specificity(specificity), m_position(position)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isLessThan(const RuleData& rule) const { return std::tie(m_specificity, m_position) < std::tie(rule.m_specificity, rule.m_position); }
|
||||||
|
|
||||||
|
const Selector& selector() const { return m_selector; }
|
||||||
|
const DeclarationList& declarations() const { return m_declarations; }
|
||||||
|
size_t specificity() const { return m_specificity; }
|
||||||
|
size_t position() const { return m_position; }
|
||||||
|
|
||||||
|
bool match(const SVGElement* element) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Selector m_selector;
|
||||||
|
DeclarationList m_declarations;
|
||||||
|
size_t m_specificity;
|
||||||
|
size_t m_position;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator<(const RuleData& a, const RuleData& b) { return a.isLessThan(b); }
|
||||||
|
|
||||||
|
using RuleDataList = std::vector<RuleData>;
|
||||||
|
|
||||||
|
constexpr bool equals(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
return value.compare(subvalue) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool contains(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
return value.find(subvalue) != std::string_view::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool includes(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
if(subvalue.empty() || subvalue.length() > value.length())
|
||||||
|
return false;
|
||||||
|
std::string_view input(value);
|
||||||
|
while(!input.empty()) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
std::string_view start(input);
|
||||||
|
while(!input.empty() && !IS_WS(input.front()))
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(subvalue == start.substr(0, start.length() - input.length())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool startswith(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
if(subvalue.empty() || subvalue.length() > value.length())
|
||||||
|
return false;
|
||||||
|
return subvalue == value.substr(0, subvalue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool endswith(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
if(subvalue.empty() || subvalue.length() > value.length())
|
||||||
|
return false;
|
||||||
|
return subvalue == value.substr(value.size() - subvalue.size(), subvalue.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool dashequals(std::string_view value, std::string_view subvalue)
|
||||||
|
{
|
||||||
|
if(startswith(value, subvalue))
|
||||||
|
return (value.length() == subvalue.length() || value.at(subvalue.length()) == '-');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool matchAttributeSelector(const AttributeSelector& selector, const SVGElement* element)
|
||||||
|
{
|
||||||
|
const auto& value = element->getAttribute(selector.id);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::None)
|
||||||
|
return !value.empty();
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::Equals)
|
||||||
|
return equals(value, selector.value);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::Contains)
|
||||||
|
return contains(value, selector.value);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::Includes)
|
||||||
|
return includes(value, selector.value);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::StartsWith)
|
||||||
|
return startswith(value, selector.value);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::EndsWith)
|
||||||
|
return endswith(value, selector.value);
|
||||||
|
if(selector.matchType == AttributeSelector::MatchType::DashEquals)
|
||||||
|
return dashequals(value, selector.value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool matchSimpleSelector(const SimpleSelector& selector, const SVGElement* element);
|
||||||
|
|
||||||
|
static bool matchPseudoClassSelector(const PseudoClassSelector& selector, const SVGElement* element)
|
||||||
|
{
|
||||||
|
if(selector.type == PseudoClassSelector::Type::Empty)
|
||||||
|
return element->children().empty();
|
||||||
|
if(selector.type == PseudoClassSelector::Type::Root)
|
||||||
|
return element->isRootElement();
|
||||||
|
if(selector.type == PseudoClassSelector::Type::Is) {
|
||||||
|
for(const auto& subSelector : selector.subSelectors) {
|
||||||
|
for(const auto& simpleSelector : subSelector) {
|
||||||
|
if(!matchSimpleSelector(simpleSelector, element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selector.type == PseudoClassSelector::Type::Not) {
|
||||||
|
for(const auto& subSelector : selector.subSelectors) {
|
||||||
|
for(const auto& simpleSelector : subSelector) {
|
||||||
|
if(matchSimpleSelector(simpleSelector, element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selector.type == PseudoClassSelector::Type::FirstChild)
|
||||||
|
return !element->previousElement();
|
||||||
|
if(selector.type == PseudoClassSelector::Type::LastChild)
|
||||||
|
return !element->nextElement();
|
||||||
|
if(selector.type == PseudoClassSelector::Type::OnlyChild)
|
||||||
|
return !(element->previousElement() || element->nextElement());
|
||||||
|
if(selector.type == PseudoClassSelector::Type::FirstOfType) {
|
||||||
|
auto sibling = element->previousElement();
|
||||||
|
while(sibling) {
|
||||||
|
if(sibling->id() == element->id())
|
||||||
|
return false;
|
||||||
|
sibling = sibling->previousElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selector.type == PseudoClassSelector::Type::LastOfType) {
|
||||||
|
auto sibling = element->nextElement();
|
||||||
|
while(sibling) {
|
||||||
|
if(sibling->id() == element->id())
|
||||||
|
return false;
|
||||||
|
sibling = sibling->nextElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool matchSimpleSelector(const SimpleSelector& selector, const SVGElement* element)
|
||||||
|
{
|
||||||
|
if(selector.id != ElementID::Star && selector.id != element->id())
|
||||||
|
return false;
|
||||||
|
for(const auto& sel : selector.attributeSelectors) {
|
||||||
|
if(!matchAttributeSelector(sel, element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto& sel : selector.pseudoClassSelectors) {
|
||||||
|
if(!matchPseudoClassSelector(sel, element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool matchSelector(const Selector& selector, const SVGElement* element)
|
||||||
|
{
|
||||||
|
if(selector.empty())
|
||||||
|
return false;
|
||||||
|
auto it = selector.rbegin();
|
||||||
|
auto end = selector.rend();
|
||||||
|
if(!matchSimpleSelector(*it, element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto combinator = it->combinator;
|
||||||
|
++it;
|
||||||
|
|
||||||
|
while(it != end) {
|
||||||
|
switch(combinator) {
|
||||||
|
case SimpleSelector::Combinator::Child:
|
||||||
|
case SimpleSelector::Combinator::Descendant:
|
||||||
|
element = element->parentElement();
|
||||||
|
break;
|
||||||
|
case SimpleSelector::Combinator::DirectAdjacent:
|
||||||
|
case SimpleSelector::Combinator::InDirectAdjacent:
|
||||||
|
element = element->previousElement();
|
||||||
|
break;
|
||||||
|
case SimpleSelector::Combinator::None:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(element == nullptr)
|
||||||
|
return false;
|
||||||
|
if(matchSimpleSelector(*it, element)) {
|
||||||
|
combinator = it->combinator;
|
||||||
|
++it;
|
||||||
|
} else if(combinator != SimpleSelector::Combinator::Descendant
|
||||||
|
&& combinator != SimpleSelector::Combinator::InDirectAdjacent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RuleData::match(const SVGElement* element) const
|
||||||
|
{
|
||||||
|
return matchSelector(m_selector, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IS_CSS_STARTNAMECHAR(int c) { return IS_ALPHA(c) || c == '_' || c == '-'; }
|
||||||
|
constexpr bool IS_CSS_NAMECHAR(int c) { return IS_CSS_STARTNAMECHAR(c) || IS_NUM(c); }
|
||||||
|
|
||||||
|
inline bool readCSSIdentifier(std::string_view& input, std::string& output)
|
||||||
|
{
|
||||||
|
if(input.empty() || !IS_CSS_STARTNAMECHAR(input.front()))
|
||||||
|
return false;
|
||||||
|
output.clear();
|
||||||
|
do {
|
||||||
|
output.push_back(input.front());
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && IS_CSS_NAMECHAR(input.front()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseTagSelector(std::string_view& input, SimpleSelector& simpleSelector)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
if(skipDelimiter(input, '*'))
|
||||||
|
simpleSelector.id = ElementID::Star;
|
||||||
|
else if(readCSSIdentifier(input, name))
|
||||||
|
simpleSelector.id = elementid(name);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseIdSelector(std::string_view& input, SimpleSelector& simpleSelector)
|
||||||
|
{
|
||||||
|
AttributeSelector a;
|
||||||
|
a.id = PropertyID::Id;
|
||||||
|
a.matchType = AttributeSelector::MatchType::Equals;
|
||||||
|
if(!readCSSIdentifier(input, a.value))
|
||||||
|
return false;
|
||||||
|
simpleSelector.attributeSelectors.push_back(std::move(a));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseClassSelector(std::string_view& input, SimpleSelector& simpleSelector)
|
||||||
|
{
|
||||||
|
AttributeSelector a;
|
||||||
|
a.id = PropertyID::Class;
|
||||||
|
a.matchType = AttributeSelector::MatchType::Includes;
|
||||||
|
if(!readCSSIdentifier(input, a.value))
|
||||||
|
return false;
|
||||||
|
simpleSelector.attributeSelectors.push_back(std::move(a));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseAttributeSelector(std::string_view& input, SimpleSelector& simpleSelector)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!readCSSIdentifier(input, name))
|
||||||
|
return false;
|
||||||
|
AttributeSelector a;
|
||||||
|
a.id = propertyid(name);
|
||||||
|
a.matchType = AttributeSelector::MatchType::None;
|
||||||
|
if(skipDelimiter(input, '='))
|
||||||
|
a.matchType = AttributeSelector::MatchType::Equals;
|
||||||
|
else if(skipString(input, "*="))
|
||||||
|
a.matchType = AttributeSelector::MatchType::Contains;
|
||||||
|
else if(skipString(input, "~="))
|
||||||
|
a.matchType = AttributeSelector::MatchType::Includes;
|
||||||
|
else if(skipString(input, "^="))
|
||||||
|
a.matchType = AttributeSelector::MatchType::StartsWith;
|
||||||
|
else if(skipString(input, "$="))
|
||||||
|
a.matchType = AttributeSelector::MatchType::EndsWith;
|
||||||
|
else if(skipString(input, "|="))
|
||||||
|
a.matchType = AttributeSelector::MatchType::DashEquals;
|
||||||
|
if(a.matchType != AttributeSelector::MatchType::None) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!readCSSIdentifier(input, a.value)) {
|
||||||
|
if(input.empty() || !(input.front() == '\"' || input.front() == '\''))
|
||||||
|
return false;
|
||||||
|
auto quote = input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
auto n = input.find(quote);
|
||||||
|
if(n == std::string_view::npos)
|
||||||
|
return false;
|
||||||
|
a.value.assign(input.substr(0, n));
|
||||||
|
input.remove_prefix(n + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, ']'))
|
||||||
|
return false;
|
||||||
|
simpleSelector.attributeSelectors.push_back(std::move(a));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseSelectors(std::string_view& input, SelectorList& selectors);
|
||||||
|
|
||||||
|
static bool parsePseudoClassSelector(std::string_view& input, SimpleSelector& simpleSelector)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
if(!readCSSIdentifier(input, name))
|
||||||
|
return false;
|
||||||
|
PseudoClassSelector selector;
|
||||||
|
if(name.compare("empty") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::Empty;
|
||||||
|
else if(name.compare("root") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::Root;
|
||||||
|
else if(name.compare("not") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::Not;
|
||||||
|
else if(name.compare("first-child") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::FirstChild;
|
||||||
|
else if(name.compare("last-child") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::LastChild;
|
||||||
|
else if(name.compare("only-child") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::OnlyChild;
|
||||||
|
else if(name.compare("first-of-type") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::FirstOfType;
|
||||||
|
else if(name.compare("last-of-type") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::LastOfType;
|
||||||
|
else if(name.compare("only-of-type") == 0)
|
||||||
|
selector.type = PseudoClassSelector::Type::OnlyOfType;
|
||||||
|
if(selector.type == PseudoClassSelector::Type::Is || selector.type == PseudoClassSelector::Type::Not) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, '('))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!parseSelectors(input, selector.subSelectors))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, ')')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleSelector.pseudoClassSelectors.push_back(std::move(selector));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseSimpleSelector(std::string_view& input, SimpleSelector& simpleSelector, bool& failed)
|
||||||
|
{
|
||||||
|
auto consumed = parseTagSelector(input, simpleSelector);
|
||||||
|
do {
|
||||||
|
if(skipDelimiter(input, '#'))
|
||||||
|
failed = !parseIdSelector(input, simpleSelector);
|
||||||
|
else if(skipDelimiter(input, '.'))
|
||||||
|
failed = !parseClassSelector(input, simpleSelector);
|
||||||
|
else if(skipDelimiter(input, '['))
|
||||||
|
failed = !parseAttributeSelector(input, simpleSelector);
|
||||||
|
else if(skipDelimiter(input, ':'))
|
||||||
|
failed = !parsePseudoClassSelector(input, simpleSelector);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
consumed = true;
|
||||||
|
} while(!failed);
|
||||||
|
return consumed && !failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseCombinator(std::string_view& input, SimpleSelector::Combinator& combinator)
|
||||||
|
{
|
||||||
|
combinator = SimpleSelector::Combinator::None;
|
||||||
|
while(!input.empty() && IS_WS(input.front())) {
|
||||||
|
combinator = SimpleSelector::Combinator::Descendant;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiterAndOptionalSpaces(input, '>'))
|
||||||
|
combinator = SimpleSelector::Combinator::Child;
|
||||||
|
else if(skipDelimiterAndOptionalSpaces(input, '+'))
|
||||||
|
combinator = SimpleSelector::Combinator::DirectAdjacent;
|
||||||
|
else if(skipDelimiterAndOptionalSpaces(input, '~'))
|
||||||
|
combinator = SimpleSelector::Combinator::InDirectAdjacent;
|
||||||
|
return combinator != SimpleSelector::Combinator::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseSelector(std::string_view& input, Selector& selector)
|
||||||
|
{
|
||||||
|
auto combinator = SimpleSelector::Combinator::None;
|
||||||
|
do {
|
||||||
|
bool failed = false;
|
||||||
|
SimpleSelector simpleSelector(combinator);
|
||||||
|
if(!parseSimpleSelector(input, simpleSelector, failed))
|
||||||
|
return !failed && (combinator == SimpleSelector::Combinator::Descendant);
|
||||||
|
selector.push_back(std::move(simpleSelector));
|
||||||
|
} while(parseCombinator(input, combinator));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseSelectors(std::string_view& input, SelectorList& selectors)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
Selector selector;
|
||||||
|
if(!parseSelector(input, selector))
|
||||||
|
return false;
|
||||||
|
selectors.push_back(std::move(selector));
|
||||||
|
} while(skipDelimiterAndOptionalSpaces(input, ','));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseDeclarations(std::string_view& input, DeclarationList& declarations)
|
||||||
|
{
|
||||||
|
if(!skipDelimiter(input, '{'))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
do {
|
||||||
|
std::string name;
|
||||||
|
if(!readCSSIdentifier(input, name))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, ':'))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
std::string_view value(input);
|
||||||
|
while(!input.empty() && !(input.front() == '!' || input.front() == ';' || input.front() == '}'))
|
||||||
|
input.remove_prefix(1);
|
||||||
|
value.remove_suffix(input.length());
|
||||||
|
stripTrailingSpaces(value);
|
||||||
|
|
||||||
|
Declaration declaration;
|
||||||
|
declaration.specificity = 0x10;
|
||||||
|
declaration.id = csspropertyid(name);
|
||||||
|
declaration.value.assign(value);
|
||||||
|
if(skipDelimiter(input, '!')) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipString(input, "important"))
|
||||||
|
return false;
|
||||||
|
declaration.specificity = 0x1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(declaration.id != PropertyID::Unknown)
|
||||||
|
declarations.push_back(std::move(declaration));
|
||||||
|
skipOptionalSpacesOrDelimiter(input, ';');
|
||||||
|
} while(!input.empty() && input.front() != '}');
|
||||||
|
return skipDelimiter(input, '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseRule(std::string_view& input, Rule& rule)
|
||||||
|
{
|
||||||
|
if(!parseSelectors(input, rule.selectors))
|
||||||
|
return false;
|
||||||
|
return parseDeclarations(input, rule.declarations);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RuleDataList parseStyleSheet(std::string_view input)
|
||||||
|
{
|
||||||
|
RuleDataList rules;
|
||||||
|
while(!input.empty()) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(skipDelimiter(input, '@')) {
|
||||||
|
int depth = 0;
|
||||||
|
while(!input.empty()) {
|
||||||
|
auto ch = input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(ch == ';' && depth == 0)
|
||||||
|
break;
|
||||||
|
if(ch == '{') ++depth;
|
||||||
|
else if(ch == '}' && depth > 0) {
|
||||||
|
if(depth == 1)
|
||||||
|
break;
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule rule;
|
||||||
|
if(!parseRule(input, rule))
|
||||||
|
break;
|
||||||
|
for(const auto& selector : rule.selectors) {
|
||||||
|
size_t specificity = 0;
|
||||||
|
for(const auto& simpleSelector : selector) {
|
||||||
|
specificity += (simpleSelector.id == ElementID::Star) ? 0x0 : 0x1;
|
||||||
|
for(const auto& attributeSelector : simpleSelector.attributeSelectors) {
|
||||||
|
specificity += (attributeSelector.id == PropertyID::Id) ? 0x10000 : 0x100;
|
||||||
|
}
|
||||||
|
for(const auto& pseudoClassSelector : simpleSelector.pseudoClassSelectors) {
|
||||||
|
specificity += 0x100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rules.emplace_back(selector, rule.declarations, specificity, rules.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SelectorList parseQuerySelectors(std::string_view input)
|
||||||
|
{
|
||||||
|
SelectorList selectors;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseSelectors(input, selectors)
|
||||||
|
|| !input.empty()) {
|
||||||
|
return SelectorList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void parseInlineStyle(std::string_view input, SVGElement* element)
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
while(readCSSIdentifier(input, name)) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, ':'))
|
||||||
|
return;
|
||||||
|
std::string value;
|
||||||
|
while(!input.empty() && input.front() != ';') {
|
||||||
|
value.push_back(input.front());
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = csspropertyid(name);
|
||||||
|
if(id != PropertyID::Unknown)
|
||||||
|
element->setAttribute(0x100, id, value);
|
||||||
|
skipOptionalSpacesOrDelimiter(input, ';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void removeStyleComments(std::string& value)
|
||||||
|
{
|
||||||
|
auto start = value.find("/*");
|
||||||
|
while(start != std::string::npos) {
|
||||||
|
auto end = value.find("*/", start + 2);
|
||||||
|
value.erase(start, end - start + 2);
|
||||||
|
start = value.find("/*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool decodeText(std::string_view input, std::string& output)
|
||||||
|
{
|
||||||
|
output.clear();
|
||||||
|
while(!input.empty()) {
|
||||||
|
auto ch = input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(ch != '&') {
|
||||||
|
output.push_back(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiter(input, '#')) {
|
||||||
|
int base = 10;
|
||||||
|
if(skipDelimiter(input, 'x'))
|
||||||
|
base = 16;
|
||||||
|
unsigned int cp;
|
||||||
|
if(!parseInteger(input, cp, base))
|
||||||
|
return false;
|
||||||
|
char c[5] = {0, 0, 0, 0, 0};
|
||||||
|
if(cp < 0x80) {
|
||||||
|
c[1] = 0;
|
||||||
|
c[0] = char(cp);
|
||||||
|
} else if(cp < 0x800) {
|
||||||
|
c[2] = 0;
|
||||||
|
c[1] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[0] = char(cp | 0xC0);
|
||||||
|
} else if(cp < 0x10000) {
|
||||||
|
c[3] = 0;
|
||||||
|
c[2] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[1] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[0] = char(cp | 0xE0);
|
||||||
|
} else if(cp < 0x200000) {
|
||||||
|
c[4] = 0;
|
||||||
|
c[3] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[2] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[1] = char((cp & 0x3F) | 0x80);
|
||||||
|
cp >>= 6;
|
||||||
|
c[0] = char(cp | 0xF0);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.append(c);
|
||||||
|
} else {
|
||||||
|
if(skipString(input, "amp")) {
|
||||||
|
output.push_back('&');
|
||||||
|
} else if(skipString(input, "lt")) {
|
||||||
|
output.push_back('<');
|
||||||
|
} else if(skipString(input, "gt")) {
|
||||||
|
output.push_back('>');
|
||||||
|
} else if(skipString(input, "quot")) {
|
||||||
|
output.push_back('\"');
|
||||||
|
} else if(skipString(input, "apos")) {
|
||||||
|
output.push_back('\'');
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!skipDelimiter(input, ';')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IS_STARTNAMECHAR(int c) { return IS_ALPHA(c) || c == '_' || c == ':'; }
|
||||||
|
constexpr bool IS_NAMECHAR(int c) { return IS_STARTNAMECHAR(c) || IS_NUM(c) || c == '-' || c == '.'; }
|
||||||
|
|
||||||
|
inline bool readIdentifier(std::string_view& input, std::string& output)
|
||||||
|
{
|
||||||
|
if(input.empty() || !IS_STARTNAMECHAR(input.front()))
|
||||||
|
return false;
|
||||||
|
output.clear();
|
||||||
|
do {
|
||||||
|
output.push_back(input.front());
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && IS_NAMECHAR(input.front()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::parse(const char* data, size_t length)
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
std::string styleSheet;
|
||||||
|
SVGElement* currentElement = nullptr;
|
||||||
|
int ignoring = 0;
|
||||||
|
auto handleText = [&](std::string_view text, bool in_cdata) {
|
||||||
|
if(text.empty() || currentElement == nullptr || ignoring > 0)
|
||||||
|
return;
|
||||||
|
if(currentElement->id() != ElementID::Text && currentElement->id() != ElementID::Tspan && currentElement->id() != ElementID::Style) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_cdata) {
|
||||||
|
buffer.assign(text);
|
||||||
|
} else {
|
||||||
|
decodeText(text, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentElement->id() == ElementID::Style) {
|
||||||
|
removeStyleComments(buffer);
|
||||||
|
styleSheet.append(buffer);
|
||||||
|
} else {
|
||||||
|
auto node = std::make_unique<SVGTextNode>(this);
|
||||||
|
node->setData(buffer);
|
||||||
|
currentElement->addChild(std::move(node));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view input(data, length);
|
||||||
|
if(length >= 3) {
|
||||||
|
auto buffer = (const uint8_t*)(data);
|
||||||
|
|
||||||
|
const auto c1 = buffer[0];
|
||||||
|
const auto c2 = buffer[1];
|
||||||
|
const auto c3 = buffer[2];
|
||||||
|
if(c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) {
|
||||||
|
input.remove_prefix(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!input.empty()) {
|
||||||
|
if(currentElement) {
|
||||||
|
auto text = input.substr(0, input.find('<'));
|
||||||
|
handleText(text, false);
|
||||||
|
input.remove_prefix(text.length());
|
||||||
|
} else {
|
||||||
|
if(!skipOptionalSpaces(input)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!skipDelimiter(input, '<'))
|
||||||
|
return false;
|
||||||
|
if(skipDelimiter(input, '?')) {
|
||||||
|
if(!readIdentifier(input, buffer))
|
||||||
|
return false;
|
||||||
|
auto n = input.find("?>");
|
||||||
|
if(n == std::string_view::npos)
|
||||||
|
return false;
|
||||||
|
input.remove_prefix(n + 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiter(input, '!')) {
|
||||||
|
if(skipString(input, "--")) {
|
||||||
|
auto n = input.find("-->");
|
||||||
|
if(n == std::string_view::npos)
|
||||||
|
return false;
|
||||||
|
handleText(input.substr(0, n), false);
|
||||||
|
input.remove_prefix(n + 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipString(input, "[CDATA[")) {
|
||||||
|
auto n = input.find("]]>");
|
||||||
|
if(n == std::string_view::npos)
|
||||||
|
return false;
|
||||||
|
handleText(input.substr(0, n), true);
|
||||||
|
input.remove_prefix(n + 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipString(input, "DOCTYPE")) {
|
||||||
|
while(!input.empty() && input.front() != '>') {
|
||||||
|
if(input.front() == '[') {
|
||||||
|
int depth = 1;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
while(!input.empty() && depth > 0) {
|
||||||
|
if(input.front() == '[') ++depth;
|
||||||
|
else if(input.front() == ']') --depth;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!skipDelimiter(input, '>'))
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiter(input, '/')) {
|
||||||
|
if(currentElement == nullptr && ignoring == 0)
|
||||||
|
return false;
|
||||||
|
if(!readIdentifier(input, buffer))
|
||||||
|
return false;
|
||||||
|
if(ignoring == 0) {
|
||||||
|
auto id = elementid(buffer);
|
||||||
|
if(id != currentElement->id())
|
||||||
|
return false;
|
||||||
|
currentElement = currentElement->parentElement();
|
||||||
|
} else {
|
||||||
|
--ignoring;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, '>'))
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!readIdentifier(input, buffer))
|
||||||
|
return false;
|
||||||
|
SVGElement* element = nullptr;
|
||||||
|
if(ignoring > 0) {
|
||||||
|
++ignoring;
|
||||||
|
} else {
|
||||||
|
auto id = elementid(buffer);
|
||||||
|
if(id == ElementID::Unknown) {
|
||||||
|
ignoring = 1;
|
||||||
|
} else {
|
||||||
|
if(m_rootElement && currentElement == nullptr)
|
||||||
|
return false;
|
||||||
|
if(m_rootElement == nullptr) {
|
||||||
|
if(id != ElementID::Svg)
|
||||||
|
return false;
|
||||||
|
m_rootElement = std::make_unique<SVGRootElement>(this);
|
||||||
|
element = m_rootElement.get();
|
||||||
|
} else {
|
||||||
|
auto child = SVGElement::create(this, id);
|
||||||
|
element = child.get();
|
||||||
|
currentElement->addChild(std::move(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
while(readIdentifier(input, buffer)) {
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!skipDelimiter(input, '='))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(input.empty() || !(input.front() == '\"' || input.front() == '\''))
|
||||||
|
return false;
|
||||||
|
auto quote = input.front();
|
||||||
|
input.remove_prefix(1);
|
||||||
|
auto n = input.find(quote);
|
||||||
|
if(n == std::string_view::npos)
|
||||||
|
return false;
|
||||||
|
auto id = PropertyID::Unknown;
|
||||||
|
if(element != nullptr)
|
||||||
|
id = propertyid(buffer);
|
||||||
|
if(id != PropertyID::Unknown) {
|
||||||
|
decodeText(input.substr(0, n), buffer);
|
||||||
|
if(id == PropertyID::Style) {
|
||||||
|
removeStyleComments(buffer);
|
||||||
|
parseInlineStyle(buffer, element);
|
||||||
|
} else {
|
||||||
|
if(id == PropertyID::Id)
|
||||||
|
m_rootElement->addElementById(buffer, element);
|
||||||
|
element->setAttribute(0x1, id, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.remove_prefix(n + 1);
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiter(input, '>')) {
|
||||||
|
if(element != nullptr)
|
||||||
|
currentElement = element;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(skipDelimiter(input, '/')) {
|
||||||
|
if(!skipDelimiter(input, '>'))
|
||||||
|
return false;
|
||||||
|
if(ignoring > 0)
|
||||||
|
--ignoring;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_rootElement == nullptr || ignoring > 0 || !input.empty())
|
||||||
|
return false;
|
||||||
|
applyStyleSheet(styleSheet);
|
||||||
|
m_rootElement->build();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::applyStyleSheet(const std::string& content)
|
||||||
|
{
|
||||||
|
auto rules = parseStyleSheet(content);
|
||||||
|
if(!rules.empty()) {
|
||||||
|
std::sort(rules.begin(), rules.end());
|
||||||
|
m_rootElement->transverse([&rules](SVGElement* element) {
|
||||||
|
for(const auto& rule : rules) {
|
||||||
|
if(rule.match(element)) {
|
||||||
|
for(const auto& declaration : rule.declarations()) {
|
||||||
|
element->setAttribute(declaration.specificity, declaration.id, declaration.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementList Document::querySelectorAll(const std::string& content) const
|
||||||
|
{
|
||||||
|
auto selectors = parseQuerySelectors(content);
|
||||||
|
if(selectors.empty())
|
||||||
|
return ElementList();
|
||||||
|
ElementList elements;
|
||||||
|
m_rootElement->transverse([&](SVGElement* element) {
|
||||||
|
for(const auto& selector : selectors) {
|
||||||
|
if(matchSelector(selector, element)) {
|
||||||
|
elements.push_back(element);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
210
vendor/lunasvg/source/svgparserutils.h
vendored
Normal file
210
vendor/lunasvg/source/svgparserutils.h
vendored
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
#ifndef LUNASVG_SVGPARSERUTILS_H
|
||||||
|
#define LUNASVG_SVGPARSERUTILS_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <string_view>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
constexpr bool IS_NUM(int cc) { return cc >= '0' && cc <= '9'; }
|
||||||
|
constexpr bool IS_ALPHA(int cc) { return (cc >= 'a' && cc <= 'z') || (cc >= 'A' && cc <= 'Z'); }
|
||||||
|
constexpr bool IS_WS(int cc) { return cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r'; }
|
||||||
|
|
||||||
|
constexpr void stripLeadingSpaces(std::string_view& input)
|
||||||
|
{
|
||||||
|
while(!input.empty() && IS_WS(input.front())) {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void stripTrailingSpaces(std::string_view& input)
|
||||||
|
{
|
||||||
|
while(!input.empty() && IS_WS(input.back())) {
|
||||||
|
input.remove_suffix(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void stripLeadingAndTrailingSpaces(std::string_view& input)
|
||||||
|
{
|
||||||
|
stripLeadingSpaces(input);
|
||||||
|
stripTrailingSpaces(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipOptionalSpaces(std::string_view& input)
|
||||||
|
{
|
||||||
|
while(!input.empty() && IS_WS(input.front()))
|
||||||
|
input.remove_prefix(1);
|
||||||
|
return !input.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipOptionalSpacesOrDelimiter(std::string_view& input, char delimiter)
|
||||||
|
{
|
||||||
|
if(!input.empty() && !IS_WS(input.front()) && delimiter != input.front())
|
||||||
|
return false;
|
||||||
|
if(skipOptionalSpaces(input)) {
|
||||||
|
if(delimiter == input.front()) {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !input.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipOptionalSpacesOrComma(std::string_view& input)
|
||||||
|
{
|
||||||
|
return skipOptionalSpacesOrDelimiter(input, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipDelimiterAndOptionalSpaces(std::string_view& input, char delimiter)
|
||||||
|
{
|
||||||
|
if(!input.empty() && input.front() == delimiter) {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipDelimiter(std::string_view& input, char delimiter)
|
||||||
|
{
|
||||||
|
if(!input.empty() && input.front() == delimiter) {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool skipString(std::string_view& input, std::string_view value)
|
||||||
|
{
|
||||||
|
if(input.size() >= value.size() && value == input.substr(0, value.size())) {
|
||||||
|
input.remove_prefix(value.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool isIntegralDigit(char ch, int base)
|
||||||
|
{
|
||||||
|
if(IS_NUM(ch))
|
||||||
|
return ch - '0' < base;
|
||||||
|
if(IS_ALPHA(ch))
|
||||||
|
return (ch >= 'a' && ch < 'a' + base - 10) || (ch >= 'A' && ch < 'A' + base - 10);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int toIntegralDigit(char ch)
|
||||||
|
{
|
||||||
|
if(IS_NUM(ch))
|
||||||
|
return ch - '0';
|
||||||
|
if(ch >= 'a')
|
||||||
|
return ch - 'a' + 10;
|
||||||
|
return ch - 'A' + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool parseInteger(std::string_view& input, T& integer, int base = 10)
|
||||||
|
{
|
||||||
|
constexpr bool isSigned = std::numeric_limits<T>::is_signed;
|
||||||
|
constexpr T intMax = std::numeric_limits<T>::max();
|
||||||
|
const T maxMultiplier = intMax / static_cast<T>(base);
|
||||||
|
|
||||||
|
T value = 0;
|
||||||
|
bool isNegative = false;
|
||||||
|
|
||||||
|
if(!input.empty() && input.front() == '+') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} else if(!input.empty() && isSigned && input.front() == '-') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
isNegative = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input.empty() || !isIntegralDigit(input.front(), base))
|
||||||
|
return false;
|
||||||
|
do {
|
||||||
|
const int digitValue = toIntegralDigit(input.front());
|
||||||
|
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);
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && isIntegralDigit(input.front(), base));
|
||||||
|
|
||||||
|
using SignedType = typename std::make_signed<T>::type;
|
||||||
|
if(isNegative)
|
||||||
|
integer = -static_cast<SignedType>(value);
|
||||||
|
else
|
||||||
|
integer = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool parseNumber(std::string_view& input, T& number)
|
||||||
|
{
|
||||||
|
constexpr T maxValue = std::numeric_limits<T>::max();
|
||||||
|
T integer = 0;
|
||||||
|
T fraction = 0;
|
||||||
|
int exponent = 0;
|
||||||
|
int sign = 1;
|
||||||
|
int expsign = 1;
|
||||||
|
|
||||||
|
if(!input.empty() && input.front() == '+') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} else if(!input.empty() && input.front() == '-') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
sign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input.empty() || (!IS_NUM(input.front()) && input.front() != '.'))
|
||||||
|
return false;
|
||||||
|
if(IS_NUM(input.front())) {
|
||||||
|
do {
|
||||||
|
integer = static_cast<T>(10) * integer + (input.front() - '0');
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && IS_NUM(input.front()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!input.empty() && input.front() == '.') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty() || !IS_NUM(input.front()))
|
||||||
|
return false;
|
||||||
|
T divisor = static_cast<T>(1);
|
||||||
|
do {
|
||||||
|
fraction = static_cast<T>(10) * fraction + (input.front() - '0');
|
||||||
|
divisor *= static_cast<T>(10);
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && IS_NUM(input.front()));
|
||||||
|
fraction /= divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input.size() > 1 && (input[0] == 'e' || input[0] == 'E')
|
||||||
|
&& (input[1] != 'x' && input[1] != 'm'))
|
||||||
|
{
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(!input.empty() && input.front() == '+')
|
||||||
|
input.remove_prefix(1);
|
||||||
|
else if(!input.empty() && input.front() == '-') {
|
||||||
|
input.remove_prefix(1);
|
||||||
|
expsign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input.empty() || !IS_NUM(input.front()))
|
||||||
|
return false;
|
||||||
|
do {
|
||||||
|
exponent = 10 * exponent + (input.front() - '0');
|
||||||
|
input.remove_prefix(1);
|
||||||
|
} while(!input.empty() && IS_NUM(input.front()));
|
||||||
|
}
|
||||||
|
|
||||||
|
number = sign * (integer + fraction);
|
||||||
|
if(exponent)
|
||||||
|
number *= static_cast<T>(std::pow(10.0, expsign * exponent));
|
||||||
|
return number >= -maxValue && number <= maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGPARSERUTILS_H
|
||||||
705
vendor/lunasvg/source/svgproperty.cpp
vendored
Normal file
705
vendor/lunasvg/source/svgproperty.cpp
vendored
Normal file
@@ -0,0 +1,705 @@
|
|||||||
|
#include "svgproperty.h"
|
||||||
|
#include "svgelement.h"
|
||||||
|
#include "svgparserutils.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
PropertyID propertyid(std::string_view name)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
std::string_view name;
|
||||||
|
PropertyID value;
|
||||||
|
} table[] = {
|
||||||
|
{"class", PropertyID::Class},
|
||||||
|
{"clipPathUnits", PropertyID::ClipPathUnits},
|
||||||
|
{"cx", PropertyID::Cx},
|
||||||
|
{"cy", PropertyID::Cy},
|
||||||
|
{"d", PropertyID::D},
|
||||||
|
{"dx", PropertyID::Dx},
|
||||||
|
{"dy", PropertyID::Dy},
|
||||||
|
{"fx", PropertyID::Fx},
|
||||||
|
{"fy", PropertyID::Fy},
|
||||||
|
{"gradientTransform", PropertyID::GradientTransform},
|
||||||
|
{"gradientUnits", PropertyID::GradientUnits},
|
||||||
|
{"height", PropertyID::Height},
|
||||||
|
{"href", PropertyID::Href},
|
||||||
|
{"id", PropertyID::Id},
|
||||||
|
{"lengthAdjust", PropertyID::LengthAdjust},
|
||||||
|
{"markerHeight", PropertyID::MarkerHeight},
|
||||||
|
{"markerUnits", PropertyID::MarkerUnits},
|
||||||
|
{"markerWidth", PropertyID::MarkerWidth},
|
||||||
|
{"maskContentUnits", PropertyID::MaskContentUnits},
|
||||||
|
{"maskUnits", PropertyID::MaskUnits},
|
||||||
|
{"offset", PropertyID::Offset},
|
||||||
|
{"orient", PropertyID::Orient},
|
||||||
|
{"patternContentUnits", PropertyID::PatternContentUnits},
|
||||||
|
{"patternTransform", PropertyID::PatternTransform},
|
||||||
|
{"patternUnits", PropertyID::PatternUnits},
|
||||||
|
{"points", PropertyID::Points},
|
||||||
|
{"preserveAspectRatio", PropertyID::PreserveAspectRatio},
|
||||||
|
{"r", PropertyID::R},
|
||||||
|
{"refX", PropertyID::RefX},
|
||||||
|
{"refY", PropertyID::RefY},
|
||||||
|
{"rotate", PropertyID::Rotate},
|
||||||
|
{"rx", PropertyID::Rx},
|
||||||
|
{"ry", PropertyID::Ry},
|
||||||
|
{"spreadMethod", PropertyID::SpreadMethod},
|
||||||
|
{"style", PropertyID::Style},
|
||||||
|
{"textLength", PropertyID::TextLength},
|
||||||
|
{"transform", PropertyID::Transform},
|
||||||
|
{"viewBox", PropertyID::ViewBox},
|
||||||
|
{"width", PropertyID::Width},
|
||||||
|
{"x", PropertyID::X},
|
||||||
|
{"x1", PropertyID::X1},
|
||||||
|
{"x2", PropertyID::X2},
|
||||||
|
{"xlink:href", PropertyID::Href},
|
||||||
|
{"xml:space", PropertyID::White_Space},
|
||||||
|
{"y", PropertyID::Y},
|
||||||
|
{"y1", PropertyID::Y1},
|
||||||
|
{"y2", PropertyID::Y2}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = std::lower_bound(table, std::end(table), name, [](const auto& item, const auto& name) { return item.name < name; });
|
||||||
|
if(it == std::end(table) || it->name != name)
|
||||||
|
return csspropertyid(name);
|
||||||
|
return it->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyID csspropertyid(std::string_view name)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
std::string_view name;
|
||||||
|
PropertyID value;
|
||||||
|
} table[] = {
|
||||||
|
{"alignment-baseline", PropertyID::Alignment_Baseline},
|
||||||
|
{"baseline-shift", PropertyID::Baseline_Shift},
|
||||||
|
{"clip-path", PropertyID::Clip_Path},
|
||||||
|
{"clip-rule", PropertyID::Clip_Rule},
|
||||||
|
{"color", PropertyID::Color},
|
||||||
|
{"direction", PropertyID::Direction},
|
||||||
|
{"display", PropertyID::Display},
|
||||||
|
{"dominant-baseline", PropertyID::Dominant_Baseline},
|
||||||
|
{"fill", PropertyID::Fill},
|
||||||
|
{"fill-opacity", PropertyID::Fill_Opacity},
|
||||||
|
{"fill-rule", PropertyID::Fill_Rule},
|
||||||
|
{"font-family", PropertyID::Font_Family},
|
||||||
|
{"font-size", PropertyID::Font_Size},
|
||||||
|
{"font-style", PropertyID::Font_Style},
|
||||||
|
{"font-weight", PropertyID::Font_Weight},
|
||||||
|
{"letter-spacing", PropertyID::Letter_Spacing},
|
||||||
|
{"marker-end", PropertyID::Marker_End},
|
||||||
|
{"marker-mid", PropertyID::Marker_Mid},
|
||||||
|
{"marker-start", PropertyID::Marker_Start},
|
||||||
|
{"mask", PropertyID::Mask},
|
||||||
|
{"mask-type", PropertyID::Mask_Type},
|
||||||
|
{"opacity", PropertyID::Opacity},
|
||||||
|
{"overflow", PropertyID::Overflow},
|
||||||
|
{"pointer-events", PropertyID::Pointer_Events},
|
||||||
|
{"stop-color", PropertyID::Stop_Color},
|
||||||
|
{"stop-opacity", PropertyID::Stop_Opacity},
|
||||||
|
{"stroke", PropertyID::Stroke},
|
||||||
|
{"stroke-dasharray", PropertyID::Stroke_Dasharray},
|
||||||
|
{"stroke-dashoffset", PropertyID::Stroke_Dashoffset},
|
||||||
|
{"stroke-linecap", PropertyID::Stroke_Linecap},
|
||||||
|
{"stroke-linejoin", PropertyID::Stroke_Linejoin},
|
||||||
|
{"stroke-miterlimit", PropertyID::Stroke_Miterlimit},
|
||||||
|
{"stroke-opacity", PropertyID::Stroke_Opacity},
|
||||||
|
{"stroke-width", PropertyID::Stroke_Width},
|
||||||
|
{"text-anchor", PropertyID::Text_Anchor},
|
||||||
|
{"text-orientation", PropertyID::Text_Orientation},
|
||||||
|
{"visibility", PropertyID::Visibility},
|
||||||
|
{"white-space", PropertyID::White_Space},
|
||||||
|
{"word-spacing", PropertyID::Word_Spacing},
|
||||||
|
{"writing-mode", PropertyID::Writing_Mode}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = std::lower_bound(table, std::end(table), name, [](const auto& item, const auto& name) { return item.name < name; });
|
||||||
|
if(it == std::end(table) || it->name != name)
|
||||||
|
return PropertyID::Unknown;
|
||||||
|
return it->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGProperty::SVGProperty(PropertyID id)
|
||||||
|
: m_id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGString::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
m_value.assign(input);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool SVGEnumeration<SpreadMethod>::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<SpreadMethod> entries[] = {
|
||||||
|
{SpreadMethod::Pad, "pad"},
|
||||||
|
{SpreadMethod::Reflect, "reflect"},
|
||||||
|
{SpreadMethod::Repeat, "repeat"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnum(input, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool SVGEnumeration<Units>::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<Units> entries[] = {
|
||||||
|
{Units::UserSpaceOnUse, "userSpaceOnUse"},
|
||||||
|
{Units::ObjectBoundingBox, "objectBoundingBox"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnum(input, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool SVGEnumeration<MarkerUnits>::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<MarkerUnits> entries[] = {
|
||||||
|
{MarkerUnits::StrokeWidth, "strokeWidth"},
|
||||||
|
{MarkerUnits::UserSpaceOnUse, "userSpaceOnUse"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnum(input, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool SVGEnumeration<LengthAdjust>::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
static const SVGEnumerationEntry<LengthAdjust> entries[] = {
|
||||||
|
{LengthAdjust::Spacing, "spacing"},
|
||||||
|
{LengthAdjust::SpacingAndGlyphs, "spacingAndGlyphs"}
|
||||||
|
};
|
||||||
|
|
||||||
|
return parseEnum(input, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Enum>
|
||||||
|
template<unsigned int N>
|
||||||
|
bool SVGEnumeration<Enum>::parseEnum(std::string_view input, const SVGEnumerationEntry<Enum>(&entries)[N])
|
||||||
|
{
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
for(const auto& entry : entries) {
|
||||||
|
if(input == entry.second) {
|
||||||
|
m_value = entry.first;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGAngle::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(input == "auto") {
|
||||||
|
m_value = 0.f;
|
||||||
|
m_orientType = OrientType::Auto;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input == "auto-start-reverse") {
|
||||||
|
m_value = 0.f;
|
||||||
|
m_orientType = OrientType::AutoStartReverse;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float value = 0.f;
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return false;
|
||||||
|
if(!input.empty()) {
|
||||||
|
if(input == "rad")
|
||||||
|
value *= 180.f / PLUTOVG_PI;
|
||||||
|
else if(input == "grad")
|
||||||
|
value *= 360.f / 400.f;
|
||||||
|
else if(input == "turn")
|
||||||
|
value *= 360.f;
|
||||||
|
else if(input != "deg") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_value = value;
|
||||||
|
m_orientType = OrientType::Angle;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Length::parse(std::string_view input, LengthNegativeMode mode)
|
||||||
|
{
|
||||||
|
float value = 0.f;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return false;
|
||||||
|
if(value < 0.f && mode == LengthNegativeMode::Forbid)
|
||||||
|
return false;
|
||||||
|
if(input.empty()) {
|
||||||
|
m_value = value;
|
||||||
|
m_units = LengthUnits::None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto dpi = 96.f;
|
||||||
|
switch(input.front()) {
|
||||||
|
case '%':
|
||||||
|
m_value = value;
|
||||||
|
m_units = LengthUnits::Percent;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty())
|
||||||
|
return false;
|
||||||
|
else if(input.front() == 'x')
|
||||||
|
m_value = value;
|
||||||
|
else if(input.front() == 'c')
|
||||||
|
m_value = value * dpi / 6.f;
|
||||||
|
else if(input.front() == 't')
|
||||||
|
m_value = value * dpi / 72.f;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
m_units = LengthUnits::Px;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty())
|
||||||
|
return false;
|
||||||
|
else if(input.front() == 'n')
|
||||||
|
m_value = value * dpi;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
m_units = LengthUnits::Px;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty())
|
||||||
|
return false;
|
||||||
|
else if(input.front() == 'm')
|
||||||
|
m_value = value * dpi / 2.54f;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
m_units = LengthUnits::Px;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty())
|
||||||
|
return false;
|
||||||
|
else if(input.front() == 'm')
|
||||||
|
m_value = value * dpi / 25.4f;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
m_units = LengthUnits::Px;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if(input.empty())
|
||||||
|
return false;
|
||||||
|
else if(input.front() == 'm')
|
||||||
|
m_units = LengthUnits::Em;
|
||||||
|
else if(input.front() == 'x')
|
||||||
|
m_units = LengthUnits::Ex;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
m_value = value;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
float LengthContext::valueForLength(const Length& length, LengthDirection direction) const
|
||||||
|
{
|
||||||
|
if(length.units() == LengthUnits::Percent) {
|
||||||
|
if(m_units == Units::UserSpaceOnUse)
|
||||||
|
return length.value() * viewportDimension(direction) / 100.f;
|
||||||
|
return length.value() / 100.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(length.units() == LengthUnits::Ex)
|
||||||
|
return length.value() * m_element->font_size() / 2.f;
|
||||||
|
if(length.units() == LengthUnits::Em)
|
||||||
|
return length.value() * m_element->font_size();
|
||||||
|
return length.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
float LengthContext::viewportDimension(LengthDirection direction) const
|
||||||
|
{
|
||||||
|
auto viewportSize = m_element->currentViewportSize();
|
||||||
|
switch(direction) {
|
||||||
|
case LengthDirection::Horizontal:
|
||||||
|
return viewportSize.w;
|
||||||
|
case LengthDirection::Vertical:
|
||||||
|
return viewportSize.h;
|
||||||
|
default:
|
||||||
|
return std::sqrt(viewportSize.w * viewportSize.w + viewportSize.h * viewportSize.h) / PLUTOVG_SQRT2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGLength::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
return m_value.parse(input, m_negativeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGLengthList::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
m_values.clear();
|
||||||
|
while(!input.empty()) {
|
||||||
|
size_t count = 0;
|
||||||
|
while(count < input.length() && input[count] != ',' && !IS_WS(input[count]))
|
||||||
|
++count;
|
||||||
|
if(count == 0)
|
||||||
|
break;
|
||||||
|
Length value(0, LengthUnits::None);
|
||||||
|
if(!value.parse(input.substr(0, count), m_negativeMode))
|
||||||
|
return false;
|
||||||
|
input.remove_prefix(count);
|
||||||
|
skipOptionalSpacesOrComma(input);
|
||||||
|
m_values.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGNumber::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
float value = 0.f;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return false;
|
||||||
|
if(!input.empty())
|
||||||
|
return false;
|
||||||
|
m_value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGNumberPercentage::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
float value = 0.f;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return false;
|
||||||
|
if(!input.empty() && input.front() == '%') {
|
||||||
|
value /= 100.f;
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!input.empty())
|
||||||
|
return false;
|
||||||
|
m_value = std::clamp(value, 0.f, 1.f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGNumberList::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
m_values.clear();
|
||||||
|
stripLeadingSpaces(input);
|
||||||
|
while(!input.empty()) {
|
||||||
|
float value = 0.f;
|
||||||
|
if(!parseNumber(input, value))
|
||||||
|
return false;
|
||||||
|
skipOptionalSpacesOrComma(input);
|
||||||
|
m_values.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGPath::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
return m_value.parse(input.data(), input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGPoint::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
Point value;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseNumber(input, value.x)
|
||||||
|
|| !skipOptionalSpaces(input)
|
||||||
|
|| !parseNumber(input, value.y)
|
||||||
|
|| !input.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGPointList::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
m_values.clear();
|
||||||
|
stripLeadingSpaces(input);
|
||||||
|
while(!input.empty()) {
|
||||||
|
Point value;
|
||||||
|
if(!parseNumber(input, value.x)
|
||||||
|
|| !skipOptionalSpacesOrComma(input)
|
||||||
|
|| !parseNumber(input, value.y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_values.push_back(value);
|
||||||
|
skipOptionalSpacesOrComma(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGRect::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
Rect value;
|
||||||
|
stripLeadingAndTrailingSpaces(input);
|
||||||
|
if(!parseNumber(input, value.x)
|
||||||
|
|| !skipOptionalSpacesOrComma(input)
|
||||||
|
|| !parseNumber(input, value.y)
|
||||||
|
|| !skipOptionalSpacesOrComma(input)
|
||||||
|
|| !parseNumber(input, value.w)
|
||||||
|
|| !skipOptionalSpacesOrComma(input)
|
||||||
|
|| !parseNumber(input, value.h)
|
||||||
|
|| !input.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value.w < 0.f || value.h < 0.f)
|
||||||
|
return false;
|
||||||
|
m_value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGTransform::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
return m_value.parse(input.data(), input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGPreserveAspectRatio::parse(std::string_view input)
|
||||||
|
{
|
||||||
|
auto alignType = AlignType::xMidYMid;
|
||||||
|
stripLeadingSpaces(input);
|
||||||
|
if(skipString(input, "none"))
|
||||||
|
alignType = AlignType::None;
|
||||||
|
else if(skipString(input, "xMinYMin"))
|
||||||
|
alignType = AlignType::xMinYMin;
|
||||||
|
else if(skipString(input, "xMidYMin"))
|
||||||
|
alignType = AlignType::xMidYMin;
|
||||||
|
else if(skipString(input, "xMaxYMin"))
|
||||||
|
alignType = AlignType::xMaxYMin;
|
||||||
|
else if(skipString(input, "xMinYMid"))
|
||||||
|
alignType = AlignType::xMinYMid;
|
||||||
|
else if(skipString(input, "xMidYMid"))
|
||||||
|
alignType = AlignType::xMidYMid;
|
||||||
|
else if(skipString(input, "xMaxYMid"))
|
||||||
|
alignType = AlignType::xMaxYMid;
|
||||||
|
else if(skipString(input, "xMinYMax"))
|
||||||
|
alignType = AlignType::xMinYMax;
|
||||||
|
else if(skipString(input, "xMidYMax"))
|
||||||
|
alignType = AlignType::xMidYMax;
|
||||||
|
else if(skipString(input, "xMaxYMax"))
|
||||||
|
alignType = AlignType::xMaxYMax;
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto meetOrSlice = MeetOrSlice::Meet;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(skipString(input, "meet")) {
|
||||||
|
meetOrSlice = MeetOrSlice::Meet;
|
||||||
|
} else if(skipString(input, "slice")) {
|
||||||
|
meetOrSlice = MeetOrSlice::Slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(alignType == AlignType::None)
|
||||||
|
meetOrSlice = MeetOrSlice::Meet;
|
||||||
|
skipOptionalSpaces(input);
|
||||||
|
if(!input.empty())
|
||||||
|
return false;
|
||||||
|
m_alignType = alignType;
|
||||||
|
m_meetOrSlice = meetOrSlice;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGPreserveAspectRatio::getClipRect(const Rect& viewBoxRect, const Size& viewportSize) const
|
||||||
|
{
|
||||||
|
assert(!viewBoxRect.isEmpty() && !viewportSize.isEmpty());
|
||||||
|
auto xScale = viewportSize.w / viewBoxRect.w;
|
||||||
|
auto yScale = viewportSize.h / viewBoxRect.h;
|
||||||
|
if(m_alignType == AlignType::None) {
|
||||||
|
return Rect(viewBoxRect.x, viewBoxRect.y, viewportSize.w / xScale, viewportSize.h / yScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scale = (m_meetOrSlice == MeetOrSlice::Meet) ? std::min(xScale, yScale) : std::max(xScale, yScale);
|
||||||
|
auto xOffset = -viewBoxRect.x * scale;
|
||||||
|
auto yOffset = -viewBoxRect.y * scale;
|
||||||
|
auto viewWidth = viewBoxRect.w * scale;
|
||||||
|
auto viewHeight = viewBoxRect.h * scale;
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMidYMin:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
xOffset += (viewportSize.w - viewWidth) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMaxYMin:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
xOffset += (viewportSize.w - viewWidth);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMinYMid:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
yOffset += (viewportSize.h - viewHeight) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMinYMax:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
yOffset += (viewportSize.h - viewHeight);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Rect(-xOffset / scale, -yOffset / scale, viewportSize.w / scale, viewportSize.h / scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform SVGPreserveAspectRatio::getTransform(const Rect& viewBoxRect, const Size& viewportSize) const
|
||||||
|
{
|
||||||
|
assert(!viewBoxRect.isEmpty() && !viewportSize.isEmpty());
|
||||||
|
auto xScale = viewportSize.w / viewBoxRect.w;
|
||||||
|
auto yScale = viewportSize.h / viewBoxRect.h;
|
||||||
|
if(m_alignType == AlignType::None) {
|
||||||
|
return Transform(xScale, 0, 0, yScale, -viewBoxRect.x * xScale, -viewBoxRect.y * yScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto scale = (m_meetOrSlice == MeetOrSlice::Meet) ? std::min(xScale, yScale) : std::max(xScale, yScale);
|
||||||
|
auto xOffset = -viewBoxRect.x * scale;
|
||||||
|
auto yOffset = -viewBoxRect.y * scale;
|
||||||
|
auto viewWidth = viewBoxRect.w * scale;
|
||||||
|
auto viewHeight = viewBoxRect.h * scale;
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMidYMin:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
xOffset += (viewportSize.w - viewWidth) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMaxYMin:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
xOffset += (viewportSize.w - viewWidth);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMinYMid:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
yOffset += (viewportSize.h - viewHeight) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMinYMax:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
yOffset += (viewportSize.h - viewHeight);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Transform(scale, 0, 0, scale, xOffset, yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGPreserveAspectRatio::transformRect(Rect& dstRect, Rect& srcRect) const
|
||||||
|
{
|
||||||
|
if(m_alignType == AlignType::None)
|
||||||
|
return;
|
||||||
|
auto viewSize = dstRect.size();
|
||||||
|
auto imageSize = srcRect.size();
|
||||||
|
if(m_meetOrSlice == MeetOrSlice::Meet) {
|
||||||
|
auto scale = imageSize.h / imageSize.w;
|
||||||
|
if(viewSize.h > viewSize.w * scale) {
|
||||||
|
dstRect.h = viewSize.w * scale;
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMinYMid:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
dstRect.y += (viewSize.h - dstRect.h) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMinYMax:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
dstRect.y += viewSize.h - dstRect.h;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(viewSize.w > viewSize.h / scale) {
|
||||||
|
dstRect.w = viewSize.h / scale;
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMidYMin:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
dstRect.x += (viewSize.w - dstRect.w) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMaxYMin:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
dstRect.x += viewSize.w - dstRect.w;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(m_meetOrSlice == MeetOrSlice::Slice) {
|
||||||
|
auto scale = imageSize.h / imageSize.w;
|
||||||
|
if(viewSize.h < viewSize.w * scale) {
|
||||||
|
srcRect.h = viewSize.h * (imageSize.w / viewSize.w);
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMinYMid:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
srcRect.y += (imageSize.h - srcRect.h) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMinYMax:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
srcRect.y += imageSize.h - srcRect.h;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(viewSize.w < viewSize.h / scale) {
|
||||||
|
srcRect.w = viewSize.w * (imageSize.h / viewSize.h);
|
||||||
|
switch(m_alignType) {
|
||||||
|
case AlignType::xMidYMin:
|
||||||
|
case AlignType::xMidYMid:
|
||||||
|
case AlignType::xMidYMax:
|
||||||
|
srcRect.x += (imageSize.w - srcRect.w) * 0.5f;
|
||||||
|
break;
|
||||||
|
case AlignType::xMaxYMin:
|
||||||
|
case AlignType::xMaxYMid:
|
||||||
|
case AlignType::xMaxYMax:
|
||||||
|
srcRect.x += imageSize.w - srcRect.w;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
570
vendor/lunasvg/source/svgproperty.h
vendored
Normal file
570
vendor/lunasvg/source/svgproperty.h
vendored
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
#ifndef LUNASVG_SVGPROPERTY_H
|
||||||
|
#define LUNASVG_SVGPROPERTY_H
|
||||||
|
|
||||||
|
#include "graphics.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
enum class PropertyID : uint8_t {
|
||||||
|
Unknown = 0,
|
||||||
|
Alignment_Baseline,
|
||||||
|
Baseline_Shift,
|
||||||
|
Class,
|
||||||
|
Clip_Path,
|
||||||
|
Clip_Rule,
|
||||||
|
ClipPathUnits,
|
||||||
|
Color,
|
||||||
|
Cx,
|
||||||
|
Cy,
|
||||||
|
D,
|
||||||
|
Direction,
|
||||||
|
Display,
|
||||||
|
Dominant_Baseline,
|
||||||
|
Dx,
|
||||||
|
Dy,
|
||||||
|
Fill,
|
||||||
|
Fill_Opacity,
|
||||||
|
Fill_Rule,
|
||||||
|
Font_Family,
|
||||||
|
Font_Size,
|
||||||
|
Font_Style,
|
||||||
|
Font_Weight,
|
||||||
|
Fx,
|
||||||
|
Fy,
|
||||||
|
GradientTransform,
|
||||||
|
GradientUnits,
|
||||||
|
Height,
|
||||||
|
Href,
|
||||||
|
Id,
|
||||||
|
LengthAdjust,
|
||||||
|
Letter_Spacing,
|
||||||
|
Marker_End,
|
||||||
|
Marker_Mid,
|
||||||
|
Marker_Start,
|
||||||
|
MarkerHeight,
|
||||||
|
MarkerUnits,
|
||||||
|
MarkerWidth,
|
||||||
|
Mask,
|
||||||
|
Mask_Type,
|
||||||
|
MaskContentUnits,
|
||||||
|
MaskUnits,
|
||||||
|
Offset,
|
||||||
|
Opacity,
|
||||||
|
Orient,
|
||||||
|
Overflow,
|
||||||
|
PatternContentUnits,
|
||||||
|
PatternTransform,
|
||||||
|
PatternUnits,
|
||||||
|
Pointer_Events,
|
||||||
|
Points,
|
||||||
|
PreserveAspectRatio,
|
||||||
|
R,
|
||||||
|
RefX,
|
||||||
|
RefY,
|
||||||
|
Rotate,
|
||||||
|
Rx,
|
||||||
|
Ry,
|
||||||
|
SpreadMethod,
|
||||||
|
Stop_Color,
|
||||||
|
Stop_Opacity,
|
||||||
|
Stroke,
|
||||||
|
Stroke_Dasharray,
|
||||||
|
Stroke_Dashoffset,
|
||||||
|
Stroke_Linecap,
|
||||||
|
Stroke_Linejoin,
|
||||||
|
Stroke_Miterlimit,
|
||||||
|
Stroke_Opacity,
|
||||||
|
Stroke_Width,
|
||||||
|
Style,
|
||||||
|
Text_Anchor,
|
||||||
|
Text_Orientation,
|
||||||
|
TextLength,
|
||||||
|
Transform,
|
||||||
|
ViewBox,
|
||||||
|
Visibility,
|
||||||
|
White_Space,
|
||||||
|
Width,
|
||||||
|
Word_Spacing,
|
||||||
|
Writing_Mode,
|
||||||
|
X,
|
||||||
|
X1,
|
||||||
|
X2,
|
||||||
|
Y,
|
||||||
|
Y1,
|
||||||
|
Y2
|
||||||
|
};
|
||||||
|
|
||||||
|
PropertyID propertyid(std::string_view name);
|
||||||
|
PropertyID csspropertyid(std::string_view name);
|
||||||
|
|
||||||
|
class SVGElement;
|
||||||
|
|
||||||
|
class SVGProperty {
|
||||||
|
public:
|
||||||
|
SVGProperty(PropertyID id);
|
||||||
|
virtual ~SVGProperty() = default;
|
||||||
|
PropertyID id() const { return m_id; }
|
||||||
|
|
||||||
|
virtual bool parse(std::string_view input) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SVGProperty(const SVGProperty&) = delete;
|
||||||
|
SVGProperty& operator=(const SVGProperty&) = delete;
|
||||||
|
PropertyID m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGString final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGString(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Paint {
|
||||||
|
public:
|
||||||
|
Paint() = default;
|
||||||
|
explicit Paint(const Color& color) : m_color(color) {}
|
||||||
|
Paint(const std::string& id, const Color& color)
|
||||||
|
: m_id(id), m_color(color)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const Color& color() const { return m_color; }
|
||||||
|
const std::string& id() const { return m_id; }
|
||||||
|
bool isNone() const { return m_id.empty() && !m_color.isVisible(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_id;
|
||||||
|
Color m_color = Color::Transparent;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Display : uint8_t {
|
||||||
|
Inline,
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Visibility : uint8_t {
|
||||||
|
Visible,
|
||||||
|
Hidden,
|
||||||
|
Collapse
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Overflow : uint8_t {
|
||||||
|
Visible,
|
||||||
|
Hidden
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PointerEvents : uint8_t {
|
||||||
|
None,
|
||||||
|
Auto,
|
||||||
|
Stroke,
|
||||||
|
Fill,
|
||||||
|
Painted,
|
||||||
|
Visible,
|
||||||
|
VisibleStroke,
|
||||||
|
VisibleFill,
|
||||||
|
VisiblePainted,
|
||||||
|
BoundingBox,
|
||||||
|
All
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FontStyle : uint8_t {
|
||||||
|
Normal,
|
||||||
|
Italic
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FontWeight : uint8_t {
|
||||||
|
Normal,
|
||||||
|
Bold
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AlignmentBaseline : uint8_t {
|
||||||
|
Auto,
|
||||||
|
Baseline,
|
||||||
|
BeforeEdge,
|
||||||
|
TextBeforeEdge,
|
||||||
|
Middle,
|
||||||
|
Central,
|
||||||
|
AfterEdge,
|
||||||
|
TextAfterEdge,
|
||||||
|
Ideographic,
|
||||||
|
Alphabetic,
|
||||||
|
Hanging,
|
||||||
|
Mathematical
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DominantBaseline : uint8_t {
|
||||||
|
Auto,
|
||||||
|
UseScript,
|
||||||
|
NoChange,
|
||||||
|
ResetSize,
|
||||||
|
Ideographic,
|
||||||
|
Alphabetic,
|
||||||
|
Hanging,
|
||||||
|
Mathematical,
|
||||||
|
Central,
|
||||||
|
Middle,
|
||||||
|
TextAfterEdge,
|
||||||
|
TextBeforeEdge
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextAnchor : uint8_t {
|
||||||
|
Start,
|
||||||
|
Middle,
|
||||||
|
End
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WhiteSpace : uint8_t {
|
||||||
|
Default,
|
||||||
|
Preserve
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WritingMode : uint8_t {
|
||||||
|
Horizontal,
|
||||||
|
Vertical
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TextOrientation : uint8_t {
|
||||||
|
Mixed,
|
||||||
|
Upright
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Direction : uint8_t {
|
||||||
|
Ltr,
|
||||||
|
Rtl
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MaskType : uint8_t {
|
||||||
|
Luminance,
|
||||||
|
Alpha
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Units : uint8_t {
|
||||||
|
UserSpaceOnUse,
|
||||||
|
ObjectBoundingBox
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MarkerUnits : uint8_t {
|
||||||
|
StrokeWidth,
|
||||||
|
UserSpaceOnUse
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LengthAdjust : uint8_t {
|
||||||
|
Spacing,
|
||||||
|
SpacingAndGlyphs
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Enum>
|
||||||
|
using SVGEnumerationEntry = std::pair<Enum, std::string_view>;
|
||||||
|
|
||||||
|
template<typename Enum>
|
||||||
|
class SVGEnumeration final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGEnumeration(PropertyID id, Enum value)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_value(value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Enum value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<unsigned int N>
|
||||||
|
bool parseEnum(std::string_view input, const SVGEnumerationEntry<Enum>(&entries)[N]);
|
||||||
|
Enum m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGAngle final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
enum class OrientType {
|
||||||
|
Auto,
|
||||||
|
AutoStartReverse,
|
||||||
|
Angle
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit SVGAngle(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
float value() const { return m_value; }
|
||||||
|
OrientType orientType() const { return m_orientType; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_value = 0;
|
||||||
|
OrientType m_orientType = OrientType::Angle;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LengthUnits : uint8_t {
|
||||||
|
None,
|
||||||
|
Percent,
|
||||||
|
Px,
|
||||||
|
Em,
|
||||||
|
Ex
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LengthDirection : uint8_t {
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
Diagonal
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LengthNegativeMode : uint8_t {
|
||||||
|
Allow,
|
||||||
|
Forbid
|
||||||
|
};
|
||||||
|
|
||||||
|
class Length {
|
||||||
|
public:
|
||||||
|
Length() = default;
|
||||||
|
Length(float value, LengthUnits units)
|
||||||
|
: m_value(value), m_units(units)
|
||||||
|
{}
|
||||||
|
|
||||||
|
float value() const { return m_value; }
|
||||||
|
LengthUnits units() const { return m_units; }
|
||||||
|
|
||||||
|
bool parse(std::string_view input, LengthNegativeMode mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_value = 0.f;
|
||||||
|
LengthUnits m_units = LengthUnits::None;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGLength final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
SVGLength(PropertyID id, LengthDirection direction, LengthNegativeMode negativeMode, float value = 0, LengthUnits units = LengthUnits::None)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_direction(direction)
|
||||||
|
, m_negativeMode(negativeMode)
|
||||||
|
, m_value(value, units)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool isPercent() const { return m_value.units() == LengthUnits::Percent; }
|
||||||
|
|
||||||
|
LengthDirection direction() const { return m_direction; }
|
||||||
|
LengthNegativeMode negativeMode() const { return m_negativeMode; }
|
||||||
|
const Length& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const LengthDirection m_direction;
|
||||||
|
const LengthNegativeMode m_negativeMode;
|
||||||
|
Length m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LengthContext {
|
||||||
|
public:
|
||||||
|
LengthContext(const SVGElement* element, Units units = Units::UserSpaceOnUse)
|
||||||
|
: m_element(element), m_units(units)
|
||||||
|
{}
|
||||||
|
|
||||||
|
float valueForLength(const Length& length, LengthDirection direction) const;
|
||||||
|
float valueForLength(const SVGLength& length) const { return valueForLength(length.value(), length.direction()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
float viewportDimension(LengthDirection direction) const;
|
||||||
|
const SVGElement* m_element;
|
||||||
|
const Units m_units;
|
||||||
|
};
|
||||||
|
|
||||||
|
using LengthList = std::vector<Length>;
|
||||||
|
|
||||||
|
class SVGLengthList final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
SVGLengthList(PropertyID id, LengthDirection direction, LengthNegativeMode negativeMode)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_direction(direction)
|
||||||
|
, m_negativeMode(negativeMode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LengthDirection direction() const { return m_direction; }
|
||||||
|
LengthNegativeMode negativeMode() const { return m_negativeMode; }
|
||||||
|
const LengthList& values() const { return m_values; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const LengthDirection m_direction;
|
||||||
|
const LengthNegativeMode m_negativeMode;
|
||||||
|
LengthList m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BaselineShift {
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Baseline,
|
||||||
|
Sub,
|
||||||
|
Super,
|
||||||
|
Length
|
||||||
|
};
|
||||||
|
|
||||||
|
BaselineShift() = default;
|
||||||
|
BaselineShift(Type type) : m_type(type) {}
|
||||||
|
BaselineShift(const Length& length) : m_type(Type::Length), m_length(length) {}
|
||||||
|
|
||||||
|
Type type() const { return m_type; }
|
||||||
|
const Length& length() const { return m_length; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type{Type::Baseline};
|
||||||
|
Length m_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGNumber : public SVGProperty {
|
||||||
|
public:
|
||||||
|
SVGNumber(PropertyID id, float value)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_value(value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
float value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGNumberPercentage final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
SVGNumberPercentage(PropertyID id, float value)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_value(value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
float value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NumberList = std::vector<float>;
|
||||||
|
|
||||||
|
class SVGNumberList final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGNumberList(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const NumberList& values() const { return m_values; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NumberList m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPath final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGPath(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const Path& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Path m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPoint final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGPoint(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const Point& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Point m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PointList = std::vector<Point>;
|
||||||
|
|
||||||
|
class SVGPointList final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGPointList(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const PointList& values() const { return m_values; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PointList m_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRect final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGRect(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
, m_value(Rect::Invalid)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const Rect& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rect m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGTransform final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
explicit SVGTransform(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const Transform& value() const { return m_value; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Transform m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGPreserveAspectRatio final : public SVGProperty {
|
||||||
|
public:
|
||||||
|
enum class AlignType {
|
||||||
|
None,
|
||||||
|
xMinYMin,
|
||||||
|
xMidYMin,
|
||||||
|
xMaxYMin,
|
||||||
|
xMinYMid,
|
||||||
|
xMidYMid,
|
||||||
|
xMaxYMid,
|
||||||
|
xMinYMax,
|
||||||
|
xMidYMax,
|
||||||
|
xMaxYMax
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MeetOrSlice {
|
||||||
|
Meet,
|
||||||
|
Slice
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit SVGPreserveAspectRatio(PropertyID id)
|
||||||
|
: SVGProperty(id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AlignType alignType() const { return m_alignType; }
|
||||||
|
MeetOrSlice meetOrSlice() const { return m_meetOrSlice; }
|
||||||
|
bool parse(std::string_view input) final;
|
||||||
|
|
||||||
|
Rect getClipRect(const Rect& viewBoxRect, const Size& viewportSize) const;
|
||||||
|
Transform getTransform(const Rect& viewBoxRect, const Size& viewportSize) const;
|
||||||
|
void transformRect(Rect& dstRect, Rect& srcRect) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AlignType m_alignType = AlignType::xMidYMid;
|
||||||
|
MeetOrSlice m_meetOrSlice = MeetOrSlice::Meet;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGPROPERTY_H
|
||||||
61
vendor/lunasvg/source/svgrenderstate.cpp
vendored
Normal file
61
vendor/lunasvg/source/svgrenderstate.cpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "svgrenderstate.h"
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
SVGBlendInfo::SVGBlendInfo(const SVGElement* element)
|
||||||
|
: m_clipper(element->clipper())
|
||||||
|
, m_masker(element->masker())
|
||||||
|
, m_opacity(element->opacity())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGBlendInfo::requiresCompositing(SVGRenderMode mode) const
|
||||||
|
{
|
||||||
|
return (m_clipper && m_clipper->requiresMasking()) || (mode == SVGRenderMode::Painting && (m_masker || m_opacity < 1.f));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SVGRenderState::hasCycleReference(const SVGElement* element) const
|
||||||
|
{
|
||||||
|
auto current = this;
|
||||||
|
do {
|
||||||
|
if(element == current->element())
|
||||||
|
return true;
|
||||||
|
current = current->parent();
|
||||||
|
} while(current);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGRenderState::beginGroup(const SVGBlendInfo& blendInfo)
|
||||||
|
{
|
||||||
|
auto requiresCompositing = blendInfo.requiresCompositing(m_mode);
|
||||||
|
if(requiresCompositing) {
|
||||||
|
auto boundingBox = m_currentTransform.mapRect(m_element->paintBoundingBox());
|
||||||
|
boundingBox.intersect(m_canvas->extents());
|
||||||
|
m_canvas = Canvas::create(boundingBox);
|
||||||
|
} else {
|
||||||
|
m_canvas->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!requiresCompositing && blendInfo.clipper()) {
|
||||||
|
blendInfo.clipper()->applyClipPath(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGRenderState::endGroup(const SVGBlendInfo& blendInfo)
|
||||||
|
{
|
||||||
|
if(m_canvas == m_parent->canvas()) {
|
||||||
|
m_canvas->restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto opacity = m_mode == SVGRenderMode::Clipping ? 1.f : blendInfo.opacity();
|
||||||
|
if(blendInfo.clipper())
|
||||||
|
blendInfo.clipper()->applyClipMask(*this);
|
||||||
|
if(m_mode == SVGRenderMode::Painting && blendInfo.masker()) {
|
||||||
|
blendInfo.masker()->applyMask(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_parent->m_canvas->blendCanvas(*m_canvas, BlendMode::Src_Over, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
69
vendor/lunasvg/source/svgrenderstate.h
vendored
Normal file
69
vendor/lunasvg/source/svgrenderstate.h
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#ifndef LUNASVG_SVGRENDERSTATE_H
|
||||||
|
#define LUNASVG_SVGRENDERSTATE_H
|
||||||
|
|
||||||
|
#include "svgelement.h"
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
enum class SVGRenderMode {
|
||||||
|
Painting,
|
||||||
|
Clipping
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGBlendInfo {
|
||||||
|
public:
|
||||||
|
explicit SVGBlendInfo(const SVGElement* element);
|
||||||
|
SVGBlendInfo(const SVGClipPathElement* clipper, const SVGMaskElement* masker, float opacity)
|
||||||
|
: m_clipper(clipper), m_masker(masker), m_opacity(opacity)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool requiresCompositing(SVGRenderMode mode) const;
|
||||||
|
const SVGClipPathElement* clipper() const { return m_clipper; }
|
||||||
|
const SVGMaskElement* masker() const { return m_masker; }
|
||||||
|
float opacity() const { return m_opacity; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGClipPathElement* m_clipper;
|
||||||
|
const SVGMaskElement* m_masker;
|
||||||
|
const float m_opacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGRenderState {
|
||||||
|
public:
|
||||||
|
SVGRenderState(const SVGElement* element, const SVGRenderState& parent, const Transform& localTransform)
|
||||||
|
: m_element(element), m_parent(&parent), m_currentTransform(parent.currentTransform() * localTransform)
|
||||||
|
, m_mode(parent.mode()), m_canvas(parent.canvas())
|
||||||
|
{}
|
||||||
|
|
||||||
|
SVGRenderState(const SVGElement* element, const SVGRenderState* parent, const Transform& currentTransform, SVGRenderMode mode, std::shared_ptr<Canvas> canvas)
|
||||||
|
: m_element(element), m_parent(parent), m_currentTransform(currentTransform), m_mode(mode), m_canvas(std::move(canvas))
|
||||||
|
{}
|
||||||
|
|
||||||
|
Canvas& operator*() const { return *m_canvas; }
|
||||||
|
Canvas* operator->() const { return &*m_canvas; }
|
||||||
|
|
||||||
|
const SVGElement* element() const { return m_element; }
|
||||||
|
const SVGRenderState* parent() const { return m_parent; }
|
||||||
|
const Transform& currentTransform() const { return m_currentTransform; }
|
||||||
|
const SVGRenderMode mode() const { return m_mode; }
|
||||||
|
const std::shared_ptr<Canvas>& canvas() const { return m_canvas; }
|
||||||
|
|
||||||
|
Rect fillBoundingBox() const { return m_element->fillBoundingBox(); }
|
||||||
|
Rect paintBoundingBox() const { return m_element->paintBoundingBox(); }
|
||||||
|
|
||||||
|
bool hasCycleReference(const SVGElement* element) const;
|
||||||
|
|
||||||
|
void beginGroup(const SVGBlendInfo& blendInfo);
|
||||||
|
void endGroup(const SVGBlendInfo& blendInfo);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SVGElement* m_element;
|
||||||
|
const SVGRenderState* m_parent;
|
||||||
|
const Transform m_currentTransform;
|
||||||
|
const SVGRenderMode m_mode;
|
||||||
|
std::shared_ptr<Canvas> m_canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGRENDERSTATE_H
|
||||||
579
vendor/lunasvg/source/svgtextelement.cpp
vendored
Normal file
579
vendor/lunasvg/source/svgtextelement.cpp
vendored
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
#include "svgtextelement.h"
|
||||||
|
#include "svglayoutstate.h"
|
||||||
|
#include "svgrenderstate.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
inline const SVGTextNode* toSVGTextNode(const SVGNode* node)
|
||||||
|
{
|
||||||
|
assert(node && node->isTextNode());
|
||||||
|
return static_cast<const SVGTextNode*>(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const SVGTextPositioningElement* toSVGTextPositioningElement(const SVGNode* node)
|
||||||
|
{
|
||||||
|
assert(node && node->isTextPositioningElement());
|
||||||
|
return static_cast<const SVGTextPositioningElement*>(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlignmentBaseline resolveDominantBaseline(const SVGTextPositioningElement* element)
|
||||||
|
{
|
||||||
|
switch(element->dominant_baseline()) {
|
||||||
|
case DominantBaseline::Auto:
|
||||||
|
if(element->isVerticalWritingMode())
|
||||||
|
return AlignmentBaseline::Central;
|
||||||
|
return AlignmentBaseline::Alphabetic;
|
||||||
|
case DominantBaseline::UseScript:
|
||||||
|
case DominantBaseline::NoChange:
|
||||||
|
case DominantBaseline::ResetSize:
|
||||||
|
return AlignmentBaseline::Auto;
|
||||||
|
case DominantBaseline::Ideographic:
|
||||||
|
return AlignmentBaseline::Ideographic;
|
||||||
|
case DominantBaseline::Alphabetic:
|
||||||
|
return AlignmentBaseline::Alphabetic;
|
||||||
|
case DominantBaseline::Hanging:
|
||||||
|
return AlignmentBaseline::Hanging;
|
||||||
|
case DominantBaseline::Mathematical:
|
||||||
|
return AlignmentBaseline::Mathematical;
|
||||||
|
case DominantBaseline::Central:
|
||||||
|
return AlignmentBaseline::Central;
|
||||||
|
case DominantBaseline::Middle:
|
||||||
|
return AlignmentBaseline::Middle;
|
||||||
|
case DominantBaseline::TextAfterEdge:
|
||||||
|
return AlignmentBaseline::TextAfterEdge;
|
||||||
|
case DominantBaseline::TextBeforeEdge:
|
||||||
|
return AlignmentBaseline::TextBeforeEdge;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlignmentBaseline::Auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calculateBaselineOffset(const SVGTextPositioningElement* element)
|
||||||
|
{
|
||||||
|
auto offset = element->baseline_offset();
|
||||||
|
auto parent = element->parentElement();
|
||||||
|
while(parent->isTextPositioningElement()) {
|
||||||
|
offset += toSVGTextPositioningElement(parent)->baseline_offset();
|
||||||
|
parent = parent->parentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto baseline = element->alignment_baseline();
|
||||||
|
if(baseline == AlignmentBaseline::Auto || baseline == AlignmentBaseline::Baseline) {
|
||||||
|
baseline = resolveDominantBaseline(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& font = element->font();
|
||||||
|
switch(baseline) {
|
||||||
|
case AlignmentBaseline::BeforeEdge:
|
||||||
|
case AlignmentBaseline::TextBeforeEdge:
|
||||||
|
offset -= font.ascent();
|
||||||
|
break;
|
||||||
|
case AlignmentBaseline::Middle:
|
||||||
|
offset -= font.xHeight() / 2.f;
|
||||||
|
break;
|
||||||
|
case AlignmentBaseline::Central:
|
||||||
|
offset -= (font.ascent() + font.descent()) / 2.f;
|
||||||
|
break;
|
||||||
|
case AlignmentBaseline::AfterEdge:
|
||||||
|
case AlignmentBaseline::TextAfterEdge:
|
||||||
|
case AlignmentBaseline::Ideographic:
|
||||||
|
offset -= font.descent();
|
||||||
|
break;
|
||||||
|
case AlignmentBaseline::Hanging:
|
||||||
|
offset -= font.ascent() * 8.f / 10.f;
|
||||||
|
break;
|
||||||
|
case AlignmentBaseline::Mathematical:
|
||||||
|
offset -= font.ascent() / 2.f;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool needsTextAnchorAdjustment(const SVGTextPositioningElement* element)
|
||||||
|
{
|
||||||
|
auto direction = element->direction();
|
||||||
|
switch(element->text_anchor()) {
|
||||||
|
case TextAnchor::Start:
|
||||||
|
return direction == Direction::Rtl;
|
||||||
|
case TextAnchor::Middle:
|
||||||
|
return true;
|
||||||
|
case TextAnchor::End:
|
||||||
|
return direction == Direction::Ltr;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calculateTextAnchorOffset(const SVGTextPositioningElement* element, float width)
|
||||||
|
{
|
||||||
|
auto direction = element->direction();
|
||||||
|
switch(element->text_anchor()) {
|
||||||
|
case TextAnchor::Start:
|
||||||
|
if(direction == Direction::Ltr)
|
||||||
|
return 0.f;
|
||||||
|
return -width;
|
||||||
|
case TextAnchor::Middle:
|
||||||
|
return -width / 2.f;
|
||||||
|
case TextAnchor::End:
|
||||||
|
if(direction == Direction::Ltr)
|
||||||
|
return -width;
|
||||||
|
return 0.f;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
using SVGTextFragmentIterator = SVGTextFragmentList::iterator;
|
||||||
|
|
||||||
|
static float calculateTextChunkLength(SVGTextFragmentIterator begin, SVGTextFragmentIterator end, bool isVerticalText)
|
||||||
|
{
|
||||||
|
float chunkLength = 0;
|
||||||
|
const SVGTextFragment* lastFragment = nullptr;
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
const SVGTextFragment& fragment = *it;
|
||||||
|
chunkLength += isVerticalText ? fragment.height : fragment.width;
|
||||||
|
if(!lastFragment) {
|
||||||
|
lastFragment = &fragment;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isVerticalText) {
|
||||||
|
chunkLength += fragment.y - (lastFragment->y + lastFragment->height);
|
||||||
|
} else {
|
||||||
|
chunkLength += fragment.x - (lastFragment->x + lastFragment->width);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFragment = &fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleTextChunk(SVGTextFragmentIterator begin, SVGTextFragmentIterator end)
|
||||||
|
{
|
||||||
|
const SVGTextFragment& firstFragment = *begin;
|
||||||
|
const auto isVerticalText = firstFragment.element->isVerticalWritingMode();
|
||||||
|
if(firstFragment.element->hasAttribute(PropertyID::TextLength)) {
|
||||||
|
LengthContext lengthContext(firstFragment.element);
|
||||||
|
auto textLength = lengthContext.valueForLength(firstFragment.element->textLength());
|
||||||
|
auto chunkLength = calculateTextChunkLength(begin, end, isVerticalText);
|
||||||
|
if(textLength > 0.f && chunkLength > 0.f) {
|
||||||
|
size_t numCharacters = 0;
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
const SVGTextFragment& fragment = *it;
|
||||||
|
numCharacters += fragment.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(firstFragment.element->lengthAdjust() == LengthAdjust::SpacingAndGlyphs) {
|
||||||
|
auto textLengthScale = textLength / chunkLength;
|
||||||
|
auto lengthAdjustTransform = Transform::translated(firstFragment.x, firstFragment.y);
|
||||||
|
if(isVerticalText) {
|
||||||
|
lengthAdjustTransform.scale(1.f, textLengthScale);
|
||||||
|
} else {
|
||||||
|
lengthAdjustTransform.scale(textLengthScale, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
lengthAdjustTransform.translate(-firstFragment.x, -firstFragment.y);
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
SVGTextFragment& fragment = *it;
|
||||||
|
fragment.lengthAdjustTransform = lengthAdjustTransform;
|
||||||
|
}
|
||||||
|
} else if(numCharacters > 1) {
|
||||||
|
assert(firstFragment.element->lengthAdjust() == LengthAdjust::Spacing);
|
||||||
|
size_t characterOffset = 0;
|
||||||
|
auto textLengthShift = (textLength - chunkLength) / (numCharacters - 1);
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
SVGTextFragment& fragment = *it;
|
||||||
|
if(isVerticalText) {
|
||||||
|
fragment.y += textLengthShift * characterOffset;
|
||||||
|
} else {
|
||||||
|
fragment.x += textLengthShift * characterOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
characterOffset += fragment.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(needsTextAnchorAdjustment(firstFragment.element)) {
|
||||||
|
auto chunkLength = calculateTextChunkLength(begin, end, isVerticalText);
|
||||||
|
auto chunkOffset = calculateTextAnchorOffset(firstFragment.element, chunkLength);
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
SVGTextFragment& fragment = *it;
|
||||||
|
if(isVerticalText) {
|
||||||
|
fragment.y += chunkOffset;
|
||||||
|
} else {
|
||||||
|
fragment.x += chunkOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGTextFragmentsBuilder::SVGTextFragmentsBuilder(std::u32string& text, SVGTextFragmentList& fragments)
|
||||||
|
: m_text(text), m_fragments(fragments)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
m_fragments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextFragmentsBuilder::build(const SVGTextElement* textElement)
|
||||||
|
{
|
||||||
|
handleElement(textElement);
|
||||||
|
for(const auto& position : m_textPositions) {
|
||||||
|
fillCharacterPositions(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u32string_view wholeText(m_text);
|
||||||
|
for(const auto& textPosition : m_textPositions) {
|
||||||
|
if(!textPosition.node->isTextNode())
|
||||||
|
continue;
|
||||||
|
auto element = toSVGTextPositioningElement(textPosition.node->parentElement());
|
||||||
|
const auto isVerticalText = element->isVerticalWritingMode();
|
||||||
|
const auto isUprightText = element->isUprightTextOrientation();
|
||||||
|
const auto& font = element->font();
|
||||||
|
|
||||||
|
SVGTextFragment fragment(element);
|
||||||
|
auto recordTextFragment = [&](auto startOffset, auto endOffset) {
|
||||||
|
auto text = wholeText.substr(startOffset, endOffset - startOffset);
|
||||||
|
fragment.offset = startOffset;
|
||||||
|
fragment.length = text.length();
|
||||||
|
fragment.width = font.measureText(text);
|
||||||
|
fragment.height = font.height() + font.lineGap();
|
||||||
|
if(isVerticalText) {
|
||||||
|
m_y += isUprightText ? fragment.height : fragment.width;
|
||||||
|
} else {
|
||||||
|
m_x += fragment.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fragments.push_back(fragment);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto needsTextLengthSpacing = element->lengthAdjust() == LengthAdjust::Spacing && element->hasAttribute(PropertyID::TextLength);
|
||||||
|
auto baselineOffset = calculateBaselineOffset(element);
|
||||||
|
auto startOffset = textPosition.startOffset;
|
||||||
|
auto textOffset = textPosition.startOffset;
|
||||||
|
auto didStartTextFragment = false;
|
||||||
|
auto applySpacingToNextCharacter = false;
|
||||||
|
auto lastCharacter = 0u;
|
||||||
|
auto lastAngle = 0.f;
|
||||||
|
while(textOffset < textPosition.endOffset) {
|
||||||
|
SVGCharacterPosition characterPosition;
|
||||||
|
if(auto it = m_characterPositions.find(m_characterOffset); it != m_characterPositions.end()) {
|
||||||
|
characterPosition = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentCharacter = wholeText.at(textOffset);
|
||||||
|
auto angle = characterPosition.rotate.value_or(0);
|
||||||
|
auto dx = characterPosition.dx.value_or(0);
|
||||||
|
auto dy = characterPosition.dy.value_or(0);
|
||||||
|
|
||||||
|
auto shouldStartNewFragment = needsTextLengthSpacing || isVerticalText || applySpacingToNextCharacter
|
||||||
|
|| characterPosition.x || characterPosition.y || dx || dy || angle || angle != lastAngle;
|
||||||
|
if(shouldStartNewFragment && didStartTextFragment) {
|
||||||
|
recordTextFragment(startOffset, textOffset);
|
||||||
|
applySpacingToNextCharacter = false;
|
||||||
|
startOffset = textOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto startsNewTextChunk = (characterPosition.x || characterPosition.y) && textOffset == textPosition.startOffset;
|
||||||
|
if(startsNewTextChunk || shouldStartNewFragment || !didStartTextFragment) {
|
||||||
|
m_x = dx + characterPosition.x.value_or(m_x);
|
||||||
|
m_y = dy + characterPosition.y.value_or(m_y);
|
||||||
|
fragment.x = isVerticalText ? m_x + baselineOffset : m_x;
|
||||||
|
fragment.y = isVerticalText ? m_y : m_y - baselineOffset;
|
||||||
|
fragment.angle = angle;
|
||||||
|
if(isVerticalText) {
|
||||||
|
if(isUprightText) {
|
||||||
|
fragment.y += font.height();
|
||||||
|
} else {
|
||||||
|
fragment.angle += 90.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.startsNewTextChunk = startsNewTextChunk;
|
||||||
|
didStartTextFragment = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spacing = element->letter_spacing();
|
||||||
|
if(currentCharacter && lastCharacter && element->word_spacing()) {
|
||||||
|
if(currentCharacter == ' ' && lastCharacter != ' ') {
|
||||||
|
spacing += element->word_spacing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(spacing) {
|
||||||
|
applySpacingToNextCharacter = true;
|
||||||
|
if(isVerticalText) {
|
||||||
|
m_y += spacing;
|
||||||
|
} else {
|
||||||
|
m_x += spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastAngle = angle;
|
||||||
|
lastCharacter = currentCharacter;
|
||||||
|
++textOffset;
|
||||||
|
++m_characterOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordTextFragment(startOffset, textOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_fragments.empty())
|
||||||
|
return;
|
||||||
|
auto it = m_fragments.begin();
|
||||||
|
auto begin = m_fragments.begin();
|
||||||
|
auto end = m_fragments.end();
|
||||||
|
for(++it; it != end; ++it) {
|
||||||
|
const SVGTextFragment& fragment = *it;
|
||||||
|
if(!fragment.startsNewTextChunk)
|
||||||
|
continue;
|
||||||
|
handleTextChunk(begin, it);
|
||||||
|
begin = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTextChunk(begin, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextFragmentsBuilder::handleText(const SVGTextNode* node)
|
||||||
|
{
|
||||||
|
const auto& text = node->data();
|
||||||
|
if(text.empty())
|
||||||
|
return;
|
||||||
|
auto element = toSVGTextPositioningElement(node->parentElement());
|
||||||
|
const auto startOffset = m_text.length();
|
||||||
|
uint32_t lastCharacter = ' ';
|
||||||
|
if(!m_text.empty()) {
|
||||||
|
lastCharacter = m_text.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
plutovg_text_iterator_t it;
|
||||||
|
plutovg_text_iterator_init(&it, text.data(), text.length(), PLUTOVG_TEXT_ENCODING_UTF8);
|
||||||
|
while(plutovg_text_iterator_has_next(&it)) {
|
||||||
|
auto currentCharacter = plutovg_text_iterator_next(&it);
|
||||||
|
if(currentCharacter == '\t' || currentCharacter == '\n' || currentCharacter == '\r')
|
||||||
|
currentCharacter = ' ';
|
||||||
|
if(currentCharacter == ' ' && lastCharacter == ' ' && element->white_space() == WhiteSpace::Default)
|
||||||
|
continue;
|
||||||
|
m_text.push_back(currentCharacter);
|
||||||
|
lastCharacter = currentCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(startOffset < m_text.length()) {
|
||||||
|
m_textPositions.emplace_back(node, startOffset, m_text.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextFragmentsBuilder::handleElement(const SVGTextPositioningElement* element)
|
||||||
|
{
|
||||||
|
if(element->isDisplayNone())
|
||||||
|
return;
|
||||||
|
const auto itemIndex = m_textPositions.size();
|
||||||
|
m_textPositions.emplace_back(element, m_text.length(), m_text.length());
|
||||||
|
for(const auto& child : element->children()) {
|
||||||
|
if(child->isTextNode()) {
|
||||||
|
handleText(toSVGTextNode(child.get()));
|
||||||
|
} else if(child->isTextPositioningElement()) {
|
||||||
|
handleElement(toSVGTextPositioningElement(child.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& position = m_textPositions[itemIndex];
|
||||||
|
assert(position.node == element);
|
||||||
|
position.endOffset = m_text.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextFragmentsBuilder::fillCharacterPositions(const SVGTextPosition& position)
|
||||||
|
{
|
||||||
|
if(!position.node->isTextPositioningElement())
|
||||||
|
return;
|
||||||
|
auto element = toSVGTextPositioningElement(position.node);
|
||||||
|
const auto& xList = element->x();
|
||||||
|
const auto& yList = element->y();
|
||||||
|
const auto& dxList = element->dx();
|
||||||
|
const auto& dyList = element->dy();
|
||||||
|
const auto& rotateList = element->rotate();
|
||||||
|
|
||||||
|
auto xListSize = xList.size();
|
||||||
|
auto yListSize = yList.size();
|
||||||
|
auto dxListSize = dxList.size();
|
||||||
|
auto dyListSize = dyList.size();
|
||||||
|
auto rotateListSize = rotateList.size();
|
||||||
|
if(!xListSize && !yListSize && !dxListSize && !dyListSize && !rotateListSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LengthContext lengthContext(element);
|
||||||
|
std::optional<float> lastRotation;
|
||||||
|
for(auto offset = position.startOffset; offset < position.endOffset; ++offset) {
|
||||||
|
auto index = offset - position.startOffset;
|
||||||
|
if(index >= xListSize && index >= yListSize && index >= dxListSize && index >= dyListSize && index >= rotateListSize)
|
||||||
|
break;
|
||||||
|
auto& characterPosition = m_characterPositions[offset];
|
||||||
|
if(index < xListSize)
|
||||||
|
characterPosition.x = lengthContext.valueForLength(xList[index], LengthDirection::Horizontal);
|
||||||
|
if(index < yListSize)
|
||||||
|
characterPosition.y = lengthContext.valueForLength(yList[index], LengthDirection::Vertical);
|
||||||
|
if(index < dxListSize)
|
||||||
|
characterPosition.dx = lengthContext.valueForLength(dxList[index], LengthDirection::Horizontal);
|
||||||
|
if(index < dyListSize)
|
||||||
|
characterPosition.dy = lengthContext.valueForLength(dyList[index], LengthDirection::Vertical);
|
||||||
|
if(index < rotateListSize) {
|
||||||
|
characterPosition.rotate = rotateList[index];
|
||||||
|
lastRotation = characterPosition.rotate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastRotation == std::nullopt)
|
||||||
|
return;
|
||||||
|
auto offset = position.startOffset + rotateList.size();
|
||||||
|
while(offset < position.endOffset) {
|
||||||
|
m_characterPositions[offset++].rotate = lastRotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGTextPositioningElement::SVGTextPositioningElement(Document* document, ElementID id)
|
||||||
|
: SVGGraphicsElement(document, id)
|
||||||
|
, m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_dx(PropertyID::Dx, LengthDirection::Horizontal, LengthNegativeMode::Allow)
|
||||||
|
, m_dy(PropertyID::Dy, LengthDirection::Vertical, LengthNegativeMode::Allow)
|
||||||
|
, m_rotate(PropertyID::Rotate)
|
||||||
|
, m_textLength(PropertyID::TextLength, LengthDirection::Horizontal, LengthNegativeMode::Forbid)
|
||||||
|
, m_lengthAdjust(PropertyID::LengthAdjust, LengthAdjust::Spacing)
|
||||||
|
{
|
||||||
|
addProperty(m_x);
|
||||||
|
addProperty(m_y);
|
||||||
|
addProperty(m_dx);
|
||||||
|
addProperty(m_dy);
|
||||||
|
addProperty(m_rotate);
|
||||||
|
addProperty(m_textLength);
|
||||||
|
addProperty(m_lengthAdjust);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextPositioningElement::layoutElement(const SVGLayoutState& state)
|
||||||
|
{
|
||||||
|
m_font = state.font();
|
||||||
|
m_fill = getPaintServer(state.fill(), state.fill_opacity());
|
||||||
|
m_stroke = getPaintServer(state.stroke(), state.stroke_opacity());
|
||||||
|
SVGGraphicsElement::layoutElement(state);
|
||||||
|
|
||||||
|
LengthContext lengthContext(this);
|
||||||
|
m_stroke_width = lengthContext.valueForLength(state.stroke_width(), LengthDirection::Diagonal);
|
||||||
|
m_letter_spacing = lengthContext.valueForLength(state.letter_spacing(), LengthDirection::Diagonal);
|
||||||
|
m_word_spacing = lengthContext.valueForLength(state.word_spacing(), LengthDirection::Diagonal);
|
||||||
|
|
||||||
|
m_baseline_offset = convertBaselineOffset(state.baseline_shift());
|
||||||
|
m_alignment_baseline = state.alignment_baseline();
|
||||||
|
m_dominant_baseline = state.dominant_baseline();
|
||||||
|
m_text_anchor = state.text_anchor();
|
||||||
|
m_white_space = state.white_space();
|
||||||
|
m_writing_mode = state.writing_mode();
|
||||||
|
m_text_orientation = state.text_orientation();
|
||||||
|
m_direction = state.direction();
|
||||||
|
}
|
||||||
|
|
||||||
|
float SVGTextPositioningElement::convertBaselineOffset(const BaselineShift& baselineShift) const
|
||||||
|
{
|
||||||
|
if(baselineShift.type() == BaselineShift::Type::Baseline)
|
||||||
|
return 0.f;
|
||||||
|
if(baselineShift.type() == BaselineShift::Type::Sub)
|
||||||
|
return -m_font.height() / 2.f;
|
||||||
|
if(baselineShift.type() == BaselineShift::Type::Super) {
|
||||||
|
return m_font.height() / 2.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& length = baselineShift.length();
|
||||||
|
if(length.units() == LengthUnits::Percent)
|
||||||
|
return length.value() * m_font.size() / 100.f;
|
||||||
|
if(length.units() == LengthUnits::Ex)
|
||||||
|
return length.value() * m_font.size() / 2.f;
|
||||||
|
if(length.units() == LengthUnits::Em)
|
||||||
|
return length.value() * m_font.size();
|
||||||
|
return length.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGTSpanElement::SVGTSpanElement(Document* document)
|
||||||
|
: SVGTextPositioningElement(document, ElementID::Tspan)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGTextElement::SVGTextElement(Document* document)
|
||||||
|
: SVGTextPositioningElement(document, ElementID::Text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextElement::layout(SVGLayoutState& state)
|
||||||
|
{
|
||||||
|
SVGTextPositioningElement::layout(state);
|
||||||
|
SVGTextFragmentsBuilder(m_text, m_fragments).build(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVGTextElement::render(SVGRenderState& state) const
|
||||||
|
{
|
||||||
|
if(m_fragments.empty() || isVisibilityHidden() || isDisplayNone())
|
||||||
|
return;
|
||||||
|
SVGBlendInfo blendInfo(this);
|
||||||
|
SVGRenderState newState(this, state, localTransform());
|
||||||
|
newState.beginGroup(blendInfo);
|
||||||
|
if(newState.mode() == SVGRenderMode::Clipping) {
|
||||||
|
newState->setColor(Color::White);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u32string_view wholeText(m_text);
|
||||||
|
for(const auto& fragment : m_fragments) {
|
||||||
|
if(fragment.element->isVisibilityHidden())
|
||||||
|
continue;
|
||||||
|
auto transform = newState.currentTransform() * Transform::rotated(fragment.angle, fragment.x, fragment.y) * fragment.lengthAdjustTransform;
|
||||||
|
auto text = wholeText.substr(fragment.offset, fragment.length);
|
||||||
|
auto origin = Point(fragment.x, fragment.y);
|
||||||
|
|
||||||
|
const auto& font = fragment.element->font();
|
||||||
|
if(newState.mode() == SVGRenderMode::Clipping) {
|
||||||
|
newState->fillText(text, font, origin, transform);
|
||||||
|
} else {
|
||||||
|
const auto& fill = fragment.element->fill();
|
||||||
|
const auto& stroke = fragment.element->stroke();
|
||||||
|
auto stroke_width = fragment.element->stroke_width();
|
||||||
|
if(fill.applyPaint(newState))
|
||||||
|
newState->fillText(text, font, origin, transform);
|
||||||
|
if(stroke.applyPaint(newState)) {
|
||||||
|
newState->strokeText(text, stroke_width, font, origin, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.endGroup(blendInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect SVGTextElement::boundingBox(bool includeStroke) const
|
||||||
|
{
|
||||||
|
auto boundingBox = Rect::Invalid;
|
||||||
|
for(const auto& fragment : m_fragments) {
|
||||||
|
const auto& font = fragment.element->font();
|
||||||
|
const auto& stroke = fragment.element->stroke();
|
||||||
|
auto fragmentTranform = Transform::rotated(fragment.angle, fragment.x, fragment.y) * fragment.lengthAdjustTransform;
|
||||||
|
auto fragmentRect = Rect(fragment.x, fragment.y - font.ascent(), fragment.width, fragment.height);
|
||||||
|
if(includeStroke && stroke.isRenderable())
|
||||||
|
fragmentRect.inflate(fragment.element->stroke_width() / 2.f);
|
||||||
|
boundingBox.unite(fragmentTranform.mapRect(fragmentRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!boundingBox.isValid())
|
||||||
|
boundingBox = Rect::Empty;
|
||||||
|
return boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
155
vendor/lunasvg/source/svgtextelement.h
vendored
Normal file
155
vendor/lunasvg/source/svgtextelement.h
vendored
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#ifndef LUNASVG_SVGTEXTELEMENT_H
|
||||||
|
#define LUNASVG_SVGTEXTELEMENT_H
|
||||||
|
|
||||||
|
#include "svgelement.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace lunasvg {
|
||||||
|
|
||||||
|
class SVGTextPositioningElement;
|
||||||
|
class SVGTextElement;
|
||||||
|
|
||||||
|
struct SVGCharacterPosition {
|
||||||
|
std::optional<float> x;
|
||||||
|
std::optional<float> y;
|
||||||
|
std::optional<float> dx;
|
||||||
|
std::optional<float> dy;
|
||||||
|
std::optional<float> rotate;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SVGCharacterPositions = std::map<size_t, SVGCharacterPosition>;
|
||||||
|
|
||||||
|
struct SVGTextPosition {
|
||||||
|
SVGTextPosition(const SVGNode* node, size_t startOffset, size_t endOffset)
|
||||||
|
: node(node), startOffset(startOffset), endOffset(endOffset)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const SVGNode* node;
|
||||||
|
size_t startOffset;
|
||||||
|
size_t endOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SVGTextPositionList = std::vector<SVGTextPosition>;
|
||||||
|
|
||||||
|
struct SVGTextFragment {
|
||||||
|
explicit SVGTextFragment(const SVGTextPositioningElement* element) : element(element) {}
|
||||||
|
const SVGTextPositioningElement* element;
|
||||||
|
Transform lengthAdjustTransform;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t length = 0;
|
||||||
|
bool startsNewTextChunk = false;
|
||||||
|
float x = 0;
|
||||||
|
float y = 0;
|
||||||
|
float width = 0;
|
||||||
|
float height = 0;
|
||||||
|
float angle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SVGTextFragmentList = std::vector<SVGTextFragment>;
|
||||||
|
|
||||||
|
class SVGTextFragmentsBuilder {
|
||||||
|
public:
|
||||||
|
SVGTextFragmentsBuilder(std::u32string& text, SVGTextFragmentList& fragments);
|
||||||
|
|
||||||
|
void build(const SVGTextElement* textElement);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleText(const SVGTextNode* node);
|
||||||
|
void handleElement(const SVGTextPositioningElement* element);
|
||||||
|
void fillCharacterPositions(const SVGTextPosition& position);
|
||||||
|
std::u32string& m_text;
|
||||||
|
SVGTextFragmentList& m_fragments;
|
||||||
|
SVGCharacterPositions m_characterPositions;
|
||||||
|
SVGTextPositionList m_textPositions;
|
||||||
|
size_t m_characterOffset = 0;
|
||||||
|
float m_x = 0;
|
||||||
|
float m_y = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGTextPositioningElement : public SVGGraphicsElement {
|
||||||
|
public:
|
||||||
|
SVGTextPositioningElement(Document* document, ElementID id);
|
||||||
|
|
||||||
|
bool isTextPositioningElement() const final { return true; }
|
||||||
|
|
||||||
|
const LengthList& x() const { return m_x.values(); }
|
||||||
|
const LengthList& y() const { return m_y.values(); }
|
||||||
|
const LengthList& dx() const { return m_dx.values(); }
|
||||||
|
const LengthList& dy() const { return m_dy.values(); }
|
||||||
|
const NumberList& rotate() const { return m_rotate.values(); }
|
||||||
|
|
||||||
|
const SVGLength& textLength() const { return m_textLength; }
|
||||||
|
LengthAdjust lengthAdjust() const { return m_lengthAdjust.value(); }
|
||||||
|
|
||||||
|
const Font& font() const { return m_font; }
|
||||||
|
const SVGPaintServer& fill() const { return m_fill; }
|
||||||
|
const SVGPaintServer& stroke() const { return m_stroke; }
|
||||||
|
|
||||||
|
bool isVerticalWritingMode() const { return m_writing_mode == WritingMode::Vertical; }
|
||||||
|
bool isUprightTextOrientation() const { return m_text_orientation == TextOrientation::Upright; }
|
||||||
|
|
||||||
|
float stroke_width() const { return m_stroke_width; }
|
||||||
|
float letter_spacing() const { return m_letter_spacing; }
|
||||||
|
float word_spacing() const { return m_word_spacing; }
|
||||||
|
float baseline_offset() const { return m_baseline_offset; }
|
||||||
|
AlignmentBaseline alignment_baseline() const { return m_alignment_baseline; }
|
||||||
|
DominantBaseline dominant_baseline() const { return m_dominant_baseline; }
|
||||||
|
TextAnchor text_anchor() const { return m_text_anchor; }
|
||||||
|
WhiteSpace white_space() const { return m_white_space; }
|
||||||
|
Direction direction() const { return m_direction; }
|
||||||
|
|
||||||
|
void layoutElement(const SVGLayoutState& state) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float convertBaselineOffset(const BaselineShift& baselineShift) const;
|
||||||
|
SVGLengthList m_x;
|
||||||
|
SVGLengthList m_y;
|
||||||
|
SVGLengthList m_dx;
|
||||||
|
SVGLengthList m_dy;
|
||||||
|
SVGNumberList m_rotate;
|
||||||
|
|
||||||
|
SVGLength m_textLength;
|
||||||
|
SVGEnumeration<LengthAdjust> m_lengthAdjust;
|
||||||
|
|
||||||
|
Font m_font;
|
||||||
|
SVGPaintServer m_fill;
|
||||||
|
SVGPaintServer m_stroke;
|
||||||
|
|
||||||
|
float m_stroke_width = 1.f;
|
||||||
|
float m_letter_spacing = 0.f;
|
||||||
|
float m_word_spacing = 0.f;
|
||||||
|
float m_baseline_offset = 0.f;
|
||||||
|
AlignmentBaseline m_alignment_baseline = AlignmentBaseline::Auto;
|
||||||
|
DominantBaseline m_dominant_baseline = DominantBaseline::Auto;
|
||||||
|
TextAnchor m_text_anchor = TextAnchor::Start;
|
||||||
|
WhiteSpace m_white_space = WhiteSpace::Default;
|
||||||
|
WritingMode m_writing_mode = WritingMode::Horizontal;
|
||||||
|
TextOrientation m_text_orientation = TextOrientation::Mixed;
|
||||||
|
Direction m_direction = Direction::Ltr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGTSpanElement final : public SVGTextPositioningElement {
|
||||||
|
public:
|
||||||
|
SVGTSpanElement(Document* document);
|
||||||
|
};
|
||||||
|
|
||||||
|
class SVGTextElement final : public SVGTextPositioningElement {
|
||||||
|
public:
|
||||||
|
SVGTextElement(Document* document);
|
||||||
|
|
||||||
|
Rect fillBoundingBox() const final { return boundingBox(false); }
|
||||||
|
Rect strokeBoundingBox() const final { return boundingBox(true); }
|
||||||
|
|
||||||
|
void layout(SVGLayoutState& state) final;
|
||||||
|
void render(SVGRenderState& state) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rect boundingBox(bool includeStroke) const;
|
||||||
|
SVGTextFragmentList m_fragments;
|
||||||
|
std::u32string m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lunasvg
|
||||||
|
|
||||||
|
#endif // LUNASVG_SVGTEXTELEMENT_H
|
||||||
Reference in New Issue
Block a user