From 8695f69282ba44fa0789613b8ace8227cd0b6e53 Mon Sep 17 00:00:00 2001 From: Max Amundsen Date: Thu, 12 Mar 2026 16:30:04 -0400 Subject: [PATCH] Reapply "Death to C++" This reverts commit 13f856cfbce5f777947940f73361d4b45343802a. --- .claude/settings.local.json | 4 +- build.c | 274 +- src/audio/audio.h | 6 +- src/audio/{audio_asio.cpp => audio_asio.c} | 171 +- ...{audio_coreaudio.cpp => audio_coreaudio.c} | 55 +- src/base/{base_arena.cpp => base_arena.c} | 6 +- src/base/base_arena.h | 12 +- src/base/base_core.h | 3 +- src/base/base_inc.c | 3 + src/base/base_inc.cpp | 3 - src/base/base_math.h | 66 +- src/base/{base_strings.cpp => base_strings.c} | 10 +- src/base/base_strings.h | 26 +- src/{main.cpp => main.c} | 106 +- src/{menus.cpp => menus.c} | 8 +- src/midi/midi.h | 8 +- .../{midi_coremidi.cpp => midi_coremidi.c} | 16 +- src/midi/{midi_win32.cpp => midi_win32.c} | 22 +- src/platform/platform.h | 57 +- .../{platform_macos.mm => platform_macos.m} | 21 +- .../{platform_win32.cpp => platform_win32.c} | 68 +- src/renderer/renderer.h | 13 +- .../{renderer_metal.mm => renderer_metal.m} | 331 +- ...{renderer_vulkan.cpp => renderer_vulkan.c} | 225 +- src/ui/{ui_core.cpp => ui_core.c} | 138 +- src/ui/ui_core.h | 24 +- src/ui/ui_icons.c | 219 + src/ui/ui_icons.cpp | 202 - src/ui/ui_icons.h | 10 +- src/ui/{ui_piano.cpp => ui_piano.c} | 6 +- src/ui/ui_piano.h | 4 +- src/ui/{ui_popups.cpp => ui_popups.c} | 24 +- src/ui/ui_popups.h | 8 +- src/ui/{ui_widgets.cpp => ui_widgets.c} | 87 +- src/ui/ui_widgets.h | 16 +- vendor/clay/clay.h | 25 +- vendor/lunasvg/LICENSE | 21 - vendor/lunasvg/include/lunasvg.h | 791 -- vendor/lunasvg/plutovg/include/plutovg.h | 2548 ------ vendor/lunasvg/plutovg/source/plutovg-blend.c | 1144 --- .../lunasvg/plutovg/source/plutovg-canvas.c | 759 -- vendor/lunasvg/plutovg/source/plutovg-font.c | 1065 --- .../lunasvg/plutovg/source/plutovg-ft-math.c | 441 - .../lunasvg/plutovg/source/plutovg-ft-math.h | 436 - .../plutovg/source/plutovg-ft-raster.c | 1889 ---- .../plutovg/source/plutovg-ft-raster.h | 420 - .../plutovg/source/plutovg-ft-stroker.c | 1873 ---- .../plutovg/source/plutovg-ft-stroker.h | 320 - .../lunasvg/plutovg/source/plutovg-ft-types.h | 176 - .../lunasvg/plutovg/source/plutovg-matrix.c | 229 - vendor/lunasvg/plutovg/source/plutovg-paint.c | 489 - vendor/lunasvg/plutovg/source/plutovg-path.c | 918 -- .../lunasvg/plutovg/source/plutovg-private.h | 180 - .../plutovg/source/plutovg-rasterize.c | 394 - .../plutovg/source/plutovg-stb-image-write.h | 1724 ---- .../plutovg/source/plutovg-stb-image.h | 7988 ----------------- .../plutovg/source/plutovg-stb-truetype.h | 5077 ----------- .../lunasvg/plutovg/source/plutovg-surface.c | 295 - vendor/lunasvg/plutovg/source/plutovg-utils.h | 211 - vendor/lunasvg/source/graphics.cpp | 693 -- vendor/lunasvg/source/graphics.h | 562 -- vendor/lunasvg/source/lunasvg.cpp | 536 -- vendor/lunasvg/source/svgelement.cpp | 1225 --- vendor/lunasvg/source/svgelement.h | 498 - vendor/lunasvg/source/svggeometryelement.cpp | 324 - vendor/lunasvg/source/svggeometryelement.h | 139 - vendor/lunasvg/source/svglayoutstate.cpp | 607 -- vendor/lunasvg/source/svglayoutstate.h | 129 - vendor/lunasvg/source/svgpaintelement.cpp | 364 - vendor/lunasvg/source/svgpaintelement.h | 288 - vendor/lunasvg/source/svgparser.cpp | 956 -- vendor/lunasvg/source/svgparserutils.h | 210 - vendor/lunasvg/source/svgproperty.cpp | 705 -- vendor/lunasvg/source/svgproperty.h | 570 -- vendor/lunasvg/source/svgrenderstate.cpp | 61 - vendor/lunasvg/source/svgrenderstate.h | 69 - vendor/lunasvg/source/svgtextelement.cpp | 579 -- vendor/lunasvg/source/svgtextelement.h | 155 - vendor/nanosvg/nanosvg.h | 3132 +++++++ vendor/nanosvg/nanosvgrast.h | 1472 +++ 80 files changed, 5671 insertions(+), 39268 deletions(-) rename src/audio/{audio_asio.cpp => audio_asio.c} (76%) rename src/audio/{audio_coreaudio.cpp => audio_coreaudio.c} (89%) rename src/base/{base_arena.cpp => base_arena.c} (93%) create mode 100644 src/base/base_inc.c delete mode 100644 src/base/base_inc.cpp rename src/base/{base_strings.cpp => base_strings.c} (78%) rename src/{main.cpp => main.c} (97%) rename src/{menus.cpp => menus.c} (94%) rename src/midi/{midi_coremidi.cpp => midi_coremidi.c} (96%) rename src/midi/{midi_win32.cpp => midi_win32.c} (95%) rename src/platform/{platform_macos.mm => platform_macos.m} (98%) rename src/platform/{platform_win32.cpp => platform_win32.c} (89%) rename src/renderer/{renderer_metal.mm => renderer_metal.m} (83%) rename src/renderer/{renderer_vulkan.cpp => renderer_vulkan.c} (89%) rename src/ui/{ui_core.cpp => ui_core.c} (56%) create mode 100644 src/ui/ui_icons.c delete mode 100644 src/ui/ui_icons.cpp rename src/ui/{ui_piano.cpp => ui_piano.c} (96%) rename src/ui/{ui_popups.cpp => ui_popups.c} (91%) rename src/ui/{ui_widgets.cpp => ui_widgets.c} (96%) delete mode 100644 vendor/lunasvg/LICENSE delete mode 100644 vendor/lunasvg/include/lunasvg.h delete mode 100644 vendor/lunasvg/plutovg/include/plutovg.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-blend.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-canvas.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-font.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-math.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-math.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-raster.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-raster.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-ft-types.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-matrix.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-paint.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-path.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-private.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-rasterize.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-stb-image.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-surface.c delete mode 100644 vendor/lunasvg/plutovg/source/plutovg-utils.h delete mode 100644 vendor/lunasvg/source/graphics.cpp delete mode 100644 vendor/lunasvg/source/graphics.h delete mode 100644 vendor/lunasvg/source/lunasvg.cpp delete mode 100644 vendor/lunasvg/source/svgelement.cpp delete mode 100644 vendor/lunasvg/source/svgelement.h delete mode 100644 vendor/lunasvg/source/svggeometryelement.cpp delete mode 100644 vendor/lunasvg/source/svggeometryelement.h delete mode 100644 vendor/lunasvg/source/svglayoutstate.cpp delete mode 100644 vendor/lunasvg/source/svglayoutstate.h delete mode 100644 vendor/lunasvg/source/svgpaintelement.cpp delete mode 100644 vendor/lunasvg/source/svgpaintelement.h delete mode 100644 vendor/lunasvg/source/svgparser.cpp delete mode 100644 vendor/lunasvg/source/svgparserutils.h delete mode 100644 vendor/lunasvg/source/svgproperty.cpp delete mode 100644 vendor/lunasvg/source/svgproperty.h delete mode 100644 vendor/lunasvg/source/svgrenderstate.cpp delete mode 100644 vendor/lunasvg/source/svgrenderstate.h delete mode 100644 vendor/lunasvg/source/svgtextelement.cpp delete mode 100644 vendor/lunasvg/source/svgtextelement.h create mode 100644 vendor/nanosvg/nanosvg.h create mode 100644 vendor/nanosvg/nanosvgrast.h diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 7f8e07f..5fb32a4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,9 @@ "Bash(cd /c/Users/mta/projects/autosample && ./nob.exe debug 2>&1)", "Bash(cd /c/Users/mta/projects/autosample && rm -f build/*.pdb build/*.obj build/*.ilk && ./nob.exe debug 2>&1)", "Bash(cd /c/Users/mta/projects/autosample && ./nob.exe 2>&1)", - "Bash(cd:*)" + "Bash(cd:*)", + "Bash(wc:*)", + "Bash(./build.exe:*)" ] } } diff --git a/build.c b/build.c index 56c1f7f..ff36de2 100644 --- a/build.c +++ b/build.c @@ -6,36 +6,6 @@ #define NOB_IMPLEMENTATION #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", -}; - //////////////////////////////// // FreeType source files @@ -95,7 +65,7 @@ static bool embed_font_file(const char *ttf_path, const char *header_path) { #ifdef __APPLE__ //////////////////////////////// -// macOS build (clang++ with Objective-C++) +// macOS build (clang with Objective-C) static const char *frameworks[] = { "-framework", "Metal", @@ -107,128 +77,6 @@ static const char *frameworks[] = { "-framework", "CoreFoundation", }; -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 = "vendor/lunasvg/liblunasvg.a"; - - // 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, "-DPLUTOVG_BUILD", "-DPLUTOVG_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", "-DPLUTOVG_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; -} - static bool build_freetype_lib(const char *build_dir, bool debug) { const char *obj_dir = nob_temp_sprintf("%s/freetype_obj", build_dir); const char *lib_path = "vendor/freetype/libfreetype.a"; @@ -320,7 +168,6 @@ int main(int argc, char **argv) { Nob_Cmd_Opt opt = {0}; nob_cmd_run_opt(&cmd, opt); } { Nob_Cmd cmd = {0}; nob_cmd_append(&cmd, "rm", "-rf", "build_release"); Nob_Cmd_Opt opt = {0}; nob_cmd_run_opt(&cmd, opt); } - remove("vendor/lunasvg/liblunasvg.a"); remove("vendor/freetype/libfreetype.a"); remove("src/renderer/font_inter.gen.h"); return 0; @@ -340,25 +187,22 @@ int main(int argc, char **argv) { if (!nob_mkdir_if_not_exists(res_dir)) return 1; // Build static libraries - if (!build_lunasvg_lib(build_dir, debug)) return 1; if (!build_freetype_lib(build_dir, debug)) return 1; // Generate embedded font header if (!embed_font_file("assets/fonts/Inter-Regular.ttf", "src/renderer/font_inter.gen.h")) return 1; - // Unity build: single clang++ invocation compiles main.cpp (which #includes everything) + // Unity build: single clang invocation compiles main.c (which #includes everything) { Nob_Cmd cmd = {0}; - nob_cmd_append(&cmd, "clang++"); - nob_cmd_append(&cmd, "-std=c++20", "-x", "objective-c++"); - nob_cmd_append(&cmd, "-fno-exceptions", "-fno-rtti"); + nob_cmd_append(&cmd, "clang"); + nob_cmd_append(&cmd, "-std=c11", "-x", "objective-c"); nob_cmd_append(&cmd, "-Wall", "-Wextra", "-Wno-missing-field-initializers"); nob_cmd_append(&cmd, "-Wno-deprecated-declarations"); nob_cmd_append(&cmd, "-Isrc", "-Ivendor/clay"); - nob_cmd_append(&cmd, "-Ivendor/lunasvg/include"); + nob_cmd_append(&cmd, "-Ivendor/nanosvg"); nob_cmd_append(&cmd, "-Ivendor/freetype/include"); - nob_cmd_append(&cmd, "-DLUNASVG_BUILD_STATIC"); if (debug) { nob_cmd_append(&cmd, "-g", "-O0", "-D_DEBUG"); @@ -367,11 +211,10 @@ int main(int argc, char **argv) { } nob_cmd_append(&cmd, "-o", binary_path); - nob_cmd_append(&cmd, "src/main.cpp"); + nob_cmd_append(&cmd, "src/main.c"); // Reset language mode so .a is treated as a library, not source nob_cmd_append(&cmd, "-x", "none"); - nob_cmd_append(&cmd, "vendor/lunasvg/liblunasvg.a"); nob_cmd_append(&cmd, "vendor/freetype/libfreetype.a"); { @@ -379,7 +222,7 @@ int main(int argc, char **argv) { for (i = 0; i < NOB_ARRAY_LEN(frameworks); i++) nob_cmd_append(&cmd, frameworks[i]); } - nob_cmd_append(&cmd, "-lstdc++"); + nob_cmd_append(&cmd, "-lm"); { Nob_Cmd_Opt opt = {0}; if (!nob_cmd_run_opt(&cmd, opt)) return 1; } } @@ -534,92 +377,6 @@ static bool compile_and_embed_shaders(const char *build_dir) { return true; } -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 = debug ? "vendor\\lunasvg\\lunasvg_d.lib" : "vendor\\lunasvg\\lunasvg.lib"; - - // 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, "/DPLUTOVG_BUILD", "/DPLUTOVG_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", "/DPLUTOVG_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; -} - static bool build_freetype_lib(const char *build_dir, bool debug) { const char *obj_dir = nob_temp_sprintf("%s\\freetype_obj", build_dir); const char *lib_path = debug ? "vendor\\freetype\\freetype_d.lib" : "vendor\\freetype\\freetype.lib"; @@ -640,7 +397,7 @@ static bool build_freetype_lib(const char *build_dir, bool debug) { nob_cmd_append(&cmd, "/Ivendor/freetype/include"); nob_cmd_append(&cmd, "/W3"); if (debug) { - nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od"); + nob_cmd_append(&cmd, "/MTd", "/Zi"); } else { nob_cmd_append(&cmd, "/MT", "/O2"); } @@ -697,8 +454,6 @@ int main(int argc, char **argv) { { Nob_Cmd cmd = {0}; nob_cmd_append(&cmd, "cmd.exe", "/c", "if exist build_release rmdir /s /q build_release"); Nob_Cmd_Opt opt = {0}; nob_cmd_run_opt(&cmd, opt); } - remove("vendor\\lunasvg\\lunasvg.lib"); - remove("vendor\\lunasvg\\lunasvg_d.lib"); remove("vendor\\freetype\\freetype.lib"); remove("vendor\\freetype\\freetype_d.lib"); remove("src\\renderer\\font_inter.gen.h"); @@ -710,7 +465,6 @@ int main(int argc, char **argv) { if (!nob_mkdir_if_not_exists(build_dir)) return 1; // Build static libraries - if (!build_lunasvg_lib(build_dir, debug)) return 1; if (!build_freetype_lib(build_dir, debug)) return 1; // Generate embedded font header @@ -724,19 +478,18 @@ int main(int argc, char **argv) { const char *vk_include = nob_temp_sprintf("/I%s/Include", vk_sdk); const char *vk_lib = nob_temp_sprintf("%s/Lib/vulkan-1.lib", vk_sdk); - // Unity build: single cl.exe invocation compiles main.cpp (which #includes everything including the vulkan renderer) + // Unity build: single cl.exe invocation compiles main.c (which #includes everything) { Nob_Cmd cmd = {0}; nob_cmd_append(&cmd, "cl.exe"); - nob_cmd_append(&cmd, "/nologo", "/std:c++20", "/EHsc", "/W3"); + nob_cmd_append(&cmd, "/nologo", "/std:c11", "/TC", "/W3"); nob_cmd_append(&cmd, "/Isrc", "/Ivendor/clay"); - nob_cmd_append(&cmd, "/Ivendor/lunasvg/include"); + nob_cmd_append(&cmd, "/Ivendor/nanosvg"); nob_cmd_append(&cmd, "/Ivendor/freetype/include"); nob_cmd_append(&cmd, vk_include); - nob_cmd_append(&cmd, "/DLUNASVG_BUILD_STATIC"); if (debug) { - nob_cmd_append(&cmd, "/MTd", "/Zi", "/Od", "/D_DEBUG"); + nob_cmd_append(&cmd, "/MTd", "/Zi", "/D_DEBUG"); } else { nob_cmd_append(&cmd, "/MT", "/Zi", "/O2", "/DNDEBUG"); } @@ -745,14 +498,13 @@ int main(int argc, char **argv) { nob_cmd_append(&cmd, nob_temp_sprintf("/Fo:%s/", build_dir)); nob_cmd_append(&cmd, nob_temp_sprintf("/Fd:%s/autosample.pdb", build_dir)); - nob_cmd_append(&cmd, "src/main.cpp"); + nob_cmd_append(&cmd, "src/main.c"); nob_cmd_append(&cmd, "/link"); nob_cmd_append(&cmd, "/MACHINE:X64"); nob_cmd_append(&cmd, "/SUBSYSTEM:CONSOLE"); nob_cmd_append(&cmd, nob_temp_sprintf("/PDB:%s/autosample.pdb", build_dir)); nob_cmd_append(&cmd, "/DEBUG"); - nob_cmd_append(&cmd, debug ? "vendor/lunasvg/lunasvg_d.lib" : "vendor/lunasvg/lunasvg.lib"); nob_cmd_append(&cmd, debug ? "vendor/freetype/freetype_d.lib" : "vendor/freetype/freetype.lib"); nob_cmd_append(&cmd, vk_lib); { diff --git a/src/audio/audio.h b/src/audio/audio.h index 7a49b8c..1f3ef2c 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -2,12 +2,12 @@ #include "base/base_core.h" -struct AudioEngine; +typedef struct AudioEngine AudioEngine; -struct AudioDeviceInfo { +typedef struct AudioDeviceInfo { char name[128]; S32 id; // index into engine's device list -}; +} AudioDeviceInfo; AudioEngine *audio_create(void *hwnd); void audio_destroy(AudioEngine *engine); diff --git a/src/audio/audio_asio.cpp b/src/audio/audio_asio.c similarity index 76% rename from src/audio/audio_asio.cpp rename to src/audio/audio_asio.c index b02dd45..25951ee 100644 --- a/src/audio/audio_asio.cpp +++ b/src/audio/audio_asio.c @@ -30,7 +30,7 @@ enum { ASE_NoMemory = -994, }; -enum ASIOSampleType { +typedef enum ASIOSampleType { ASIOSTInt16MSB = 0, ASIOSTInt24MSB = 1, ASIOSTInt32MSB = 2, @@ -52,59 +52,59 @@ enum ASIOSampleType { ASIOSTInt32LSB18 = 25, ASIOSTInt32LSB20 = 26, ASIOSTInt32LSB24 = 27, -}; +} ASIOSampleType; -struct ASIOClockSource { +typedef struct ASIOClockSource { long index; long channel; long group; ASIOBool isCurrentSource; char name[32]; -}; +} ASIOClockSource; -struct ASIOChannelInfo { +typedef struct ASIOChannelInfo { long channel; ASIOBool isInput; ASIOBool isActive; long channelGroup; ASIOSampleType type; char name[32]; -}; +} ASIOChannelInfo; -struct ASIOBufferInfo { +typedef struct ASIOBufferInfo { ASIOBool isInput; long channelNum; void *buffers[2]; // F64 buffer -}; +} ASIOBufferInfo; -struct ASIOTimeCode { +typedef struct ASIOTimeCode { F64 speed; ASIOSamples timeCodeSamples; unsigned long flags; char future[64]; -}; +} ASIOTimeCode; -struct AsioTimeInfo { +typedef struct AsioTimeInfo { F64 speed; ASIOTimeStamp systemTime; ASIOSamples samplePosition; F64 sampleRate; unsigned long flags; char reserved[12]; -}; +} AsioTimeInfo; -struct ASIOTime { +typedef struct ASIOTime { long reserved[4]; AsioTimeInfo timeInfo; ASIOTimeCode timeCode; -}; +} ASIOTime; -struct ASIOCallbacks { +typedef struct ASIOCallbacks { void (*bufferSwitch)(long doubleBufferIndex, ASIOBool directProcess); void (*sampleRateDidChange)(F64 sRate); long (*asioMessage)(long selector, long value, void *message, F64 *opt); ASIOTime *(*bufferSwitchTimeInfo)(ASIOTime *params, long doubleBufferIndex, ASIOBool directProcess); -}; +} ASIOCallbacks; // ASIO message selectors enum { @@ -120,41 +120,49 @@ enum { }; //////////////////////////////// -// IASIO COM interface +// IASIO COM interface (C vtable) // Standard ASIO vtable — inherits IUnknown -class IASIO : public IUnknown { -public: - virtual ASIOBool init(void *sysHandle) = 0; - virtual void getDriverName(char *name) = 0; - virtual long getDriverVersion() = 0; - virtual void getErrorMessage(char *string) = 0; - virtual ASIOError start() = 0; - virtual ASIOError stop() = 0; - virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0; - virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0; - virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0; - virtual ASIOError canSampleRate(F64 sampleRate) = 0; - virtual ASIOError getSampleRate(F64 *sampleRate) = 0; - virtual ASIOError setSampleRate(F64 sampleRate) = 0; - virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0; - virtual ASIOError setClockSource(long reference) = 0; - virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0; - virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0; - virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) = 0; - virtual ASIOError disposeBuffers() = 0; - virtual ASIOError controlPanel() = 0; - virtual ASIOError future(long selector, void *opt) = 0; - virtual ASIOError outputReady() = 0; -}; +typedef struct IASIOVtbl { + // IUnknown + HRESULT (STDMETHODCALLTYPE *QueryInterface)(void *this_, REFIID riid, void **ppvObject); + ULONG (STDMETHODCALLTYPE *AddRef)(void *this_); + ULONG (STDMETHODCALLTYPE *Release)(void *this_); + // IASIO + ASIOBool (*init)(void *this_, void *sysHandle); + void (*getDriverName)(void *this_, char *name); + long (*getDriverVersion)(void *this_); + void (*getErrorMessage)(void *this_, char *string); + ASIOError (*start)(void *this_); + ASIOError (*stop)(void *this_); + ASIOError (*getChannels)(void *this_, long *numInputChannels, long *numOutputChannels); + ASIOError (*getLatencies)(void *this_, long *inputLatency, long *outputLatency); + ASIOError (*getBufferSize)(void *this_, long *minSize, long *maxSize, long *preferredSize, long *granularity); + ASIOError (*canSampleRate)(void *this_, F64 sampleRate); + ASIOError (*getSampleRate)(void *this_, F64 *sampleRate); + ASIOError (*setSampleRate)(void *this_, F64 sampleRate); + ASIOError (*getClockSources)(void *this_, ASIOClockSource *clocks, long *numSources); + ASIOError (*setClockSource)(void *this_, long reference); + ASIOError (*getSamplePosition)(void *this_, ASIOSamples *sPos, ASIOTimeStamp *tStamp); + ASIOError (*getChannelInfo)(void *this_, ASIOChannelInfo *info); + ASIOError (*createBuffers)(void *this_, ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks); + ASIOError (*disposeBuffers)(void *this_); + ASIOError (*controlPanel)(void *this_); + ASIOError (*future)(void *this_, long selector, void *opt); + ASIOError (*outputReady)(void *this_); +} IASIOVtbl; + +typedef struct IASIO { + IASIOVtbl *lpVtbl; +} IASIO; //////////////////////////////// // Internal state -struct AsioDriverInfo { +typedef struct AsioDriverInfo { char name[128]; CLSID clsid; -}; +} AsioDriverInfo; struct AudioEngine { void *hwnd; // HWND for ASIO init @@ -186,7 +194,7 @@ struct AudioEngine { //////////////////////////////// // Global engine pointer for ASIO callbacks (standard pattern — callbacks have no user-data param) -static AudioEngine *g_audio_engine = nullptr; +static AudioEngine *g_audio_engine = NULL; //////////////////////////////// // Sample writing helper @@ -351,7 +359,7 @@ static void enumerate_asio_drivers(AudioEngine *engine) { for (DWORD i = 0; engine->device_count < AUDIO_MAX_DEVICES; i++) { subkey_name_len = sizeof(subkey_name); - if (RegEnumKeyExA(asio_key, i, subkey_name, &subkey_name_len, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) + if (RegEnumKeyExA(asio_key, i, subkey_name, &subkey_name_len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break; HKEY driver_key; @@ -359,10 +367,10 @@ static void enumerate_asio_drivers(AudioEngine *engine) { continue; // Read CLSID string - char clsid_str[64] = {}; + char clsid_str[64] = {0}; DWORD clsid_len = sizeof(clsid_str); DWORD type = 0; - if (RegQueryValueExA(driver_key, "CLSID", nullptr, &type, (LPBYTE)clsid_str, &clsid_len) != ERROR_SUCCESS || + if (RegQueryValueExA(driver_key, "CLSID", NULL, &type, (LPBYTE)clsid_str, &clsid_len) != ERROR_SUCCESS || type != REG_SZ) { RegCloseKey(driver_key); continue; @@ -393,21 +401,20 @@ static void enumerate_asio_drivers(AudioEngine *engine) { // Public API AudioEngine *audio_create(void *hwnd) { - AudioEngine *engine = new AudioEngine(); - memset(engine, 0, sizeof(*engine)); + AudioEngine *engine = (AudioEngine *)calloc(1, sizeof(AudioEngine)); engine->hwnd = hwnd; engine->active_device_index = -1; g_audio_engine = engine; - CoInitialize(nullptr); + CoInitialize(NULL); enumerate_asio_drivers(engine); return engine; } void audio_destroy(AudioEngine *engine) { audio_close_device(engine); - if (g_audio_engine == engine) g_audio_engine = nullptr; - delete engine; + if (g_audio_engine == engine) g_audio_engine = NULL; + free(engine); } void audio_refresh_devices(AudioEngine *engine) { @@ -421,7 +428,7 @@ S32 audio_get_device_count(AudioEngine *engine) { AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index) { if (index < 0 || index >= engine->device_count) - return nullptr; + return NULL; return &engine->devices[index]; } @@ -433,52 +440,52 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { return false; // Create COM instance of the ASIO driver - IASIO *driver = nullptr; - HRESULT hr = CoCreateInstance(engine->drivers[index].clsid, - nullptr, CLSCTX_INPROC_SERVER, - engine->drivers[index].clsid, + IASIO *driver = NULL; + HRESULT hr = CoCreateInstance(&engine->drivers[index].clsid, + NULL, CLSCTX_INPROC_SERVER, + &engine->drivers[index].clsid, (void **)&driver); if (FAILED(hr) || !driver) return false; // Initialize the driver - if (!driver->init(engine->hwnd)) { - driver->Release(); + if (!driver->lpVtbl->init(driver, engine->hwnd)) { + driver->lpVtbl->Release(driver); return false; } // Query channel counts long num_in = 0, num_out = 0; - if (driver->getChannels(&num_in, &num_out) != ASE_OK || num_out <= 0) { - driver->Release(); + if (driver->lpVtbl->getChannels(driver, &num_in, &num_out) != ASE_OK || num_out <= 0) { + driver->lpVtbl->Release(driver); return false; } // Query buffer size long min_size = 0, max_size = 0, preferred_size = 0, granularity = 0; - if (driver->getBufferSize(&min_size, &max_size, &preferred_size, &granularity) != ASE_OK) { - driver->Release(); + if (driver->lpVtbl->getBufferSize(driver, &min_size, &max_size, &preferred_size, &granularity) != ASE_OK) { + driver->lpVtbl->Release(driver); return false; } // Query sample rate F64 sample_rate = 0; - if (driver->getSampleRate(&sample_rate) != ASE_OK || sample_rate <= 0) { + if (driver->lpVtbl->getSampleRate(driver, &sample_rate) != ASE_OK || sample_rate <= 0) { // Try setting a common rate - if (driver->setSampleRate(44100.0) == ASE_OK) { + if (driver->lpVtbl->setSampleRate(driver, 44100.0) == ASE_OK) { sample_rate = 44100.0; } else { - driver->Release(); + driver->lpVtbl->Release(driver); return false; } } // Query output channel sample type - ASIOChannelInfo chan_info = {}; + ASIOChannelInfo chan_info = {0}; chan_info.channel = 0; chan_info.isInput = 0; - if (driver->getChannelInfo(&chan_info) != ASE_OK) { - driver->Release(); + if (driver->lpVtbl->getChannelInfo(driver, &chan_info) != ASE_OK) { + driver->lpVtbl->Release(driver); return false; } @@ -490,8 +497,8 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { for (long ch = 0; ch < num_out; ch++) { engine->buffer_infos[ch].isInput = 0; engine->buffer_infos[ch].channelNum = ch; - engine->buffer_infos[ch].buffers[0] = nullptr; - engine->buffer_infos[ch].buffers[1] = nullptr; + engine->buffer_infos[ch].buffers[0] = NULL; + engine->buffer_infos[ch].buffers[1] = NULL; } // Setup callbacks @@ -501,8 +508,8 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { engine->callbacks.bufferSwitchTimeInfo = asio_buffer_switch_time_info; // Create buffers - if (driver->createBuffers(engine->buffer_infos, num_out, preferred_size, &engine->callbacks) != ASE_OK) { - driver->Release(); + if (driver->lpVtbl->createBuffers(driver, engine->buffer_infos, num_out, preferred_size, &engine->callbacks) != ASE_OK) { + driver->lpVtbl->Release(driver); return false; } @@ -519,16 +526,16 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { engine->test_tone_phase = 0.0; // Start the driver - if (driver->start() != ASE_OK) { - driver->disposeBuffers(); - driver->Release(); - engine->driver = nullptr; + if (driver->lpVtbl->start(driver) != ASE_OK) { + driver->lpVtbl->disposeBuffers(driver); + driver->lpVtbl->Release(driver); + engine->driver = NULL; engine->active_device_index = -1; return false; } // Notify driver that output is ready (optional, some drivers benefit) - driver->outputReady(); + driver->lpVtbl->outputReady(driver); return true; } @@ -539,10 +546,10 @@ void audio_close_device(AudioEngine *engine) { InterlockedExchange(&engine->test_tone_active, 0); InterlockedExchange(&engine->test_tone_samples_remaining, 0); - engine->driver->stop(); - engine->driver->disposeBuffers(); - engine->driver->Release(); - engine->driver = nullptr; + engine->driver->lpVtbl->stop(engine->driver); + engine->driver->lpVtbl->disposeBuffers(engine->driver); + engine->driver->lpVtbl->Release(engine->driver); + engine->driver = NULL; engine->active_device_index = -1; engine->test_tone_phase = 0.0; } diff --git a/src/audio/audio_coreaudio.cpp b/src/audio/audio_coreaudio.c similarity index 89% rename from src/audio/audio_coreaudio.cpp rename to src/audio/audio_coreaudio.c index 215bcc3..751cbbb 100644 --- a/src/audio/audio_coreaudio.cpp +++ b/src/audio/audio_coreaudio.c @@ -13,9 +13,9 @@ #define AUDIO_TEST_TONE_SEC 2.0 #define AUDIO_PI 3.14159265358979323846 -struct CoreAudioDeviceInfo { +typedef struct CoreAudioDeviceInfo { AudioDeviceID device_id; -}; +} CoreAudioDeviceInfo; struct AudioEngine { AudioDeviceInfo devices[AUDIO_MAX_DEVICES]; @@ -37,7 +37,7 @@ struct AudioEngine { //////////////////////////////// // Audio render callback -static AudioEngine *g_audio_engine = nullptr; +static AudioEngine *g_audio_engine = NULL; static OSStatus audio_render_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, @@ -110,14 +110,14 @@ static void enumerate_output_devices(AudioEngine *engine) { }; UInt32 data_size = 0; - if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &data_size) != noErr) + if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size) != noErr) return; S32 device_count = (S32)(data_size / sizeof(AudioDeviceID)); if (device_count <= 0) return; AudioDeviceID *device_ids = (AudioDeviceID *)malloc(data_size); - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &data_size, device_ids) != noErr) { + if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, device_ids) != noErr) { free(device_ids); return; } @@ -131,11 +131,11 @@ static void enumerate_output_devices(AudioEngine *engine) { }; UInt32 stream_size = 0; - if (AudioObjectGetPropertyDataSize(device_ids[i], &stream_prop, 0, nullptr, &stream_size) != noErr) + if (AudioObjectGetPropertyDataSize(device_ids[i], &stream_prop, 0, NULL, &stream_size) != noErr) continue; AudioBufferList *buf_list = (AudioBufferList *)malloc(stream_size); - if (AudioObjectGetPropertyData(device_ids[i], &stream_prop, 0, nullptr, &stream_size, buf_list) != noErr) { + if (AudioObjectGetPropertyData(device_ids[i], &stream_prop, 0, NULL, &stream_size, buf_list) != noErr) { free(buf_list); continue; } @@ -154,9 +154,9 @@ static void enumerate_output_devices(AudioEngine *engine) { kAudioObjectPropertyElementMain }; - CFStringRef name_ref = nullptr; + CFStringRef name_ref = NULL; UInt32 name_size = sizeof(name_ref); - if (AudioObjectGetPropertyData(device_ids[i], &name_prop, 0, nullptr, &name_size, &name_ref) != noErr) + if (AudioObjectGetPropertyData(device_ids[i], &name_prop, 0, NULL, &name_size, &name_ref) != noErr) continue; S32 idx = engine->device_count++; @@ -176,8 +176,7 @@ static void enumerate_output_devices(AudioEngine *engine) { AudioEngine *audio_create(void *hwnd) { (void)hwnd; - AudioEngine *engine = new AudioEngine(); - memset(engine, 0, sizeof(*engine)); + AudioEngine *engine = (AudioEngine *)calloc(1, sizeof(AudioEngine)); engine->active_device_index = -1; atomic_store(&engine->test_tone_active, 0); atomic_store(&engine->test_tone_samples_remaining, 0); @@ -189,8 +188,8 @@ AudioEngine *audio_create(void *hwnd) { void audio_destroy(AudioEngine *engine) { audio_close_device(engine); - if (g_audio_engine == engine) g_audio_engine = nullptr; - delete engine; + if (g_audio_engine == engine) g_audio_engine = NULL; + free(engine); } void audio_refresh_devices(AudioEngine *engine) { @@ -203,7 +202,7 @@ S32 audio_get_device_count(AudioEngine *engine) { } AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index) { - if (index < 0 || index >= engine->device_count) return nullptr; + if (index < 0 || index >= engine->device_count) return NULL; return &engine->devices[index]; } @@ -218,7 +217,7 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { if (NewAUGraph(&engine->graph) != noErr) return false; // Add HAL output node - AudioComponentDescription output_desc = {}; + AudioComponentDescription output_desc = {0}; output_desc.componentType = kAudioUnitType_Output; output_desc.componentSubType = kAudioUnitSubType_HALOutput; output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; @@ -226,19 +225,19 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { AUNode output_node; if (AUGraphAddNode(engine->graph, &output_desc, &output_node) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } if (AUGraphOpen(engine->graph) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } - if (AUGraphNodeInfo(engine->graph, output_node, nullptr, &engine->output_unit) != noErr) { + if (AUGraphNodeInfo(engine->graph, output_node, NULL, &engine->output_unit) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } @@ -246,7 +245,7 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { if (AudioUnitSetProperty(engine->output_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(device_id)) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } @@ -258,11 +257,11 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { }; Float64 sample_rate = 44100.0; UInt32 rate_size = sizeof(sample_rate); - AudioObjectGetPropertyData(device_id, &rate_prop, 0, nullptr, &rate_size, &sample_rate); + AudioObjectGetPropertyData(device_id, &rate_prop, 0, NULL, &rate_size, &sample_rate); engine->sample_rate = sample_rate; // Set stream format: Float32, non-interleaved - AudioStreamBasicDescription fmt = {}; + AudioStreamBasicDescription fmt = {0}; fmt.mSampleRate = sample_rate; fmt.mFormatID = kAudioFormatLinearPCM; fmt.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked; @@ -277,27 +276,27 @@ B32 audio_open_device(AudioEngine *engine, S32 index) { kAudioUnitScope_Input, 0, &fmt, sizeof(fmt)); // Set render callback - AURenderCallbackStruct cb = {}; + AURenderCallbackStruct cb = {0}; cb.inputProc = audio_render_callback; cb.inputProcRefCon = engine; if (AudioUnitSetProperty(engine->output_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &cb, sizeof(cb)) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } // Initialize and start if (AUGraphInitialize(engine->graph) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } if (AUGraphStart(engine->graph) != noErr) { DisposeAUGraph(engine->graph); - engine->graph = nullptr; + engine->graph = NULL; return false; } @@ -318,8 +317,8 @@ void audio_close_device(AudioEngine *engine) { AUGraphStop(engine->graph); AUGraphUninitialize(engine->graph); DisposeAUGraph(engine->graph); - engine->graph = nullptr; - engine->output_unit = nullptr; + engine->graph = NULL; + engine->output_unit = NULL; engine->active_device_index = -1; engine->test_tone_phase = 0.0; } diff --git a/src/base/base_arena.cpp b/src/base/base_arena.c similarity index 93% rename from src/base/base_arena.cpp rename to src/base/base_arena.c index ce665f7..905c316 100644 --- a/src/base/base_arena.cpp +++ b/src/base/base_arena.c @@ -3,7 +3,7 @@ Arena *arena_alloc(U64 cap) { U8 *mem = (U8 *)malloc(sizeof(Arena) + cap); - if (!mem) return nullptr; + if (!mem) return NULL; Arena *arena = (Arena *)mem; arena->base = mem + sizeof(Arena); arena->pos = 0; @@ -19,7 +19,7 @@ void *arena_push(Arena *arena, U64 size) { U64 aligned = AlignPow2(size, 8); if (arena->pos + aligned > arena->cap) { Assert(!"Arena overflow"); - return nullptr; + return NULL; } void *result = arena->base + arena->pos; arena->pos += aligned; @@ -31,7 +31,7 @@ void *arena_push_no_zero(Arena *arena, U64 size) { U64 aligned = AlignPow2(size, 8); if (arena->pos + aligned > arena->cap) { Assert(!"Arena overflow"); - return nullptr; + return NULL; } void *result = arena->base + arena->pos; arena->pos += aligned; diff --git a/src/base/base_arena.h b/src/base/base_arena.h index 6b16c99..3795d01 100644 --- a/src/base/base_arena.h +++ b/src/base/base_arena.h @@ -8,17 +8,17 @@ //////////////////////////////// // Arena type -struct Arena { +typedef struct Arena { U8 *base; U64 pos; U64 cap; -}; +} Arena; // Temporary scope (save/restore position) -struct Temp { +typedef struct Temp { Arena *arena; U64 pos; -}; +} Temp; //////////////////////////////// // Arena functions @@ -34,8 +34,8 @@ void arena_clear(Arena *arena); //////////////////////////////// // Temporary scope helpers -inline Temp temp_begin(Arena *arena) { return {arena, arena->pos}; } -inline void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.pos); } +static inline Temp temp_begin(Arena *arena) { Temp t = {arena, arena->pos}; return t; } +static inline void temp_end(Temp temp) { arena_pop_to(temp.arena, temp.pos); } //////////////////////////////// // Push helper macros diff --git a/src/base/base_core.h b/src/base/base_core.h index 50b5553..6706435 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -7,6 +7,7 @@ #include #include #include +#include //////////////////////////////// // Codebase keywords @@ -17,7 +18,7 @@ #endif #define local_persist static -#define trvke true +#define trvke 1 //////////////////////////////// // Base types diff --git a/src/base/base_inc.c b/src/base/base_inc.c new file mode 100644 index 0000000..2b7f967 --- /dev/null +++ b/src/base/base_inc.c @@ -0,0 +1,3 @@ +// base_inc.c - Unity build for the base layer +#include "base/base_arena.c" +#include "base/base_strings.c" diff --git a/src/base/base_inc.cpp b/src/base/base_inc.cpp deleted file mode 100644 index b86bb0b..0000000 --- a/src/base/base_inc.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// base_inc.cpp - Unity build for the base layer -#include "base/base_arena.cpp" -#include "base/base_strings.cpp" diff --git a/src/base/base_math.h b/src/base/base_math.h index 9886dae..21df9d9 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -7,60 +7,60 @@ //////////////////////////////// // Axis enum -enum Axis2 { +typedef enum Axis2 { Axis2_X = 0, Axis2_Y = 1, Axis2_COUNT, -}; +} Axis2; -enum Side { +typedef enum Side { Side_Min = 0, Side_Max = 1, Side_COUNT, -}; +} Side; -enum Corner { +typedef enum Corner { Corner_00 = 0, // top-left Corner_01 = 1, // top-right Corner_10 = 2, // bottom-left Corner_11 = 3, // bottom-right Corner_COUNT, -}; +} Corner; //////////////////////////////// // Vector types -struct Vec2F32 { F32 x, y; }; -struct Vec2S32 { S32 x, y; }; -struct Vec3F32 { F32 x, y, z; }; -struct Vec4F32 { F32 x, y, z, w; }; +typedef struct Vec2F32 { F32 x, y; } Vec2F32; +typedef struct Vec2S32 { S32 x, y; } Vec2S32; +typedef struct Vec3F32 { F32 x, y, z; } Vec3F32; +typedef struct Vec4F32 { F32 x, y, z, w; } Vec4F32; //////////////////////////////// // Range types -struct Rng1F32 { F32 min, max; }; -struct Rng1S64 { S64 min, max; }; -struct Rng2F32 { Vec2F32 p0, p1; }; +typedef struct Rng1F32 { F32 min, max; } Rng1F32; +typedef struct Rng1S64 { S64 min, max; } Rng1S64; +typedef struct Rng2F32 { Vec2F32 p0, p1; } Rng2F32; //////////////////////////////// // Constructors -static inline Vec2F32 v2f32(F32 x, F32 y) { return {x, y}; } -static inline Vec2S32 v2s32(S32 x, S32 y) { return {x, y}; } -static inline Vec3F32 v3f32(F32 x, F32 y, F32 z) { return {x, y, z}; } -static inline Vec4F32 v4f32(F32 x, F32 y, F32 z, F32 w) { return {x, y, z, w}; } -static inline Rng1F32 rng1f32(F32 min, F32 max) { return {min, max}; } -static inline Rng1S64 rng1s64(S64 min, S64 max) { return {min, max}; } -static inline Rng2F32 rng2f32(Vec2F32 p0, Vec2F32 p1) { return {p0, p1}; } -static inline Rng2F32 rng2f32p(F32 x0, F32 y0, F32 x1, F32 y1) { return {{x0, y0}, {x1, y1}}; } +static inline Vec2F32 v2f32(F32 x, F32 y) { return (Vec2F32){x, y}; } +static inline Vec2S32 v2s32(S32 x, S32 y) { return (Vec2S32){x, y}; } +static inline Vec3F32 v3f32(F32 x, F32 y, F32 z) { return (Vec3F32){x, y, z}; } +static inline Vec4F32 v4f32(F32 x, F32 y, F32 z, F32 w) { return (Vec4F32){x, y, z, w}; } +static inline Rng1F32 rng1f32(F32 min, F32 max) { return (Rng1F32){min, max}; } +static inline Rng1S64 rng1s64(S64 min, S64 max) { return (Rng1S64){min, max}; } +static inline Rng2F32 rng2f32(Vec2F32 p0, Vec2F32 p1) { return (Rng2F32){p0, p1}; } +static inline Rng2F32 rng2f32p(F32 x0, F32 y0, F32 x1, F32 y1) { return (Rng2F32){{x0, y0}, {x1, y1}}; } //////////////////////////////// // Vec2F32 operations -static inline Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) { return {a.x + b.x, a.y + b.y}; } -static inline Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) { return {a.x - b.x, a.y - b.y}; } -static inline Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) { return {a.x * b.x, a.y * b.y}; } -static inline Vec2F32 scale_2f32(Vec2F32 v, F32 s) { return {v.x * s, v.y * s}; } +static inline Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) { return (Vec2F32){a.x + b.x, a.y + b.y}; } +static inline Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) { return (Vec2F32){a.x - b.x, a.y - b.y}; } +static inline Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) { return (Vec2F32){a.x * b.x, a.y * b.y}; } +static inline Vec2F32 scale_2f32(Vec2F32 v, F32 s) { return (Vec2F32){v.x * s, v.y * s}; } // Axis-indexed access static inline F32 v2f32_axis(Vec2F32 v, Axis2 a) { return a == Axis2_X ? v.x : v.y; } @@ -71,10 +71,10 @@ static inline void v2f32_set_axis(Vec2F32 *v, Axis2 a, F32 val) { //////////////////////////////// // Vec4F32 operations -static inline Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) { return {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; } -static inline Vec4F32 scale_4f32(Vec4F32 v, F32 s) { return {v.x*s, v.y*s, v.z*s, v.w*s}; } +static inline Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) { return (Vec4F32){a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; } +static inline Vec4F32 scale_4f32(Vec4F32 v, F32 s) { return (Vec4F32){v.x*s, v.y*s, v.z*s, v.w*s}; } static inline Vec4F32 lerp_4f32(Vec4F32 a, Vec4F32 b, F32 t) { - return {a.x + (b.x - a.x)*t, a.y + (b.y - a.y)*t, a.z + (b.z - a.z)*t, a.w + (b.w - a.w)*t}; + return (Vec4F32){a.x + (b.x - a.x)*t, a.y + (b.y - a.y)*t, a.z + (b.z - a.z)*t, a.w + (b.w - a.w)*t}; } //////////////////////////////// @@ -82,19 +82,19 @@ static inline Vec4F32 lerp_4f32(Vec4F32 a, Vec4F32 b, F32 t) { static inline F32 rng2f32_width(Rng2F32 r) { return r.p1.x - r.p0.x; } static inline F32 rng2f32_height(Rng2F32 r) { return r.p1.y - r.p0.y; } -static inline Vec2F32 rng2f32_dim(Rng2F32 r) { return {r.p1.x - r.p0.x, r.p1.y - r.p0.y}; } -static inline Vec2F32 rng2f32_center(Rng2F32 r) { return {(r.p0.x + r.p1.x)*0.5f, (r.p0.y + r.p1.y)*0.5f}; } +static inline Vec2F32 rng2f32_dim(Rng2F32 r) { return (Vec2F32){r.p1.x - r.p0.x, r.p1.y - r.p0.y}; } +static inline Vec2F32 rng2f32_center(Rng2F32 r) { return (Vec2F32){(r.p0.x + r.p1.x)*0.5f, (r.p0.y + r.p1.y)*0.5f}; } static inline B32 rng2f32_contains(Rng2F32 r, Vec2F32 p) { return p.x >= r.p0.x && p.x <= r.p1.x && p.y >= r.p0.y && p.y <= r.p1.y; } static inline Rng2F32 rng2f32_pad(Rng2F32 r, F32 p) { - return {{r.p0.x - p, r.p0.y - p}, {r.p1.x + p, r.p1.y + p}}; + return (Rng2F32){{r.p0.x - p, r.p0.y - p}, {r.p1.x + p, r.p1.y + p}}; } static inline Rng2F32 rng2f32_shift(Rng2F32 r, Vec2F32 v) { - return {{r.p0.x + v.x, r.p0.y + v.y}, {r.p1.x + v.x, r.p1.y + v.y}}; + return (Rng2F32){{r.p0.x + v.x, r.p0.y + v.y}, {r.p1.x + v.x, r.p1.y + v.y}}; } static inline Rng2F32 rng2f32_intersect(Rng2F32 a, Rng2F32 b) { - return {{Max(a.p0.x, b.p0.x), Max(a.p0.y, b.p0.y)}, + return (Rng2F32){{Max(a.p0.x, b.p0.x), Max(a.p0.y, b.p0.y)}, {Min(a.p1.x, b.p1.x), Min(a.p1.y, b.p1.y)}}; } diff --git a/src/base/base_strings.cpp b/src/base/base_strings.c similarity index 78% rename from src/base/base_strings.cpp rename to src/base/base_strings.c index 87eebcb..769fa99 100644 --- a/src/base/base_strings.cpp +++ b/src/base/base_strings.c @@ -5,22 +5,24 @@ Str8 str8_pushf(Arena *arena, const char *fmt, ...) { va_list args, args2; va_start(args, fmt); va_copy(args2, args); - S32 len = vsnprintf(nullptr, 0, fmt, args); + S32 len = vsnprintf(NULL, 0, fmt, args); va_end(args); char *buf = push_array(arena, char, len + 1); vsnprintf(buf, len + 1, fmt, args2); va_end(args2); - return {buf, (U64)len}; + Str8 r = {buf, (U64)len}; + return r; } Str8 str8_push_copy(Arena *arena, Str8 s) { - if (s.size == 0 || !s.str) return {nullptr, 0}; + if (s.size == 0 || !s.str) { Str8 r = {NULL, 0}; return r; } char *buf = push_array_no_zero(arena, char, s.size + 1); MemoryCopy(buf, s.str, s.size); buf[s.size] = 0; - return {buf, s.size}; + Str8 r = {buf, s.size}; + return r; } void str8_list_push(Arena *arena, Str8List *list, Str8 s) { diff --git a/src/base/base_strings.h b/src/base/base_strings.h index 187e303..3bb7890 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -7,38 +7,38 @@ //////////////////////////////// // String types -struct Str8 { +typedef struct Str8 { const char *str; U64 size; -}; +} Str8; -struct Str8Node { - Str8Node *next; - Str8 string; -}; +typedef struct Str8Node { + struct Str8Node *next; + Str8 string; +} Str8Node; -struct Str8List { +typedef struct Str8List { Str8Node *first; Str8Node *last; U64 count; U64 total_size; -}; +} Str8List; //////////////////////////////// // Forward declaration for Arena -struct Arena; +typedef struct Arena Arena; //////////////////////////////// // Constructors -static inline Str8 str8(const char *s, U64 len) { return {s, len}; } -static inline Str8 str8_cstr(const char *s) { return {s, s ? (U64)strlen(s) : 0}; } -static inline Str8 str8_lit(const char *s) { return {s, s ? (U64)strlen(s) : 0}; } +static inline Str8 str8(const char *s, U64 len) { Str8 r = {s, len}; return r; } +static inline Str8 str8_cstr(const char *s) { Str8 r = {s, s ? (U64)strlen(s) : 0}; return r; } +static inline Str8 str8_lit(const char *s) { Str8 r = {s, s ? (U64)strlen(s) : 0}; return r; } static inline B32 str8_match(Str8 a, Str8 b) { if (a.size != b.size) return 0; return MemoryCompare(a.str, b.str, a.size) == 0; } -static inline B32 str8_is_empty(Str8 s) { return s.size == 0 || s.str == nullptr; } +static inline B32 str8_is_empty(Str8 s) { return s.size == 0 || s.str == NULL; } //////////////////////////////// // String operations (require arena) diff --git a/src/main.cpp b/src/main.c similarity index 97% rename from src/main.cpp rename to src/main.c index d4fc0b6..fe0b094 100644 --- a/src/main.cpp +++ b/src/main.c @@ -15,25 +15,25 @@ #include "ui/ui_popups.h" #include "ui/ui_piano.h" -// [cpp] -#include "base/base_inc.cpp" -#include "ui/ui_core.cpp" -#include "ui/ui_icons.cpp" -#include "ui/ui_widgets.cpp" -#include "ui/ui_popups.cpp" -#include "ui/ui_piano.cpp" +// [c] +#include "base/base_inc.c" +#include "ui/ui_core.c" +#include "ui/ui_icons.c" +#include "ui/ui_widgets.c" +#include "ui/ui_popups.c" +#include "ui/ui_piano.c" #ifdef __APPLE__ -#include "platform/platform_macos.mm" -#include "renderer/renderer_metal.mm" -#include "midi/midi_coremidi.cpp" -#include "audio/audio_coreaudio.cpp" +#include "platform/platform_macos.m" +#include "renderer/renderer_metal.m" +#include "midi/midi_coremidi.c" +#include "audio/audio_coreaudio.c" #else -#include "platform/platform_win32.cpp" -#include "renderer/renderer_vulkan.cpp" -#include "midi/midi_win32.cpp" -#include "audio/audio_asio.cpp" +#include "platform/platform_win32.c" +#include "renderer/renderer_vulkan.c" +#include "midi/midi_win32.c" +#include "audio/audio_asio.c" #endif -#include "menus.cpp" +#include "menus.c" //////////////////////////////// // Clay text config helpers @@ -43,17 +43,17 @@ static Clay_TextElementConfig g_text_config_title; static Clay_TextElementConfig g_text_config_dim; static void init_text_configs() { - g_text_config_normal = {}; + memset(&g_text_config_normal, 0, sizeof(g_text_config_normal)); g_text_config_normal.textColor = g_theme.text; g_text_config_normal.fontSize = 15; g_text_config_normal.wrapMode = CLAY_TEXT_WRAP_NONE; - g_text_config_title = {}; + memset(&g_text_config_title, 0, sizeof(g_text_config_title)); g_text_config_title.textColor = g_theme.text; g_text_config_title.fontSize = 15; g_text_config_title.wrapMode = CLAY_TEXT_WRAP_NONE; - g_text_config_dim = {}; + memset(&g_text_config_dim, 0, sizeof(g_text_config_dim)); g_text_config_dim.textColor = g_theme.text_dim; g_text_config_dim.fontSize = 15; g_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -62,7 +62,7 @@ static void init_text_configs() { //////////////////////////////// // App state — all mutable state the frame function needs -struct AppState { +typedef struct AppState { PlatformWindow *window; Renderer *renderer; MidiEngine *midi; @@ -164,7 +164,7 @@ struct AppState { S32 patch_tab; // 0 = Matrix, 1 = Graph B32 patch_matrix[32][33]; // internal routing: 32 inputs x (Master + 32 outputs) B32 hw_matrix[32][32]; // hardware routing: 32 channel outputs x 32 hw outputs -}; +} AppState; //////////////////////////////// //////////////////////////////// @@ -466,7 +466,7 @@ static void build_main_panel(AppState *app) { // Thumb color: highlight on hover or drag Clay_Color thumb_color = g_theme.scrollbar_grab; if (app->scrollbar_dragging || thumb_hovered) { - thumb_color = Clay_Color{ + thumb_color = (Clay_Color){ (F32)Min((S32)thumb_color.r + 30, 255), (F32)Min((S32)thumb_color.g + 30, 255), (F32)Min((S32)thumb_color.b + 30, 255), @@ -582,8 +582,8 @@ static void build_right_panel(AppState *app) { static char note_bufs[64][8]; static char vel_bufs[64][8]; static Clay_TextElementConfig box_text_config; - box_text_config = {}; - box_text_config.textColor = Clay_Color{255, 255, 255, 255}; + memset(&box_text_config, 0, sizeof(box_text_config)); + box_text_config.textColor = (Clay_Color){255, 255, 255, 255}; box_text_config.fontSize = FONT_SIZE_SMALL; box_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -602,9 +602,9 @@ static void build_right_panel(AppState *app) { if (dev->active) { box_color = piano_velocity_color(dev->velocity); } else if (dev->releasing) { - box_color = Clay_Color{255, 255, 255, 255}; + box_color = (Clay_Color){255, 255, 255, 255}; } else { - box_color = Clay_Color{60, 60, 60, 255}; + box_color = (Clay_Color){60, 60, 60, 255}; } // Box text: note name when held, "OFF" when releasing, "---" when idle @@ -624,7 +624,7 @@ static void build_right_panel(AppState *app) { Clay_TextElementConfig *box_txt = &box_text_config; static Clay_TextElementConfig box_text_dark; box_text_dark = box_text_config; - box_text_dark.textColor = Clay_Color{30, 30, 30, 255}; + box_text_dark.textColor = (Clay_Color){30, 30, 30, 255}; if (dev->releasing) box_txt = &box_text_dark; // Velocity text @@ -746,7 +746,7 @@ static void build_log_panel(AppState *app) { } //////////////////////////////// -// Corner radius presets (indexed by AppState::radius_sel) +// Corner radius presets static const F32 radius_values[] = { 0.0f, 4.0f, 6.0f, 10.0f }; // Window content callbacks @@ -818,7 +818,7 @@ static void settings_window_content(void *user_data) { .backgroundColor = g_theme.disabled_bg, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS) ) { - static Clay_TextElementConfig disabled_text = {}; + static Clay_TextElementConfig disabled_text = {0}; disabled_text.textColor = g_theme.disabled_text; disabled_text.fontSize = FONT_SIZE_NORMAL; disabled_text.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -976,22 +976,22 @@ static void build_header_bar(AppState *app) { Clay_Color bar_bg = g_theme.header_bg; Clay_Color border_bot = {(F32)Max((S32)bar_bg.r - 12, 0), (F32)Max((S32)bar_bg.g - 12, 0), (F32)Max((S32)bar_bg.b - 12, 0), 255}; - static Clay_TextElementConfig header_btn_active_text = {}; - header_btn_active_text.textColor = Clay_Color{255, 255, 255, 255}; + static Clay_TextElementConfig header_btn_active_text = {0}; + header_btn_active_text.textColor = (Clay_Color){255, 255, 255, 255}; header_btn_active_text.fontSize = FONT_SIZE_NORMAL; header_btn_active_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig header_clock_text = {}; + static Clay_TextElementConfig header_clock_text = {0}; header_clock_text.textColor = g_theme.text; header_clock_text.fontSize = uifs(22); header_clock_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig header_indicator_text = {}; + static Clay_TextElementConfig header_indicator_text = {0}; header_indicator_text.textColor = g_theme.text; header_indicator_text.fontSize = FONT_SIZE_NORMAL; header_indicator_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig header_indicator_label = {}; + static Clay_TextElementConfig header_indicator_label = {0}; header_indicator_label.textColor = g_theme.text_dim; header_indicator_label.fontSize = FONT_SIZE_SMALL; header_indicator_label.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1056,7 +1056,7 @@ static void build_header_bar(AppState *app) { }, .backgroundColor = g_theme.bg_lighter, .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS), - ) { ui_icon(UI_ICON_TRANSPORT_RECORD, uis(14), Clay_Color{200, 60, 60, 255}); } + ) { ui_icon(UI_ICON_TRANSPORT_RECORD, uis(14), (Clay_Color){200, 60, 60, 255}); } } // Spacer @@ -1410,28 +1410,28 @@ static void build_patch_view(AppState *app) { #define MATRIX_OUTPUTS 33 // Master + Out 1..32 #define HW_OUTPUTS 32 - static Clay_TextElementConfig matrix_hdr_text = {}; + static Clay_TextElementConfig matrix_hdr_text = {0}; matrix_hdr_text.textColor = g_theme.text_dim; matrix_hdr_text.fontSize = FONT_SIZE_SMALL; matrix_hdr_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig matrix_axis_text = {}; + static Clay_TextElementConfig matrix_axis_text = {0}; matrix_axis_text.textColor = g_theme.text; matrix_axis_text.fontSize = FONT_SIZE_SMALL; matrix_axis_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig matrix_title_text = {}; + static Clay_TextElementConfig matrix_title_text = {0}; matrix_title_text.textColor = g_theme.text; matrix_title_text.fontSize = FONT_SIZE_NORMAL; matrix_title_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig cell_x_text = {}; - cell_x_text.textColor = Clay_Color{255, 255, 255, 255}; + static Clay_TextElementConfig cell_x_text = {0}; + cell_x_text.textColor = (Clay_Color){255, 255, 255, 255}; cell_x_text.fontSize = FONT_SIZE_SMALL; cell_x_text.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig fb_text = {}; - fb_text.textColor = Clay_Color{80, 80, 80, 255}; + static Clay_TextElementConfig fb_text = {0}; + fb_text.textColor = (Clay_Color){80, 80, 80, 255}; fb_text.fontSize = FONT_SIZE_SMALL; fb_text.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1549,7 +1549,7 @@ static void build_patch_view(AppState *app) { Clay_Color cell_bg; if (is_feedback) { - cell_bg = Clay_Color{ + cell_bg = (Clay_Color){ (F32)Max((S32)g_theme.bg_dark.r - 10, 0), (F32)Max((S32)g_theme.bg_dark.g - 10, 0), (F32)Max((S32)g_theme.bg_dark.b - 10, 0), 255 @@ -1561,7 +1561,7 @@ static void build_patch_view(AppState *app) { } else if ((s + d) % 2 == 0) { cell_bg = g_theme.bg_dark; } else { - cell_bg = Clay_Color{ + cell_bg = (Clay_Color){ (F32)Min((S32)g_theme.bg_dark.r + 6, 255), (F32)Min((S32)g_theme.bg_dark.g + 6, 255), (F32)Min((S32)g_theme.bg_dark.b + 6, 255), 255 @@ -1569,7 +1569,7 @@ static void build_patch_view(AppState *app) { } if (d == 0 && !active && !cell_hovered && !is_feedback) { - cell_bg = Clay_Color{ + cell_bg = (Clay_Color){ (F32)Min((S32)cell_bg.r + 10, 255), (F32)Min((S32)cell_bg.g + 10, 255), (F32)Min((S32)cell_bg.b + 12, 255), 255 @@ -1699,7 +1699,7 @@ static void build_patch_view(AppState *app) { } else if ((s + d) % 2 == 0) { cell_bg = g_theme.bg_dark; } else { - cell_bg = Clay_Color{ + cell_bg = (Clay_Color){ (F32)Min((S32)g_theme.bg_dark.r + 6, 255), (F32)Min((S32)g_theme.bg_dark.g + 6, 255), (F32)Min((S32)g_theme.bg_dark.b + 6, 255), 255 @@ -1933,7 +1933,7 @@ static void do_frame(AppState *app) { Clay_RenderCommandArray render_commands = ui_end_frame(app->ui); // Render - renderer_end_frame(app->renderer, render_commands); + renderer_end_frame(app->renderer, &render_commands); } //////////////////////////////// @@ -1950,7 +1950,7 @@ int main(int argc, char **argv) { (void)argc; (void)argv; - PlatformWindowDesc window_desc = {}; + PlatformWindowDesc window_desc = platform_window_desc_default(); PlatformWindow *window = platform_create_window(&window_desc); if (!window) return 1; @@ -1958,7 +1958,7 @@ int main(int argc, char **argv) { S32 w, h; platform_get_size(window, &w, &h); - RendererDesc renderer_desc = {}; + RendererDesc renderer_desc = {0}; renderer_desc.window_handle = platform_get_native_handle(window); renderer_desc.width = w; renderer_desc.height = h; @@ -1990,7 +1990,7 @@ int main(int argc, char **argv) { } } - AppState app = {}; + AppState app = {0}; app.window = window; app.renderer = renderer; app.midi = midi; @@ -2060,9 +2060,9 @@ int main(int argc, char **argv) { // Open popup windows when flags transition to 1 if (app.show_settings_window && !popup_find_by_flag(&app.show_settings_window)) - popup_open(window, renderer, "Preferences", &app.show_settings_window, 480, 400, settings_window_content, &app); + popup_open(window, renderer, "Preferences", &app.show_settings_window, 480, 400, settings_window_content, &app, PLATFORM_WINDOW_STYLE_POPUP, 0); if (app.show_about_window && !popup_find_by_flag(&app.show_about_window)) - popup_open(window, renderer, "About", &app.show_about_window, 260, 200, about_window_content, nullptr); + popup_open(window, renderer, "About", &app.show_about_window, 260, 200, about_window_content, NULL, PLATFORM_WINDOW_STYLE_POPUP, 0); if (app.show_mix_popout && !popup_find_by_flag(&app.show_mix_popout)) popup_open(window, renderer, "Mix", &app.show_mix_popout, 900, 600, mix_popout_content, &app, PLATFORM_WINDOW_STYLE_POPUP_RESIZABLE, 1); if (app.show_patch_popout && !popup_find_by_flag(&app.show_patch_popout)) @@ -2082,7 +2082,7 @@ int main(int argc, char **argv) { } popup_close_all(); - platform_set_frame_callback(window, nullptr, nullptr); + platform_set_frame_callback(window, NULL, NULL); audio_destroy(audio); midi_destroy(midi); ui_destroy(ui); diff --git a/src/menus.cpp b/src/menus.c similarity index 94% rename from src/menus.cpp rename to src/menus.c index 0c7e53e..a81fa59 100644 --- a/src/menus.cpp +++ b/src/menus.c @@ -1,6 +1,6 @@ #include "platform/platform.h" -enum MenuCmd { +typedef enum MenuCmd { MENU_NONE = 0, MENU_FILE_NEW, MENU_FILE_OPEN, @@ -15,7 +15,7 @@ enum MenuCmd { MENU_VIEW_DEMO, MENU_VIEW_MIDI_DEVICES, MENU_PREFERENCES_EDIT, -}; +} MenuCmd; static void setup_menus(PlatformWindow *window) { PlatformMenuItem file_items[] = { @@ -23,7 +23,7 @@ static void setup_menus(PlatformWindow *window) { { "Open...", MENU_FILE_OPEN }, { "Save", MENU_FILE_SAVE }, { "Save As...", MENU_FILE_SAVE_AS }, - { nullptr, 0 }, + { NULL, 0 }, { "Exit", MENU_FILE_EXIT }, }; @@ -36,7 +36,7 @@ static void setup_menus(PlatformWindow *window) { { "Browser", MENU_VIEW_BROWSER }, { "Properties", MENU_VIEW_PROPERTIES }, { "Log", MENU_VIEW_LOG }, - { nullptr, 0 }, + { NULL, 0 }, { "Demo", MENU_VIEW_DEMO }, { "MIDI Devices", MENU_VIEW_MIDI_DEVICES }, }; diff --git a/src/midi/midi.h b/src/midi/midi.h index 515a121..223c98c 100644 --- a/src/midi/midi.h +++ b/src/midi/midi.h @@ -2,9 +2,9 @@ #include "base/base_core.h" -struct MidiEngine; +typedef struct MidiEngine MidiEngine; -struct MidiDeviceInfo { +typedef struct MidiDeviceInfo { char name[64]; S32 id; B32 is_input; @@ -12,9 +12,9 @@ struct MidiDeviceInfo { B32 releasing; // true during release flash S32 velocity; // last note-on velocity (0-127) S32 note; // last MIDI note number (0-127) -}; +} MidiDeviceInfo; -MidiEngine *midi_create(); +MidiEngine *midi_create(void); void midi_destroy(MidiEngine *engine); void midi_refresh_devices(MidiEngine *engine); S32 midi_get_device_count(MidiEngine *engine); diff --git a/src/midi/midi_coremidi.cpp b/src/midi/midi_coremidi.c similarity index 96% rename from src/midi/midi_coremidi.cpp rename to src/midi/midi_coremidi.c index 4e840a4..5918a73 100644 --- a/src/midi/midi_coremidi.cpp +++ b/src/midi/midi_coremidi.c @@ -3,6 +3,7 @@ #include #include #include +#include #define MIDI_MAX_DEVICES 64 #define MIDI_RELEASE_FLASH_DURATION 0.15f @@ -113,7 +114,7 @@ static void enumerate_midi_devices(MidiEngine *engine) { for (ItemCount i = 0; i < num_sources && engine->device_count < MIDI_MAX_DEVICES; i++) { MIDIEndpointRef endpoint = MIDIGetSource((ItemCount)i); - CFStringRef name_ref = nullptr; + CFStringRef name_ref = NULL; MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref); MidiDeviceInfo *dev = &engine->devices[engine->device_count]; @@ -139,7 +140,7 @@ static void enumerate_midi_devices(MidiEngine *engine) { for (ItemCount i = 0; i < num_dests && engine->device_count < MIDI_MAX_DEVICES; i++) { MIDIEndpointRef endpoint = MIDIGetDestination((ItemCount)i); - CFStringRef name_ref = nullptr; + CFStringRef name_ref = NULL; MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref); MidiDeviceInfo *dev = &engine->devices[engine->device_count]; @@ -163,11 +164,10 @@ static void enumerate_midi_devices(MidiEngine *engine) { //////////////////////////////// // Public API -MidiEngine *midi_create() { - MidiEngine *engine = new MidiEngine(); - memset(engine, 0, sizeof(*engine)); +MidiEngine *midi_create(void) { + MidiEngine *engine = (MidiEngine *)calloc(1, sizeof(MidiEngine)); - MIDIClientCreate(CFSTR("autosample"), nullptr, nullptr, &engine->client); + MIDIClientCreate(CFSTR("autosample"), NULL, NULL, &engine->client); MIDIInputPortCreate(engine->client, CFSTR("Input"), midi_read_callback, engine, &engine->input_port); midi_refresh_devices(engine); @@ -178,7 +178,7 @@ void midi_destroy(MidiEngine *engine) { midi_close_all_inputs(engine); if (engine->input_port) MIDIPortDispose(engine->input_port); if (engine->client) MIDIClientDispose(engine->client); - delete engine; + free(engine); } void midi_open_all_inputs(MidiEngine *engine) { @@ -276,6 +276,6 @@ S32 midi_get_device_count(MidiEngine *engine) { } MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index) { - if (index < 0 || index >= engine->device_count) return nullptr; + if (index < 0 || index >= engine->device_count) return NULL; return &engine->devices[index]; } diff --git a/src/midi/midi_win32.cpp b/src/midi/midi_win32.c similarity index 95% rename from src/midi/midi_win32.cpp rename to src/midi/midi_win32.c index 4e20f85..efb7be8 100644 --- a/src/midi/midi_win32.cpp +++ b/src/midi/midi_win32.c @@ -2,6 +2,7 @@ #include #include #include +#include #define MIDI_MAX_DEVICES 64 #define MIDI_RELEASE_FLASH_DURATION 0.15f @@ -31,7 +32,7 @@ struct MidiEngine { //////////////////////////////// // MIDI input callback — called from Win32 MIDI driver thread -static MidiEngine *g_midi_engine = nullptr; +static MidiEngine *g_midi_engine = NULL; static void CALLBACK midi_in_callback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { @@ -71,9 +72,8 @@ static void CALLBACK midi_in_callback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwIn } } -MidiEngine *midi_create() { - MidiEngine *engine = new MidiEngine(); - memset(engine, 0, sizeof(*engine)); +MidiEngine *midi_create(void) { + MidiEngine *engine = (MidiEngine *)calloc(1, sizeof(MidiEngine)); g_midi_engine = engine; midi_refresh_devices(engine); return engine; @@ -81,8 +81,8 @@ MidiEngine *midi_create() { void midi_destroy(MidiEngine *engine) { midi_close_all_inputs(engine); - if (g_midi_engine == engine) g_midi_engine = nullptr; - delete engine; + if (g_midi_engine == engine) g_midi_engine = NULL; + free(engine); } void midi_open_all_inputs(MidiEngine *engine) { @@ -91,7 +91,7 @@ void midi_open_all_inputs(MidiEngine *engine) { if (!dev->is_input) continue; if (engine->input_handles[i]) continue; // already open - HMIDIIN handle = nullptr; + HMIDIIN handle = NULL; MMRESULT res = midiInOpen(&handle, (UINT)dev->id, (DWORD_PTR)midi_in_callback, (DWORD_PTR)i, CALLBACK_FUNCTION); @@ -107,7 +107,7 @@ void midi_close_all_inputs(MidiEngine *engine) { if (engine->input_handles[i]) { midiInStop(engine->input_handles[i]); midiInClose(engine->input_handles[i]); - engine->input_handles[i] = nullptr; + engine->input_handles[i] = NULL; } engine->pending_note_on_vel[i] = 0; engine->pending_note_num[i] = 0; @@ -193,7 +193,7 @@ void midi_refresh_devices(MidiEngine *engine) { UINT num_in = midiInGetNumDevs(); for (UINT i = 0; i < num_in && engine->device_count < MIDI_MAX_DEVICES; i++) { - MIDIINCAPSA caps = {}; + MIDIINCAPSA caps = {0}; if (midiInGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { MidiDeviceInfo *dev = &engine->devices[engine->device_count++]; strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE); @@ -205,7 +205,7 @@ void midi_refresh_devices(MidiEngine *engine) { UINT num_out = midiOutGetNumDevs(); for (UINT i = 0; i < num_out && engine->device_count < MIDI_MAX_DEVICES; i++) { - MIDIOUTCAPSA caps = {}; + MIDIOUTCAPSA caps = {0}; if (midiOutGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { MidiDeviceInfo *dev = &engine->devices[engine->device_count++]; strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE); @@ -224,6 +224,6 @@ S32 midi_get_device_count(MidiEngine *engine) { MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index) { if (index < 0 || index >= engine->device_count) - return nullptr; + return NULL; return &engine->devices[index]; } diff --git a/src/platform/platform.h b/src/platform/platform.h index 0cd8859..04b35bc 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -31,7 +31,7 @@ enum { PKEY_MINUS = 0xBD, // '-'/'_' (VK_OEM_MINUS) }; -struct PlatformInput { +typedef struct PlatformInput { // Typed characters (UTF-16 code units, printable only) U16 chars[PLATFORM_MAX_CHARS_PER_FRAME]; S32 char_count; @@ -49,41 +49,50 @@ struct PlatformInput { Vec2F32 scroll_delta; B32 mouse_down; B32 was_mouse_down; -}; +} PlatformInput; -struct PlatformWindow; +typedef struct PlatformWindow PlatformWindow; -enum PlatformWindowStyle { +typedef enum PlatformWindowStyle { PLATFORM_WINDOW_STYLE_NORMAL = 0, PLATFORM_WINDOW_STYLE_POPUP = 1, // utility panel, owned by parent, fixed size PLATFORM_WINDOW_STYLE_POPUP_RESIZABLE = 2, // utility panel, owned by parent, resizable -}; +} PlatformWindowStyle; -struct PlatformWindowDesc { - const char *title = "autosample"; - S32 width = 1280; - S32 height = 720; - PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_NORMAL; - PlatformWindow *parent = nullptr; - B32 independent = 0; // if true, don't attach as child (independent top-level window) -}; +typedef struct PlatformWindowDesc { + const char *title; + S32 width; + S32 height; + PlatformWindowStyle style; + PlatformWindow *parent; + B32 independent; // if true, don't attach as child (independent top-level window) +} PlatformWindowDesc; -enum PlatformMsgBoxType { +// Helper to create a default PlatformWindowDesc +static inline PlatformWindowDesc platform_window_desc_default(void) { + PlatformWindowDesc d = {0}; + d.title = "autosample"; + d.width = 1280; + d.height = 720; + return d; +} + +typedef enum PlatformMsgBoxType { PLATFORM_MSGBOX_OK = 0, PLATFORM_MSGBOX_OK_CANCEL = 1, PLATFORM_MSGBOX_YES_NO = 2, -}; +} PlatformMsgBoxType; -struct PlatformMenuItem { - const char *label; // nullptr = separator +typedef struct PlatformMenuItem { + const char *label; // NULL = separator S32 id; // command ID (ignored for separators) -}; +} PlatformMenuItem; -struct PlatformMenu { +typedef struct PlatformMenu { const char *label; PlatformMenuItem *items; S32 item_count; -}; +} PlatformMenu; // Called by the platform layer when the window needs a frame rendered // (e.g., during a live resize). user_data is the pointer passed to @@ -114,11 +123,11 @@ S32 platform_message_box(PlatformWindow *parent, const char *title, const char *message, PlatformMsgBoxType type); // Cursor shapes for resize handles -enum PlatformCursor { +typedef enum PlatformCursor { PLATFORM_CURSOR_ARROW = 0, PLATFORM_CURSOR_SIZE_WE = 1, // horizontal resize PLATFORM_CURSOR_SIZE_NS = 2, // vertical resize -}; +} PlatformCursor; void platform_set_cursor(PlatformCursor cursor); @@ -128,6 +137,6 @@ F32 platform_get_dpi_scale(PlatformWindow *window); // Clipboard operations (null-terminated UTF-8 strings). // platform_clipboard_set copies text to the system clipboard. -// platform_clipboard_get returns a pointer to a static buffer (valid until next call), or nullptr. +// platform_clipboard_get returns a pointer to a static buffer (valid until next call), or NULL. void platform_clipboard_set(const char *text); -const char *platform_clipboard_get(); +const char *platform_clipboard_get(void); diff --git a/src/platform/platform_macos.mm b/src/platform/platform_macos.m similarity index 98% rename from src/platform/platform_macos.mm rename to src/platform/platform_macos.m index fb857fe..a93233a 100644 --- a/src/platform/platform_macos.mm +++ b/src/platform/platform_macos.m @@ -1,6 +1,8 @@ #include "platform/platform.h" #import +#include +#include // macOS virtual key codes (avoids Carbon.h include) enum { @@ -46,10 +48,8 @@ static U8 macos_keycode_to_pkey(U16 keycode) { //////////////////////////////// // Forward declarations -struct PlatformWindow; - // Main window receives menu commands -static PlatformWindow *g_main_window = nullptr; +static PlatformWindow *g_main_window = NULL; //////////////////////////////// // Objective-C helper classes @@ -77,7 +77,7 @@ static PlatformWindow *g_main_window = nullptr; //////////////////////////////// // PlatformWindow struct -struct PlatformWindow { +typedef struct PlatformWindow { NSWindow *ns_window; ASmplView *view; ASmplWindowDelegate *delegate; @@ -91,7 +91,7 @@ struct PlatformWindow { B32 prev_mouse_down; B32 mouse_down_state; F32 backing_scale; -}; +} PlatformWindow; //////////////////////////////// // C callback helpers (called from ObjC via PlatformWindow pointer) @@ -288,8 +288,7 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { ASmplWindowDelegate *delegate = [[ASmplWindowDelegate alloc] init]; [ns_window setDelegate:delegate]; - PlatformWindow *window = new PlatformWindow(); - memset(window, 0, sizeof(*window)); + PlatformWindow *window = (PlatformWindow *)calloc(1, sizeof(PlatformWindow)); window->ns_window = ns_window; window->view = view; window->delegate = delegate; @@ -329,8 +328,8 @@ void platform_destroy_window(PlatformWindow *window) { [window->ns_window close]; if (g_main_window == window) - g_main_window = nullptr; - delete window; + g_main_window = NULL; + free(window); } B32 platform_poll_events(PlatformWindow *window) { @@ -442,7 +441,7 @@ PlatformInput platform_get_input(PlatformWindow *window) { result.shift_held = (mods & NSEventModifierFlagShift) != 0; // Clear accumulated events for next frame - window->input = {}; + memset(&window->input, 0, sizeof(window->input)); return result; } @@ -491,7 +490,7 @@ const char *platform_clipboard_get() { } } - return buf[0] ? buf : nullptr; + return buf[0] ? buf : NULL; } S32 platform_message_box(PlatformWindow *parent, const char *title, diff --git a/src/platform/platform_win32.cpp b/src/platform/platform_win32.c similarity index 89% rename from src/platform/platform_win32.cpp rename to src/platform/platform_win32.c index 1b68d22..55c2225 100644 --- a/src/platform/platform_win32.cpp +++ b/src/platform/platform_win32.c @@ -5,8 +5,10 @@ #endif #include #include +#include +#include -struct PlatformWindow { +typedef struct PlatformWindow { HWND hwnd; B32 should_close; S32 width; @@ -16,11 +18,11 @@ struct PlatformWindow { void *frame_callback_user_data; PlatformInput input; B32 prev_mouse_down; -}; +} PlatformWindow; // Main window receives menu commands -static PlatformWindow *g_main_window = nullptr; -static HCURSOR g_current_cursor = nullptr; +static PlatformWindow *g_main_window = NULL; +static HCURSOR g_current_cursor = NULL; static B32 g_wndclass_registered = false; static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { @@ -69,14 +71,14 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM case WM_SETCURSOR: // When the cursor is in our client area, use the app-set cursor. if (LOWORD(lparam) == HTCLIENT) { - SetCursor(g_current_cursor ? g_current_cursor : LoadCursor(nullptr, IDC_ARROW)); + SetCursor(g_current_cursor ? g_current_cursor : LoadCursor(NULL, IDC_ARROW)); return TRUE; } break; case WM_DPICHANGED: if (pw) { RECT *suggested = (RECT *)lparam; - SetWindowPos(hwnd, nullptr, suggested->left, suggested->top, + SetWindowPos(hwnd, NULL, suggested->left, suggested->top, suggested->right - suggested->left, suggested->bottom - suggested->top, SWP_NOZORDER | SWP_NOACTIVATE); } @@ -101,12 +103,12 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); if (!g_wndclass_registered) { - WNDCLASSEXW wc = {}; + WNDCLASSEXW wc = {0}; wc.cbSize = sizeof(wc); wc.style = CS_CLASSDC; wc.lpfnWndProc = win32_wndproc; - wc.hInstance = GetModuleHandleW(nullptr); - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hInstance = GetModuleHandleW(NULL); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = L"autosample_wc"; RegisterClassExW(&wc); g_wndclass_registered = true; @@ -119,7 +121,7 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { int y = (screen_h - desc->height) / 2; DWORD style; - HWND parent_hwnd = nullptr; + HWND parent_hwnd = NULL; if (desc->style == PLATFORM_WINDOW_STYLE_POPUP) { style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU; if (desc->parent && !desc->independent) parent_hwnd = desc->parent->hwnd; @@ -133,7 +135,7 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { RECT rect = { 0, 0, (LONG)desc->width, (LONG)desc->height }; AdjustWindowRectExForDpi(&rect, style, FALSE, 0, dpi); - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, nullptr, 0); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, NULL, 0); wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, wtitle, wchar_count); @@ -143,15 +145,15 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) { x, y, rect.right - rect.left, rect.bottom - rect.top, - parent_hwnd, nullptr, GetModuleHandleW(nullptr), nullptr + parent_hwnd, NULL, GetModuleHandleW(NULL), NULL ); _freea(wtitle); if (!hwnd) - return nullptr; + return NULL; - PlatformWindow *window = new PlatformWindow(); + PlatformWindow *window = (PlatformWindow *)calloc(1, sizeof(PlatformWindow)); window->hwnd = hwnd; window->should_close = false; window->width = desc->width; @@ -179,14 +181,14 @@ void platform_destroy_window(PlatformWindow *window) { } if (g_main_window == window) - g_main_window = nullptr; + g_main_window = NULL; - delete window; + free(window); } B32 platform_poll_events(PlatformWindow *window) { MSG msg; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); if (msg.message == WM_QUIT) { @@ -219,9 +221,9 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou for (S32 j = 0; j < menus[i].item_count; j++) { PlatformMenuItem *item = &menus[i].items[j]; if (!item->label) { - AppendMenuW(submenu, MF_SEPARATOR, 0, nullptr); + AppendMenuW(submenu, MF_SEPARATOR, 0, NULL); } else { - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, nullptr, 0); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, item->label, -1, NULL, 0); wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, item->label, -1, wlabel, wchar_count); AppendMenuW(submenu, MF_STRING, (UINT_PTR)item->id, wlabel); @@ -229,7 +231,7 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_cou } } - int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, nullptr, 0); + int wchar_count = MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, NULL, 0); wchar_t *wlabel = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, menus[i].label, -1, wlabel, wchar_count); AppendMenuW(menu_bar, MF_POPUP, (UINT_PTR)submenu, wlabel); @@ -260,7 +262,7 @@ PlatformInput platform_get_input(PlatformWindow *window) { window->prev_mouse_down = result.mouse_down; // Clear accumulated events for next frame - window->input = {}; + memset(&window->input, 0, sizeof(window->input)); return result; } @@ -281,9 +283,9 @@ F32 platform_get_dpi_scale(PlatformWindow *window) { void platform_set_cursor(PlatformCursor cursor) { switch (cursor) { - case PLATFORM_CURSOR_SIZE_WE: g_current_cursor = LoadCursor(nullptr, IDC_SIZEWE); break; - case PLATFORM_CURSOR_SIZE_NS: g_current_cursor = LoadCursor(nullptr, IDC_SIZENS); break; - default: g_current_cursor = LoadCursor(nullptr, IDC_ARROW); break; + case PLATFORM_CURSOR_SIZE_WE: g_current_cursor = LoadCursor(NULL, IDC_SIZEWE); break; + case PLATFORM_CURSOR_SIZE_NS: g_current_cursor = LoadCursor(NULL, IDC_SIZENS); break; + default: g_current_cursor = LoadCursor(NULL, IDC_ARROW); break; } } @@ -293,7 +295,7 @@ void platform_clipboard_set(const char *text) { if (len == 0) return; // Convert UTF-8 to wide string for Windows clipboard - int wlen = MultiByteToWideChar(CP_UTF8, 0, text, len, nullptr, 0); + int wlen = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); if (wlen == 0) return; HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, (wlen + 1) * sizeof(wchar_t)); @@ -304,7 +306,7 @@ void platform_clipboard_set(const char *text) { wbuf[wlen] = L'\0'; GlobalUnlock(hmem); - HWND hwnd = g_main_window ? g_main_window->hwnd : nullptr; + HWND hwnd = g_main_window ? g_main_window->hwnd : NULL; if (OpenClipboard(hwnd)) { EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, hmem); @@ -318,21 +320,21 @@ const char *platform_clipboard_get() { static char buf[4096]; buf[0] = '\0'; - HWND hwnd = g_main_window ? g_main_window->hwnd : nullptr; - if (!OpenClipboard(hwnd)) return nullptr; + HWND hwnd = g_main_window ? g_main_window->hwnd : NULL; + if (!OpenClipboard(hwnd)) return NULL; HGLOBAL hmem = GetClipboardData(CF_UNICODETEXT); if (hmem) { wchar_t *wbuf = (wchar_t *)GlobalLock(hmem); if (wbuf) { - int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf) - 1, nullptr, nullptr); + int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf) - 1, NULL, NULL); buf[len > 0 ? len - 1 : 0] = '\0'; // WideCharToMultiByte includes null in count GlobalUnlock(hmem); } } CloseClipboard(); - return buf[0] ? buf : nullptr; + return buf[0] ? buf : NULL; } S32 platform_message_box(PlatformWindow *parent, const char *title, @@ -346,15 +348,15 @@ S32 platform_message_box(PlatformWindow *parent, const char *title, } // Convert UTF-8 to wide strings - int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0); + int title_wlen = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); wchar_t *wtitle = (wchar_t *)_malloca(title_wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, title, -1, wtitle, title_wlen); - int msg_wlen = MultiByteToWideChar(CP_UTF8, 0, message, -1, nullptr, 0); + int msg_wlen = MultiByteToWideChar(CP_UTF8, 0, message, -1, NULL, 0); wchar_t *wmsg = (wchar_t *)_malloca(msg_wlen * sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, message, -1, wmsg, msg_wlen); - HWND hwnd = parent ? parent->hwnd : nullptr; + HWND hwnd = parent ? parent->hwnd : NULL; int result = MessageBoxW(hwnd, wmsg, wtitle, mb_type); _freea(wmsg); diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 6b0d210..53d0ac3 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -1,13 +1,10 @@ #pragma once #include "base/base_core.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "base/base_math.h" +#include "clay.h" typedef struct Renderer Renderer; -struct Clay_RenderCommandArray; typedef struct RendererDesc { void *window_handle; @@ -26,7 +23,7 @@ Renderer *renderer_create_shared(Renderer *parent, RendererDesc *desc); void renderer_destroy(Renderer *renderer); B32 renderer_begin_frame(Renderer *renderer); -void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands); +void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray *render_commands); void renderer_resize(Renderer *renderer, S32 width, S32 height); void renderer_set_font_scale(Renderer *renderer, F32 scale); void renderer_sync_from_parent(Renderer *renderer); // sync shared font atlas from parent @@ -39,7 +36,3 @@ Vec2F32 renderer_measure_text(const char *text, S32 length, F32 font_size, void // Upload an RGBA8 icon atlas texture for icon rendering (4 bytes per pixel) void renderer_create_icon_atlas(Renderer *renderer, const U8 *data, S32 w, S32 h); - -#ifdef __cplusplus -} -#endif diff --git a/src/renderer/renderer_metal.mm b/src/renderer/renderer_metal.m similarity index 83% rename from src/renderer/renderer_metal.mm rename to src/renderer/renderer_metal.m index 6033f01..0a2012d 100644 --- a/src/renderer/renderer_metal.mm +++ b/src/renderer/renderer_metal.m @@ -6,6 +6,8 @@ #import #import #include +#include +#include // FreeType headers — temporarily undefine `internal` macro (base_core.h: #define internal static) // because FreeType uses `internal` as a struct field name. @@ -30,7 +32,7 @@ //////////////////////////////// // Vertex format — matches DX12 UIVertex exactly -struct UIVertex { +typedef struct UIVertex { float pos[2]; float uv[2]; float col[4]; @@ -40,120 +42,119 @@ struct UIVertex { float border_thickness; float softness; float mode; // 0 = rect SDF, 1 = textured -}; +} UIVertex; //////////////////////////////// // Glyph info -struct GlyphInfo { +typedef struct GlyphInfo { F32 u0, v0, u1, v1; F32 w, h; F32 x_advance; -}; +} GlyphInfo; //////////////////////////////// // Metal shader (MSL) — port of HLSL SDF shader -static const char *g_shader_msl = R"( -#include -using namespace metal; - -struct Vertex { - float2 pos [[attribute(0)]]; - float2 uv [[attribute(1)]]; - float4 col [[attribute(2)]]; - float2 rect_min [[attribute(3)]]; - float2 rect_max [[attribute(4)]]; - float4 corner_radii [[attribute(5)]]; - float border_thickness [[attribute(6)]]; - float softness [[attribute(7)]]; - float mode [[attribute(8)]]; -}; - -struct Fragment { - float4 pos [[position]]; - float2 uv; - float4 col; - float2 rect_min; - float2 rect_max; - float4 corner_radii; - float border_thickness; - float softness; - float mode; -}; - -struct Constants { - float2 viewport_size; -}; - -vertex Fragment vertex_main(Vertex in [[stage_in]], - constant Constants &cb [[buffer(1)]]) { - Fragment out; - float2 ndc; - ndc.x = (in.pos.x / cb.viewport_size.x) * 2.0 - 1.0; - ndc.y = 1.0 - (in.pos.y / cb.viewport_size.y) * 2.0; - out.pos = float4(ndc, 0.0, 1.0); - out.uv = in.uv; - out.col = in.col; - out.rect_min = in.rect_min; - out.rect_max = in.rect_max; - out.corner_radii = in.corner_radii; - out.border_thickness = in.border_thickness; - out.softness = in.softness; - out.mode = in.mode; - return out; -} - -float rounded_rect_sdf(float2 sample_pos, float2 rect_center, float2 rect_half_size, float radius) { - float2 d = abs(sample_pos - rect_center) - rect_half_size + float2(radius, radius); - return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - radius; -} - -fragment float4 fragment_main(Fragment in [[stage_in]], - texture2d font_tex [[texture(0)]], - sampler font_smp [[sampler(0)]]) { - float4 col = in.col; - - if (in.mode > 1.5) { - float4 tex = font_tex.sample(font_smp, in.uv); - col *= tex; - } else if (in.mode > 0.5) { - float alpha = font_tex.sample(font_smp, in.uv).r; - col.a *= alpha; - } else { - float2 pixel_pos = in.pos.xy; - float2 rect_center = (in.rect_min + in.rect_max) * 0.5; - float2 rect_half_size = (in.rect_max - in.rect_min) * 0.5; - float radius = (pixel_pos.x < rect_center.x) - ? ((pixel_pos.y < rect_center.y) ? in.corner_radii.x : in.corner_radii.w) - : ((pixel_pos.y < rect_center.y) ? in.corner_radii.y : in.corner_radii.z); - float softness = max(in.softness, 0.5); - float dist = rounded_rect_sdf(pixel_pos, rect_center, rect_half_size, radius); - - if (in.border_thickness > 0) { - float inner_dist = dist + in.border_thickness; - float outer_alpha = 1.0 - smoothstep(-softness, softness, dist); - float inner_alpha = smoothstep(-softness, softness, inner_dist); - col.a *= outer_alpha * inner_alpha; - } else { - col.a *= 1.0 - smoothstep(-softness, softness, dist); - } - } - - // Dither to reduce gradient banding (interleaved gradient noise) - float dither = fract(52.9829189 * fract(dot(in.pos.xy, float2(0.06711056, 0.00583715)))) - 0.5; - col.rgb += dither / 255.0; - - if (col.a < 0.002) discard_fragment(); - return col; -} -)"; +static const char *g_shader_msl = + "#include \n" + "using namespace metal;\n" + "\n" + "struct Vertex {\n" + " float2 pos [[attribute(0)]];\n" + " float2 uv [[attribute(1)]];\n" + " float4 col [[attribute(2)]];\n" + " float2 rect_min [[attribute(3)]];\n" + " float2 rect_max [[attribute(4)]];\n" + " float4 corner_radii [[attribute(5)]];\n" + " float border_thickness [[attribute(6)]];\n" + " float softness [[attribute(7)]];\n" + " float mode [[attribute(8)]];\n" + "};\n" + "\n" + "struct Fragment {\n" + " float4 pos [[position]];\n" + " float2 uv;\n" + " float4 col;\n" + " float2 rect_min;\n" + " float2 rect_max;\n" + " float4 corner_radii;\n" + " float border_thickness;\n" + " float softness;\n" + " float mode;\n" + "};\n" + "\n" + "struct Constants {\n" + " float2 viewport_size;\n" + "};\n" + "\n" + "vertex Fragment vertex_main(Vertex in [[stage_in]],\n" + " constant Constants &cb [[buffer(1)]]) {\n" + " Fragment out;\n" + " float2 ndc;\n" + " ndc.x = (in.pos.x / cb.viewport_size.x) * 2.0 - 1.0;\n" + " ndc.y = 1.0 - (in.pos.y / cb.viewport_size.y) * 2.0;\n" + " out.pos = float4(ndc, 0.0, 1.0);\n" + " out.uv = in.uv;\n" + " out.col = in.col;\n" + " out.rect_min = in.rect_min;\n" + " out.rect_max = in.rect_max;\n" + " out.corner_radii = in.corner_radii;\n" + " out.border_thickness = in.border_thickness;\n" + " out.softness = in.softness;\n" + " out.mode = in.mode;\n" + " return out;\n" + "}\n" + "\n" + "float rounded_rect_sdf(float2 sample_pos, float2 rect_center, float2 rect_half_size, float radius) {\n" + " float2 d = abs(sample_pos - rect_center) - rect_half_size + float2(radius, radius);\n" + " return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - radius;\n" + "}\n" + "\n" + "fragment float4 fragment_main(Fragment in [[stage_in]],\n" + " texture2d font_tex [[texture(0)]],\n" + " sampler font_smp [[sampler(0)]]) {\n" + " float4 col = in.col;\n" + "\n" + " if (in.mode > 1.5) {\n" + " float4 tex = font_tex.sample(font_smp, in.uv);\n" + " col *= tex;\n" + " } else if (in.mode > 0.5) {\n" + " float alpha = font_tex.sample(font_smp, in.uv).r;\n" + " col.a *= alpha;\n" + " } else {\n" + " float2 pixel_pos = in.pos.xy;\n" + " float2 rect_center = (in.rect_min + in.rect_max) * 0.5;\n" + " float2 rect_half_size = (in.rect_max - in.rect_min) * 0.5;\n" + " float radius = (pixel_pos.x < rect_center.x)\n" + " ? ((pixel_pos.y < rect_center.y) ? in.corner_radii.x : in.corner_radii.w)\n" + " : ((pixel_pos.y < rect_center.y) ? in.corner_radii.y : in.corner_radii.z);\n" + " float softness = max(in.softness, 0.5);\n" + " float dist = rounded_rect_sdf(pixel_pos, rect_center, rect_half_size, radius);\n" + "\n" + " if (in.border_thickness > 0) {\n" + " float inner_dist = dist + in.border_thickness;\n" + " float outer_alpha = 1.0 - smoothstep(-softness, softness, dist);\n" + " float inner_alpha = smoothstep(-softness, softness, inner_dist);\n" + " col.a *= outer_alpha * inner_alpha;\n" + " } else {\n" + " col.a *= 1.0 - smoothstep(-softness, softness, dist);\n" + " }\n" + " }\n" + "\n" + " // Dither to reduce gradient banding (interleaved gradient noise)\n" + " float dither = fract(52.9829189 * fract(dot(in.pos.xy, float2(0.06711056, 0.00583715)))) - 0.5;\n" + " col.rgb += dither / 255.0;\n" + "\n" + " if (col.a < 0.002) discard_fragment();\n" + " return col;\n" + "}\n"; //////////////////////////////// // Renderer struct -struct Renderer { - Renderer *parent; // non-null for shared renderers +typedef struct Renderer { + struct Renderer *parent; // non-null for shared renderers S32 width; S32 height; S32 frame_count; @@ -190,7 +191,7 @@ struct Renderer { // Clear color F32 clear_r, clear_g, clear_b; -}; +} Renderer; //////////////////////////////// // Font atlas (FreeType) @@ -302,12 +303,12 @@ Vec2F32 renderer_measure_text(const char *text, S32 length, F32 font_size, void //////////////////////////////// // Draw batch and quad emission (same logic as DX12) -struct DrawBatch { +typedef struct DrawBatch { UIVertex *vertices; U32 *indices; U32 vertex_count; U32 index_count; -}; +} DrawBatch; static void emit_quad(DrawBatch *batch, F32 x0, F32 y0, F32 x1, F32 y1, @@ -519,8 +520,7 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r, // Public API Renderer *renderer_create(RendererDesc *desc) { - Renderer *r = new Renderer(); - memset(r, 0, sizeof(*r)); + Renderer *r = (Renderer *)calloc(1, sizeof(Renderer)); r->width = desc->width; r->height = desc->height; @@ -532,7 +532,7 @@ Renderer *renderer_create(RendererDesc *desc) { [view setWantsLayer:YES]; r->device = MTLCreateSystemDefaultDevice(); - if (!r->device) { delete r; return nullptr; } + if (!r->device) { free(r); return NULL; } r->command_queue = [r->device newCommandQueue]; @@ -559,8 +559,8 @@ Renderer *renderer_create(RendererDesc *desc) { [NSString stringWithUTF8String:g_shader_msl] options:nil error:&error]; if (!library) { NSLog(@"Metal shader compile error: %@", error); - delete r; - return nullptr; + free(r); + return NULL; } id vert_fn = [library newFunctionWithName:@"vertex_main"]; @@ -615,8 +615,8 @@ Renderer *renderer_create(RendererDesc *desc) { r->pipeline_state = [r->device newRenderPipelineStateWithDescriptor:pipe_desc error:&error]; if (!r->pipeline_state) { NSLog(@"Metal pipeline error: %@", error); - delete r; - return nullptr; + free(r); + return NULL; } // Create F64-buffered vertex/index buffers @@ -630,8 +630,8 @@ Renderer *renderer_create(RendererDesc *desc) { // FreeType + font atlas init_freetype(r); if (!create_font_atlas(r, 22.0f)) { - delete r; - return nullptr; + free(r); + return NULL; } // Default clear color (dark theme bg_dark) @@ -643,10 +643,9 @@ Renderer *renderer_create(RendererDesc *desc) { } Renderer *renderer_create_shared(Renderer *parent, RendererDesc *desc) { - if (!parent) return nullptr; + if (!parent) return NULL; - Renderer *r = new Renderer(); - memset(r, 0, sizeof(*r)); + Renderer *r = (Renderer *)calloc(1, sizeof(Renderer)); r->parent = parent; r->width = desc->width; @@ -711,7 +710,7 @@ void renderer_destroy(Renderer *r) { if (r->ft_face) FT_Done_Face(r->ft_face); if (r->ft_lib) FT_Done_FreeType(r->ft_lib); } - delete r; + free(r); } B32 renderer_begin_frame(Renderer *r) { @@ -741,7 +740,47 @@ B32 renderer_begin_frame(Renderer *r) { return true; } -void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { +// Helper context for batch flushing (replaces C++ lambdas) +typedef struct FlushCtx { + DrawBatch *batch; + U32 *flush_index_start; + S32 *bound_texture; + Renderer *r; + U32 buf_idx; + id encoder; +} FlushCtx; + +static void flush_batch(FlushCtx *ctx) { + U32 index_count = ctx->batch->index_count - *ctx->flush_index_start; + if (index_count == 0) return; + + [ctx->encoder setVertexBuffer:ctx->r->vertex_buffers[ctx->buf_idx] offset:0 atIndex:0]; + [ctx->encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:index_count + indexType:MTLIndexTypeUInt32 + indexBuffer:ctx->r->index_buffers[ctx->buf_idx] + indexBufferOffset:*ctx->flush_index_start * sizeof(U32)]; + + *ctx->flush_index_start = ctx->batch->index_count; +} + +static void bind_font_texture(FlushCtx *ctx) { + if (*ctx->bound_texture != 0) { + flush_batch(ctx); + [ctx->encoder setFragmentTexture:ctx->r->font_texture atIndex:0]; + *ctx->bound_texture = 0; + } +} + +static void bind_icon_texture(FlushCtx *ctx) { + if (*ctx->bound_texture != 1 && ctx->r->icon_texture) { + flush_batch(ctx); + [ctx->encoder setFragmentTexture:ctx->r->icon_texture atIndex:0]; + *ctx->bound_texture = 1; + } +} + +void renderer_end_frame(Renderer *r, Clay_RenderCommandArray *render_commands) { @autoreleasepool { id drawable = r->current_drawable; r->current_drawable = nil; @@ -762,7 +801,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { [encoder setFragmentSamplerState:r->font_sampler atIndex:0]; // Viewport - MTLViewport viewport = {}; + MTLViewport viewport = {0}; viewport.width = (F64)r->width; viewport.height = (F64)r->height; viewport.zfar = 1.0; @@ -777,8 +816,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { [encoder setVertexBytes:constants length:sizeof(constants) atIndex:1]; // Process Clay render commands - if (render_commands.length > 0) { - DrawBatch batch = {}; + if (render_commands->length > 0) { + DrawBatch batch = {0}; batch.vertices = (UIVertex *)[r->vertex_buffers[buf_idx] contents]; batch.indices = (U32 *)[r->index_buffers[buf_idx] contents]; batch.vertex_count = 0; @@ -788,37 +827,15 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { S32 bound_texture = 0; U32 flush_index_start = 0; - auto flush_batch = [&]() { - U32 index_count = batch.index_count - flush_index_start; - if (index_count == 0) return; + FlushCtx fctx; + fctx.batch = &batch; + fctx.flush_index_start = &flush_index_start; + fctx.bound_texture = &bound_texture; + fctx.r = r; + fctx.buf_idx = buf_idx; + fctx.encoder = encoder; - [encoder setVertexBuffer:r->vertex_buffers[buf_idx] offset:0 atIndex:0]; - [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle - indexCount:index_count - indexType:MTLIndexTypeUInt32 - indexBuffer:r->index_buffers[buf_idx] - indexBufferOffset:flush_index_start * sizeof(U32)]; - - flush_index_start = batch.index_count; - }; - - 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 (S32 i = 0; i < render_commands.length; i++) { + for (S32 i = 0; i < render_commands->length; i++) { Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i); Clay_BoundingBox bb = cmd->boundingBox; @@ -877,7 +894,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { } break; case CLAY_RENDER_COMMAND_TYPE_TEXT: { - bind_font_texture(); + bind_font_texture(&fctx); Clay_TextRenderData *text = &cmd->renderData.text; emit_text_glyphs(&batch, r, bb, text->textColor, text->stringContents.chars, text->stringContents.length, @@ -885,7 +902,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { } break; case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: { - flush_batch(); + flush_batch(&fctx); NSUInteger sx = (NSUInteger)Max(bb.x, 0.f); NSUInteger sy = (NSUInteger)Max(bb.y, 0.f); NSUInteger sw = (NSUInteger)Min(bb.width, (F32)r->width - (F32)sx); @@ -897,7 +914,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { } break; case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: { - flush_batch(); + flush_batch(&fctx); [encoder setScissorRect:full_scissor]; } break; @@ -906,7 +923,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { if (custom->customData) { CustomRenderType type = *(CustomRenderType *)custom->customData; if (type == CUSTOM_RENDER_VGRADIENT) { - bind_font_texture(); + bind_font_texture(&fctx); CustomGradientData *grad = (CustomGradientData *)custom->customData; Clay_Color tc = grad->top_color; Clay_Color bc = grad->bottom_color; @@ -918,7 +935,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { custom->cornerRadius.bottomRight, custom->cornerRadius.bottomLeft, 1.0f); } else if (type == CUSTOM_RENDER_ICON) { - bind_icon_texture(); + bind_icon_texture(&fctx); CustomIconData *icon = (CustomIconData *)custom->customData; Clay_Color c = icon->color; F32 cr = c.r / 255.f, cg = c.g / 255.f; @@ -932,7 +949,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { 0, 0, 0, 0, 0, 0, 2.0f); } else if (type == CUSTOM_RENDER_ROTATED_ICON) { - bind_icon_texture(); + bind_icon_texture(&fctx); CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData; Clay_Color c = ri->color; F32 cr = c.r / 255.f, cg = c.g / 255.f; @@ -953,7 +970,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { } } - flush_batch(); + flush_batch(&fctx); } [encoder endEncoding]; diff --git a/src/renderer/renderer_vulkan.cpp b/src/renderer/renderer_vulkan.c similarity index 89% rename from src/renderer/renderer_vulkan.cpp rename to src/renderer/renderer_vulkan.c index ad74960..781d1af 100644 --- a/src/renderer/renderer_vulkan.cpp +++ b/src/renderer/renderer_vulkan.c @@ -14,12 +14,6 @@ #define VK_USE_PLATFORM_WIN32_KHR #include -// VMA — single-file implementation in this translation unit -#define VMA_IMPLEMENTATION -#define VMA_STATIC_VULKAN_FUNCTIONS 1 -#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0 -#include - // FreeType headers — temporarily undefine `internal` macro (base_core.h: #define internal static) // because FreeType uses `internal` as a struct field name. #undef internal @@ -33,6 +27,13 @@ #include "renderer/ui_vert.spv.h" #include "renderer/ui_frag.spv.h" +// MSVC ICE workaround: prevent inlining of large Vulkan setup functions +#ifdef _MSC_VER +#define VK_NOINLINE __declspec(noinline) +#else +#define VK_NOINLINE +#endif + #define NUM_BACK_BUFFERS 2 #define MAX_VERTICES (64 * 1024) #define MAX_INDICES (MAX_VERTICES * 3) @@ -95,8 +96,6 @@ struct Renderer { VkDevice device; VkQueue graphics_queue; U32 queue_family; - VmaAllocator allocator; - // Surface & swap chain VkSurfaceKHR surface; VkSwapchainKHR swap_chain; @@ -130,16 +129,16 @@ struct Renderer { // Per-frame vertex/index buffers (double-buffered, persistently mapped) VkBuffer vertex_buffers[NUM_BACK_BUFFERS]; - VmaAllocation vertex_allocs[NUM_BACK_BUFFERS]; + VkDeviceMemory vertex_memory[NUM_BACK_BUFFERS]; void *vb_mapped[NUM_BACK_BUFFERS]; VkBuffer index_buffers[NUM_BACK_BUFFERS]; - VmaAllocation index_allocs[NUM_BACK_BUFFERS]; + VkDeviceMemory index_memory[NUM_BACK_BUFFERS]; void *ib_mapped[NUM_BACK_BUFFERS]; // Font atlas VkImage font_image; - VmaAllocation font_alloc; + VkDeviceMemory font_memory; VkImageView font_view; GlyphInfo glyphs[GLYPH_COUNT]; F32 font_atlas_size; @@ -147,7 +146,7 @@ struct Renderer { // Icon atlas VkImage icon_image; - VmaAllocation icon_alloc; + VkDeviceMemory icon_memory; VkImageView icon_view; // FreeType @@ -242,7 +241,7 @@ static void transition_image(VkCommandBuffer cb, VkImage image, //////////////////////////////// // Vulkan infrastructure -static B32 create_instance(Renderer *r) { +static VK_NOINLINE B32 create_instance(Renderer *r) { VkApplicationInfo app_info = {0}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.pApplicationName = "autosample"; @@ -296,7 +295,7 @@ static B32 create_instance(Renderer *r) { return 1; } -static B32 create_surface(Renderer *r) { +static VK_NOINLINE B32 create_surface(Renderer *r) { VkWin32SurfaceCreateInfoKHR ci = {0}; ci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; ci.hinstance = GetModuleHandle(NULL); @@ -304,7 +303,7 @@ static B32 create_surface(Renderer *r) { return vkCreateWin32SurfaceKHR(r->instance, &ci, NULL, &r->surface) == VK_SUCCESS; } -static B32 pick_physical_device(Renderer *r) { +static VK_NOINLINE B32 pick_physical_device(Renderer *r) { U32 count = 0; vkEnumeratePhysicalDevices(r->instance, &count, NULL); if (count == 0) return 0; @@ -326,7 +325,7 @@ static B32 pick_physical_device(Renderer *r) { return 1; } -static B32 find_queue_family(Renderer *r) { +static VK_NOINLINE B32 find_queue_family(Renderer *r) { U32 count = 0; vkGetPhysicalDeviceQueueFamilyProperties(r->physical_device, &count, NULL); VkQueueFamilyProperties props[64]; @@ -344,7 +343,7 @@ static B32 find_queue_family(Renderer *r) { return 0; } -static B32 create_device(Renderer *r) { +static VK_NOINLINE B32 create_device(Renderer *r) { float priority = 1.0f; VkDeviceQueueCreateInfo queue_ci = {0}; queue_ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -368,16 +367,17 @@ static B32 create_device(Renderer *r) { return 1; } -static B32 create_vma(Renderer *r) { - VmaAllocatorCreateInfo ci = {0}; - ci.physicalDevice = r->physical_device; - ci.device = r->device; - ci.instance = r->instance; - ci.vulkanApiVersion = VK_API_VERSION_1_2; - return vmaCreateAllocator(&ci, &r->allocator) == VK_SUCCESS; +static U32 find_memory_type(VkPhysicalDevice phys_dev, U32 type_filter, VkMemoryPropertyFlags props) { + VkPhysicalDeviceMemoryProperties mem_props; + vkGetPhysicalDeviceMemoryProperties(phys_dev, &mem_props); + for (U32 i = 0; i < mem_props.memoryTypeCount; i++) { + if ((type_filter & (1 << i)) && (mem_props.memoryTypes[i].propertyFlags & props) == props) + return i; + } + return 0; // fallback } -static B32 create_swap_chain(Renderer *r) { +static VK_NOINLINE B32 create_swap_chain(Renderer *r) { VkSurfaceCapabilitiesKHR caps; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(r->physical_device, r->surface, &caps); @@ -468,7 +468,7 @@ static B32 create_swap_chain(Renderer *r) { return 1; } -static B32 create_render_pass(Renderer *r) { +static VK_NOINLINE B32 create_render_pass(Renderer *r) { VkAttachmentDescription color_attach = {0}; color_attach.format = r->swap_chain_format; color_attach.samples = VK_SAMPLE_COUNT_1_BIT; @@ -508,7 +508,7 @@ static B32 create_render_pass(Renderer *r) { return vkCreateRenderPass(r->device, &ci, NULL, &r->render_pass) == VK_SUCCESS; } -static B32 create_framebuffers(Renderer *r) { +static VK_NOINLINE B32 create_framebuffers(Renderer *r) { for (U32 i = 0; i < NUM_BACK_BUFFERS; i++) { VkFramebufferCreateInfo ci = {0}; ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; @@ -524,7 +524,7 @@ static B32 create_framebuffers(Renderer *r) { return 1; } -static B32 create_command_resources(Renderer *r) { +static VK_NOINLINE B32 create_command_resources(Renderer *r) { VkCommandPoolCreateInfo pool_ci = {0}; pool_ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; pool_ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; @@ -564,7 +564,7 @@ static B32 create_command_resources(Renderer *r) { return 1; } -static B32 create_sampler(Renderer *r) { +static VK_NOINLINE B32 create_sampler(Renderer *r) { VkSamplerCreateInfo ci = {0}; ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; ci.magFilter = VK_FILTER_LINEAR; @@ -576,7 +576,7 @@ static B32 create_sampler(Renderer *r) { return vkCreateSampler(r->device, &ci, NULL, &r->sampler) == VK_SUCCESS; } -static B32 create_descriptor_resources(Renderer *r) { +static VK_NOINLINE B32 create_descriptor_resources(Renderer *r) { // Descriptor set layout: single combined image sampler at binding 0 VkDescriptorSetLayoutBinding binding = {0}; binding.binding = 0; @@ -626,7 +626,7 @@ static VkShaderModule create_shader_module(Renderer *r, const U32 *code, size_t return module; } -static B32 create_pipeline(Renderer *r) { +static VK_NOINLINE B32 create_pipeline(Renderer *r) { VkShaderModule vert_module = create_shader_module(r, ui_vert_spv, sizeof(ui_vert_spv)); VkShaderModule frag_module = create_shader_module(r, ui_frag_spv, sizeof(ui_frag_spv)); if (!vert_module || !frag_module) return 0; @@ -647,7 +647,7 @@ static B32 create_pipeline(Renderer *r) { binding.stride = sizeof(UIVertex); binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - VkVertexInputAttributeDescription attrs[9] = {}; + VkVertexInputAttributeDescription attrs[9] = {0}; attrs[0].location = 0; attrs[0].binding = 0; attrs[0].format = VK_FORMAT_R32G32_SFLOAT; attrs[0].offset = offsetof(UIVertex, pos); attrs[1].location = 1; attrs[1].binding = 0; attrs[1].format = VK_FORMAT_R32G32_SFLOAT; attrs[1].offset = offsetof(UIVertex, uv); attrs[2].location = 2; attrs[2].binding = 0; attrs[2].format = VK_FORMAT_R32G32B32A32_SFLOAT; attrs[2].offset = offsetof(UIVertex, col); @@ -753,29 +753,42 @@ static B32 create_pipeline(Renderer *r) { return result == VK_SUCCESS; } -static B32 create_ui_buffers(Renderer *r) { +static VK_NOINLINE B32 create_ui_buffers(Renderer *r) { for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { + // Vertex buffer VkBufferCreateInfo buf_ci = {0}; buf_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buf_ci.size = MAX_VERTICES * sizeof(UIVertex); buf_ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; - alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - - VmaAllocationInfo alloc_info; - if (vmaCreateBuffer(r->allocator, &buf_ci, &alloc_ci, - &r->vertex_buffers[i], &r->vertex_allocs[i], &alloc_info) != VK_SUCCESS) + if (vkCreateBuffer(r->device, &buf_ci, NULL, &r->vertex_buffers[i]) != VK_SUCCESS) return 0; - r->vb_mapped[i] = alloc_info.pMappedData; + VkMemoryRequirements mem_req; + vkGetBufferMemoryRequirements(r->device, r->vertex_buffers[i], &mem_req); + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + if (vkAllocateMemory(r->device, &alloc_info, NULL, &r->vertex_memory[i]) != VK_SUCCESS) + return 0; + vkBindBufferMemory(r->device, r->vertex_buffers[i], r->vertex_memory[i], 0); + vkMapMemory(r->device, r->vertex_memory[i], 0, buf_ci.size, 0, &r->vb_mapped[i]); + // Index buffer buf_ci.size = MAX_INDICES * sizeof(U32); buf_ci.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - if (vmaCreateBuffer(r->allocator, &buf_ci, &alloc_ci, - &r->index_buffers[i], &r->index_allocs[i], &alloc_info) != VK_SUCCESS) + + if (vkCreateBuffer(r->device, &buf_ci, NULL, &r->index_buffers[i]) != VK_SUCCESS) return 0; - r->ib_mapped[i] = alloc_info.pMappedData; + vkGetBufferMemoryRequirements(r->device, r->index_buffers[i], &mem_req); + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + if (vkAllocateMemory(r->device, &alloc_info, NULL, &r->index_memory[i]) != VK_SUCCESS) + return 0; + vkBindBufferMemory(r->device, r->index_buffers[i], r->index_memory[i], 0); + vkMapMemory(r->device, r->index_memory[i], 0, buf_ci.size, 0, &r->ib_mapped[i]); } return 1; } @@ -807,7 +820,7 @@ static void init_freetype(Renderer *r) { FT_New_Memory_Face(r->ft_lib, font_inter_data, font_inter_size, 0, &r->ft_face); } -static B32 create_font_atlas(Renderer *r, F32 font_size) { +static VK_NOINLINE B32 create_font_atlas(Renderer *r, F32 font_size) { S32 pixel_size = (S32)(font_size + 0.5f); r->font_atlas_size = font_size; @@ -875,13 +888,24 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; - - if (vmaCreateImage(r->allocator, &img_ci, &alloc_ci, &r->font_image, &r->font_alloc, NULL) != VK_SUCCESS) { + if (vkCreateImage(r->device, &img_ci, NULL, &r->font_image) != VK_SUCCESS) { free(atlas_data); return 0; } + { + VkMemoryRequirements mem_req; + vkGetImageMemoryRequirements(r->device, r->font_image, &mem_req); + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (vkAllocateMemory(r->device, &alloc_info, NULL, &r->font_memory) != VK_SUCCESS) { + free(atlas_data); + return 0; + } + vkBindImageMemory(r->device, r->font_image, r->font_memory, 0); + } // Staging buffer VkBufferCreateInfo staging_ci = {0}; @@ -889,15 +913,23 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { staging_ci.size = FONT_ATLAS_W * FONT_ATLAS_H; staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo staging_alloc_ci = {0}; - staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; - staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - VkBuffer staging_buf; - VmaAllocation staging_alloc; - VmaAllocationInfo staging_info; - vmaCreateBuffer(r->allocator, &staging_ci, &staging_alloc_ci, &staging_buf, &staging_alloc, &staging_info); - memcpy(staging_info.pMappedData, atlas_data, FONT_ATLAS_W * FONT_ATLAS_H); + VkDeviceMemory staging_memory; + void *staging_mapped; + vkCreateBuffer(r->device, &staging_ci, NULL, &staging_buf); + { + VkMemoryRequirements mem_req; + vkGetBufferMemoryRequirements(r->device, staging_buf, &mem_req); + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + vkAllocateMemory(r->device, &alloc_info, NULL, &staging_memory); + vkBindBufferMemory(r->device, staging_buf, staging_memory, 0); + vkMapMemory(r->device, staging_memory, 0, staging_ci.size, 0, &staging_mapped); + } + memcpy(staging_mapped, atlas_data, FONT_ATLAS_W * FONT_ATLAS_H); free(atlas_data); // Copy via command buffer @@ -923,7 +955,8 @@ static B32 create_font_atlas(Renderer *r, F32 font_size) { end_one_shot(r, cb); - vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc); + vkDestroyBuffer(r->device, staging_buf, NULL); + vkFreeMemory(r->device, staging_memory, NULL); // Image view VkImageViewCreateInfo view_ci = {0}; @@ -1223,7 +1256,6 @@ Renderer *renderer_create(RendererDesc *desc) { if (!pick_physical_device(r)) goto fail; if (!find_queue_family(r)) goto fail; if (!create_device(r)) goto fail; - if (!create_vma(r)) goto fail; if (!create_command_resources(r)) goto fail; if (!create_sampler(r)) goto fail; if (!create_descriptor_resources(r)) goto fail; @@ -1261,18 +1293,17 @@ Renderer *renderer_create_shared(Renderer *parent, RendererDesc *desc) { r->device = parent->device; r->graphics_queue = parent->graphics_queue; r->queue_family = parent->queue_family; - r->allocator = parent->allocator; r->render_pass = parent->render_pass; r->pipeline_layout = parent->pipeline_layout; r->pipeline = parent->pipeline; r->descriptor_set_layout = parent->descriptor_set_layout; r->sampler = parent->sampler; r->font_image = parent->font_image; - r->font_alloc = parent->font_alloc; + r->font_memory = parent->font_memory; r->font_view = parent->font_view; r->font_descriptor_set = parent->font_descriptor_set; r->icon_image = parent->icon_image; - r->icon_alloc = parent->icon_alloc; + r->icon_memory = parent->icon_memory; r->icon_view = parent->icon_view; r->icon_descriptor_set = parent->icon_descriptor_set; r->descriptor_pool = parent->descriptor_pool; @@ -1308,10 +1339,14 @@ void renderer_destroy(Renderer *r) { // Per-window resources for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { - if (r->vertex_buffers[i]) - vmaDestroyBuffer(r->allocator, r->vertex_buffers[i], r->vertex_allocs[i]); - if (r->index_buffers[i]) - vmaDestroyBuffer(r->allocator, r->index_buffers[i], r->index_allocs[i]); + if (r->vertex_buffers[i]) { + vkDestroyBuffer(r->device, r->vertex_buffers[i], NULL); + vkFreeMemory(r->device, r->vertex_memory[i], NULL); + } + if (r->index_buffers[i]) { + vkDestroyBuffer(r->device, r->index_buffers[i], NULL); + vkFreeMemory(r->device, r->index_memory[i], NULL); + } } for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) { @@ -1333,9 +1368,9 @@ void renderer_destroy(Renderer *r) { // Shared resources only freed by root renderer if (!r->parent) { if (r->font_view) vkDestroyImageView(r->device, r->font_view, NULL); - if (r->font_image) vmaDestroyImage(r->allocator, r->font_image, r->font_alloc); + if (r->font_image) { vkDestroyImage(r->device, r->font_image, NULL); vkFreeMemory(r->device, r->font_memory, NULL); } if (r->icon_view) vkDestroyImageView(r->device, r->icon_view, NULL); - if (r->icon_image) vmaDestroyImage(r->allocator, r->icon_image, r->icon_alloc); + if (r->icon_image) { vkDestroyImage(r->device, r->icon_image, NULL); vkFreeMemory(r->device, r->icon_memory, NULL); } if (r->pipeline) vkDestroyPipeline(r->device, r->pipeline, NULL); if (r->pipeline_layout) vkDestroyPipelineLayout(r->device, r->pipeline_layout, NULL); @@ -1347,7 +1382,6 @@ void renderer_destroy(Renderer *r) { if (r->ft_face) FT_Done_Face(r->ft_face); if (r->ft_lib) FT_Done_FreeType(r->ft_lib); - if (r->allocator) vmaDestroyAllocator(r->allocator); if (r->device) vkDestroyDevice(r->device, NULL); #ifdef _DEBUG @@ -1385,7 +1419,7 @@ B32 renderer_begin_frame(Renderer *r) { return 1; } -void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { +void renderer_end_frame(Renderer *r, Clay_RenderCommandArray *render_commands) { U32 frame_idx = r->frame_index % NUM_BACK_BUFFERS; FrameContext *fc = &r->frames[frame_idx]; @@ -1440,7 +1474,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { vkCmdSetScissor(cb, 0, 1, &scissor); // Process Clay render commands - if (render_commands.length > 0) { + if (render_commands->length > 0) { DrawBatch batch = {0}; batch.vertices = (UIVertex *)r->vb_mapped[frame_idx]; batch.indices = (U32 *)r->ib_mapped[frame_idx]; @@ -1448,8 +1482,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) { S32 bound_texture = 0; // 0 = font, 1 = icon U32 flush_index_start = 0; - for (S32 i = 0; i < render_commands.length; i++) { - Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i); + for (S32 i = 0; i < render_commands->length; i++) { + Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(render_commands, i); Clay_BoundingBox bb = cmd->boundingBox; switch (cmd->commandType) { @@ -1655,10 +1689,18 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) { img_ci.tiling = VK_IMAGE_TILING_OPTIMAL; img_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - VmaAllocationCreateInfo alloc_ci = {0}; - alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; - - vmaCreateImage(r->allocator, &img_ci, &alloc_ci, &r->icon_image, &r->icon_alloc, NULL); + vkCreateImage(r->device, &img_ci, NULL, &r->icon_image); + { + VkMemoryRequirements mem_req; + vkGetImageMemoryRequirements(r->device, r->icon_image, &mem_req); + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vkAllocateMemory(r->device, &alloc_info, NULL, &r->icon_memory); + vkBindImageMemory(r->device, r->icon_image, r->icon_memory, 0); + } // Staging buffer VkBufferCreateInfo staging_ci = {0}; @@ -1666,15 +1708,23 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) { staging_ci.size = (VkDeviceSize)w * h * 4; staging_ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VmaAllocationCreateInfo staging_alloc_ci = {0}; - staging_alloc_ci.usage = VMA_MEMORY_USAGE_CPU_ONLY; - staging_alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - VkBuffer staging_buf; - VmaAllocation staging_alloc; - VmaAllocationInfo staging_info; - vmaCreateBuffer(r->allocator, &staging_ci, &staging_alloc_ci, &staging_buf, &staging_alloc, &staging_info); - memcpy(staging_info.pMappedData, data, (size_t)w * h * 4); + VkDeviceMemory staging_memory; + void *staging_mapped; + vkCreateBuffer(r->device, &staging_ci, NULL, &staging_buf); + { + VkMemoryRequirements mem_req; + vkGetBufferMemoryRequirements(r->device, staging_buf, &mem_req); + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = mem_req.size; + alloc_info.memoryTypeIndex = find_memory_type(r->physical_device, mem_req.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + vkAllocateMemory(r->device, &alloc_info, NULL, &staging_memory); + vkBindBufferMemory(r->device, staging_buf, staging_memory, 0); + vkMapMemory(r->device, staging_memory, 0, staging_ci.size, 0, &staging_mapped); + } + memcpy(staging_mapped, data, (size_t)w * h * 4); VkCommandBuffer cb = begin_one_shot(r); @@ -1697,7 +1747,8 @@ void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) { VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); end_one_shot(r, cb); - vmaDestroyBuffer(r->allocator, staging_buf, staging_alloc); + vkDestroyBuffer(r->device, staging_buf, NULL); + vkFreeMemory(r->device, staging_memory, NULL); // Image view VkImageViewCreateInfo view_ci = {0}; @@ -1756,7 +1807,7 @@ void renderer_set_font_scale(Renderer *r, F32 scale) { if (fabsf(target_size - r->font_atlas_size) < 0.1f) return; vkDeviceWaitIdle(r->device); if (r->font_view) { vkDestroyImageView(r->device, r->font_view, NULL); r->font_view = VK_NULL_HANDLE; } - if (r->font_image) { vmaDestroyImage(r->allocator, r->font_image, r->font_alloc); r->font_image = VK_NULL_HANDLE; } + if (r->font_image) { vkDestroyImage(r->device, r->font_image, NULL); vkFreeMemory(r->device, r->font_memory, NULL); r->font_image = VK_NULL_HANDLE; } create_font_atlas(r, target_size); } diff --git a/src/ui/ui_core.cpp b/src/ui/ui_core.c similarity index 56% rename from src/ui/ui_core.cpp rename to src/ui/ui_core.c index 9729161..98bf01b 100644 --- a/src/ui/ui_core.cpp +++ b/src/ui/ui_core.c @@ -1,13 +1,25 @@ -// ui_core.cpp - Clay wrapper implementation +// ui_core.c - Clay wrapper implementation // CLAY_IMPLEMENTATION must be defined exactly once before including clay.h. // In the unity build, ui_core.h (which includes clay.h) has #pragma once, // so we include clay.h directly here first to get the implementation compiled. #include +// MSVC: stub out Clay's debug view (uses CLAY() macros MSVC can't compile in C) +#ifdef _MSC_VER +#define CLAY_DISABLE_DEBUG_VIEW +#endif + #define CLAY_IMPLEMENTATION #include "clay.h" +// MSVC C mode: bypass Clay's wrapper struct pattern for designated initializers. +// Must come after clay.h so CLAY__CONFIG_WRAPPER is defined, then we override it. +#ifdef _MSC_VER +#undef CLAY__CONFIG_WRAPPER +#define CLAY__CONFIG_WRAPPER(type, ...) ((type) { __VA_ARGS__ }) +#endif + #include "ui/ui_core.h" //////////////////////////////// @@ -18,7 +30,7 @@ F32 g_ui_scale = 1.0f; //////////////////////////////// // Theme global -UI_Theme g_theme = {}; +UI_Theme g_theme = {0}; S32 g_theme_id = 0; void ui_set_theme(S32 theme_id) { @@ -27,54 +39,54 @@ void ui_set_theme(S32 theme_id) { switch (theme_id) { default: case 0: // Dark - g_theme.bg_dark = Clay_Color{ 26, 26, 26, 255}; - g_theme.bg_medium = Clay_Color{ 36, 36, 36, 255}; - g_theme.bg_light = Clay_Color{ 46, 46, 46, 255}; - g_theme.bg_lighter = Clay_Color{ 54, 54, 54, 255}; - g_theme.border = Clay_Color{ 52, 52, 52, 255}; - g_theme.text = Clay_Color{220, 220, 220, 255}; - g_theme.text_dim = Clay_Color{105, 105, 105, 255}; - g_theme.accent = Clay_Color{ 87, 138, 176, 255}; - g_theme.accent_hover = Clay_Color{102, 153, 191, 255}; - g_theme.button_text = Clay_Color{224, 224, 224, 255}; - g_theme.disabled_bg = Clay_Color{ 44, 44, 44, 255}; - g_theme.disabled_text = Clay_Color{ 90, 90, 90, 255}; - g_theme.header_bg = Clay_Color{ 46, 46, 46, 255}; - g_theme.title_bar = Clay_Color{ 22, 22, 22, 255}; - g_theme.scrollbar_bg = Clay_Color{ 22, 22, 22, 255}; - g_theme.scrollbar_grab = Clay_Color{ 58, 58, 58, 255}; - g_theme.shadow = Clay_Color{ 0, 0, 0, 30}; - g_theme.tab_active_top = Clay_Color{ 70, 120, 160, 255}; - g_theme.tab_active_bottom= Clay_Color{ 30, 55, 80, 255}; - g_theme.tab_inactive = Clay_Color{ 40, 40, 40, 255}; - g_theme.tab_inactive_hover= Clay_Color{ 50, 50, 50, 255}; - g_theme.tab_text = Clay_Color{240, 240, 240, 255}; + g_theme.bg_dark = (Clay_Color){ 26, 26, 26, 255}; + g_theme.bg_medium = (Clay_Color){ 36, 36, 36, 255}; + g_theme.bg_light = (Clay_Color){ 46, 46, 46, 255}; + g_theme.bg_lighter = (Clay_Color){ 54, 54, 54, 255}; + g_theme.border = (Clay_Color){ 52, 52, 52, 255}; + g_theme.text = (Clay_Color){220, 220, 220, 255}; + g_theme.text_dim = (Clay_Color){105, 105, 105, 255}; + g_theme.accent = (Clay_Color){ 87, 138, 176, 255}; + g_theme.accent_hover = (Clay_Color){102, 153, 191, 255}; + g_theme.button_text = (Clay_Color){224, 224, 224, 255}; + g_theme.disabled_bg = (Clay_Color){ 44, 44, 44, 255}; + g_theme.disabled_text = (Clay_Color){ 90, 90, 90, 255}; + g_theme.header_bg = (Clay_Color){ 46, 46, 46, 255}; + g_theme.title_bar = (Clay_Color){ 22, 22, 22, 255}; + g_theme.scrollbar_bg = (Clay_Color){ 22, 22, 22, 255}; + g_theme.scrollbar_grab = (Clay_Color){ 58, 58, 58, 255}; + g_theme.shadow = (Clay_Color){ 0, 0, 0, 30}; + g_theme.tab_active_top = (Clay_Color){ 70, 120, 160, 255}; + g_theme.tab_active_bottom= (Clay_Color){ 30, 55, 80, 255}; + g_theme.tab_inactive = (Clay_Color){ 40, 40, 40, 255}; + g_theme.tab_inactive_hover= (Clay_Color){ 50, 50, 50, 255}; + g_theme.tab_text = (Clay_Color){240, 240, 240, 255}; g_theme.corner_radius = 4.0f; break; case 1: // Light - g_theme.bg_dark = Clay_Color{195, 193, 190, 255}; - g_theme.bg_medium = Clay_Color{212, 210, 207, 255}; - g_theme.bg_light = Clay_Color{225, 223, 220, 255}; - g_theme.bg_lighter = Clay_Color{232, 230, 227, 255}; - g_theme.border = Clay_Color{178, 176, 173, 255}; - g_theme.text = Clay_Color{ 35, 33, 30, 255}; - g_theme.text_dim = Clay_Color{115, 113, 108, 255}; - g_theme.accent = Clay_Color{ 50, 110, 170, 255}; - g_theme.accent_hover = Clay_Color{ 65, 125, 185, 255}; - g_theme.button_text = Clay_Color{255, 255, 255, 255}; - g_theme.disabled_bg = Clay_Color{185, 183, 180, 255}; - g_theme.disabled_text = Clay_Color{145, 143, 140, 255}; - g_theme.header_bg = Clay_Color{202, 200, 197, 255}; - g_theme.title_bar = Clay_Color{202, 200, 197, 255}; - g_theme.scrollbar_bg = Clay_Color{202, 200, 197, 255}; - g_theme.scrollbar_grab = Clay_Color{168, 166, 163, 255}; - g_theme.shadow = Clay_Color{ 0, 0, 0, 18}; - g_theme.tab_active_top = Clay_Color{ 70, 130, 180, 255}; - g_theme.tab_active_bottom= Clay_Color{ 50, 100, 150, 255}; - g_theme.tab_inactive = Clay_Color{205, 203, 200, 255}; - g_theme.tab_inactive_hover= Clay_Color{195, 193, 190, 255}; - g_theme.tab_text = Clay_Color{255, 255, 255, 255}; + g_theme.bg_dark = (Clay_Color){195, 193, 190, 255}; + g_theme.bg_medium = (Clay_Color){212, 210, 207, 255}; + g_theme.bg_light = (Clay_Color){225, 223, 220, 255}; + g_theme.bg_lighter = (Clay_Color){232, 230, 227, 255}; + g_theme.border = (Clay_Color){178, 176, 173, 255}; + g_theme.text = (Clay_Color){ 35, 33, 30, 255}; + g_theme.text_dim = (Clay_Color){115, 113, 108, 255}; + g_theme.accent = (Clay_Color){ 50, 110, 170, 255}; + g_theme.accent_hover = (Clay_Color){ 65, 125, 185, 255}; + g_theme.button_text = (Clay_Color){255, 255, 255, 255}; + g_theme.disabled_bg = (Clay_Color){185, 183, 180, 255}; + g_theme.disabled_text = (Clay_Color){145, 143, 140, 255}; + g_theme.header_bg = (Clay_Color){202, 200, 197, 255}; + g_theme.title_bar = (Clay_Color){202, 200, 197, 255}; + g_theme.scrollbar_bg = (Clay_Color){202, 200, 197, 255}; + g_theme.scrollbar_grab = (Clay_Color){168, 166, 163, 255}; + g_theme.shadow = (Clay_Color){ 0, 0, 0, 18}; + g_theme.tab_active_top = (Clay_Color){ 70, 130, 180, 255}; + g_theme.tab_active_bottom= (Clay_Color){ 50, 100, 150, 255}; + g_theme.tab_inactive = (Clay_Color){205, 203, 200, 255}; + g_theme.tab_inactive_hover= (Clay_Color){195, 193, 190, 255}; + g_theme.tab_text = (Clay_Color){255, 255, 255, 255}; g_theme.corner_radius = 4.0f; break; } @@ -90,9 +102,9 @@ void ui_set_accent(S32 accent_id) { B32 dark = (g_theme_id == 0); // Each palette: accent, accent_hover, tab_top, tab_bottom, button_text, tab_text - struct AccentColors { + typedef struct AccentColors { Clay_Color accent, accent_hover, tab_top, tab_bottom, button_text, tab_text; - }; + } AccentColors; // Dark-mode palettes static const AccentColors dark_palettes[] = { @@ -134,13 +146,13 @@ void ui_set_accent(S32 accent_id) { if (idx < 0) idx = 0; if (idx > 6) idx = 6; - const AccentColors &c = dark ? dark_palettes[idx] : light_palettes[idx]; - g_theme.accent = c.accent; - g_theme.accent_hover = c.accent_hover; - g_theme.tab_active_top = c.tab_top; - g_theme.tab_active_bottom = c.tab_bottom; - g_theme.button_text = c.button_text; - g_theme.tab_text = c.tab_text; + const AccentColors *c = dark ? &dark_palettes[idx] : &light_palettes[idx]; + g_theme.accent = c->accent; + g_theme.accent_hover = c->accent_hover; + g_theme.tab_active_top = c->tab_top; + g_theme.tab_active_bottom = c->tab_bottom; + g_theme.button_text = c->button_text; + g_theme.tab_text = c->tab_text; } //////////////////////////////// @@ -158,15 +170,15 @@ static void clay_error_handler(Clay_ErrorData error) { // Clay text measurement bridge // Clay calls this; we forward to the app's UI_MeasureTextFn -static UI_Context *g_measure_ctx = nullptr; +static UI_Context *g_measure_ctx = NULL; static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *user_data) { UI_Context *ctx = (UI_Context *)user_data; if (!ctx || !ctx->measure_text_fn || text.length == 0) { - return Clay_Dimensions{0, (F32)config->fontSize}; + return (Clay_Dimensions){0, (F32)config->fontSize}; } Vec2F32 result = ctx->measure_text_fn(text.chars, text.length, (F32)config->fontSize, ctx->measure_text_user_data); - return Clay_Dimensions{result.x, result.y}; + return (Clay_Dimensions){result.x, result.y}; } //////////////////////////////// @@ -179,12 +191,12 @@ UI_Context *ui_create(F32 viewport_w, F32 viewport_h) { ctx->clay_memory = malloc(min_memory); Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(min_memory, ctx->clay_memory); - Clay_ErrorHandler err_handler = {}; + Clay_ErrorHandler err_handler = {0}; err_handler.errorHandlerFunction = clay_error_handler; err_handler.userData = ctx; ctx->clay_ctx = Clay_Initialize(clay_arena, - Clay_Dimensions{viewport_w, viewport_h}, + (Clay_Dimensions){viewport_w, viewport_h}, err_handler); Clay_SetMeasureTextFunction(clay_measure_text, ctx); @@ -204,9 +216,9 @@ void ui_begin_frame(UI_Context *ctx, F32 viewport_w, F32 viewport_h, { g_measure_ctx = ctx; Clay_SetCurrentContext(ctx->clay_ctx); - Clay_SetLayoutDimensions(Clay_Dimensions{viewport_w, viewport_h}); - Clay_SetPointerState(Clay_Vector2{mouse_pos.x, mouse_pos.y}, mouse_down != 0); - Clay_UpdateScrollContainers(false, Clay_Vector2{scroll_delta.x, scroll_delta.y}, dt); + Clay_SetLayoutDimensions((Clay_Dimensions){viewport_w, viewport_h}); + Clay_SetPointerState((Clay_Vector2){mouse_pos.x, mouse_pos.y}, mouse_down != 0); + Clay_UpdateScrollContainers(false, (Clay_Vector2){scroll_delta.x, scroll_delta.y}, dt); Clay_BeginLayout(); } diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 15d2b48..19c62ab 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -14,14 +14,14 @@ typedef Vec2F32 (*UI_MeasureTextFn)(const char *text, S32 length, F32 font_size, //////////////////////////////// // UI Context -struct UI_Context { +typedef struct UI_Context { Clay_Context *clay_ctx; void *clay_memory; // Text measurement UI_MeasureTextFn measure_text_fn; void *measure_text_user_data; -}; +} UI_Context; //////////////////////////////// // Lifecycle @@ -48,7 +48,7 @@ Vec2F32 ui_measure_text(const char *text, S32 length, F32 font_size); //////////////////////////////// // Theme colors (convenience - 0-255 Clay_Color) -struct UI_Theme { +typedef struct UI_Theme { Clay_Color bg_dark; Clay_Color bg_medium; Clay_Color bg_light; @@ -76,7 +76,7 @@ struct UI_Theme { // Corner radius (unscaled pixels, applied via uis()) F32 corner_radius; -}; +} UI_Theme; extern UI_Theme g_theme; extern S32 g_theme_id; @@ -116,30 +116,30 @@ static inline U16 uifs(F32 x) { return (U16)(x * g_ui_scale + 0.5f); } //////////////////////////////// // Custom render types (for gradient rects via CLAY_RENDER_COMMAND_TYPE_CUSTOM) -enum CustomRenderType { +typedef enum CustomRenderType { CUSTOM_RENDER_VGRADIENT = 1, CUSTOM_RENDER_ICON = 2, CUSTOM_RENDER_ROTATED_ICON = 3, -}; +} CustomRenderType; -struct CustomGradientData { +typedef struct CustomGradientData { CustomRenderType type; Clay_Color top_color; Clay_Color bottom_color; -}; +} CustomGradientData; -struct CustomIconData { +typedef struct CustomIconData { CustomRenderType type; // CUSTOM_RENDER_ICON S32 icon_id; Clay_Color color; -}; +} CustomIconData; -struct CustomRotatedIconData { +typedef struct CustomRotatedIconData { CustomRenderType type; // CUSTOM_RENDER_ROTATED_ICON S32 icon_id; Clay_Color color; F32 angle_rad; -}; +} CustomRotatedIconData; //////////////////////////////// // Font sizes diff --git a/src/ui/ui_icons.c b/src/ui/ui_icons.c new file mode 100644 index 0000000..f91f049 --- /dev/null +++ b/src/ui/ui_icons.c @@ -0,0 +1,219 @@ +// ui_icons.cpp - SVG icon rasterization via nanosvg + +#include "ui/ui_icons.h" +#include +#include + +#define NANOSVG_IMPLEMENTATION +#include "nanosvg.h" +#define NANOSVGRAST_IMPLEMENTATION +#include "nanosvgrast.h" + +UI_IconInfo g_icons[UI_ICON_COUNT] = {0}; + +// Simple SVG icon sources (24x24 viewBox) +static const char *g_icon_svgs[UI_ICON_COUNT] = { + // UI_ICON_CLOSE - X mark + "" + "" + "", + + // UI_ICON_CHECK - checkmark + "" + "" + "", + + // UI_ICON_CHEVRON_DOWN - downward arrow + "" + "" + "", + + // UI_ICON_KNOB - filled circle with indicator line pointing up (12 o'clock) + "" + "" + "" + "", + + // UI_ICON_SLIDER_THUMB - solid body with grip ridges + "" + "" + "" + "" + "" + "" + "", + + // UI_ICON_FADER - exact asset fader cap from assets/fader.svg + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "", + + // UI_ICON_TRANSPORT_REWIND - skip to start (|<<) + "" + "" + "" + "" + "", + + // UI_ICON_TRANSPORT_STOP - filled square + "" + "" + "", + + // UI_ICON_TRANSPORT_PLAY - right-pointing triangle + "" + "" + "", + + // UI_ICON_TRANSPORT_RECORD - filled circle + "" + "" + "", + + // UI_ICON_POP_OUT - box with arrow pointing out (top-right) + "" + "" + "" + "" + "", + + // UI_ICON_POP_IN - arrow pointing into a box (bottom-left) + "" + "" + "" + "" + "", +}; + +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 * 4, 1); + if (!atlas) return NULL; + + NSVGrasterizer *rast = nsvgCreateRasterizer(); + if (!rast) { free(atlas); return NULL; } + + S32 pen_x = 0; + for (S32 i = 0; i < UI_ICON_COUNT; i++) { + // nanosvg modifies the input string, so we need a mutable copy + U64 svg_len = strlen(g_icon_svgs[i]); + char *svg_copy = (char *)malloc(svg_len + 1); + memcpy(svg_copy, g_icon_svgs[i], svg_len + 1); + + NSVGimage *image = nsvgParse(svg_copy, "px", 96.0f); + free(svg_copy); + if (!image) continue; + + // Calculate scale to fit icon_size + F32 scale_x = (F32)icon_size / image->width; + F32 scale_y = (F32)icon_size / image->height; + F32 scale = scale_x < scale_y ? scale_x : scale_y; + + // Rasterize to a temporary RGBA buffer + U8 *tmp = (U8 *)calloc(icon_size * icon_size * 4, 1); + if (tmp) { + nsvgRasterize(rast, image, 0, 0, scale, tmp, icon_size, icon_size, icon_size * 4); + + // Copy into atlas (nanosvg outputs RGBA, which is what we want) + for (S32 y = 0; y < icon_size && y < atlas_h; y++) { + for (S32 x = 0; x < icon_size && (pen_x + x) < atlas_w; x++) { + S32 src_idx = (y * icon_size + x) * 4; + S32 dst_idx = (y * atlas_w + pen_x + x) * 4; + atlas[dst_idx + 0] = tmp[src_idx + 0]; + atlas[dst_idx + 1] = tmp[src_idx + 1]; + atlas[dst_idx + 2] = tmp[src_idx + 2]; + atlas[dst_idx + 3] = tmp[src_idx + 3]; + } + } + free(tmp); + } + + S32 bmp_w = icon_size; + S32 bmp_h = icon_size; + + // 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; + nsvgDelete(image); + } + + nsvgDeleteRasterizer(rast); + + *out_w = atlas_w; + *out_h = atlas_h; + return atlas; +} diff --git a/src/ui/ui_icons.cpp b/src/ui/ui_icons.cpp deleted file mode 100644 index c1641c9..0000000 --- a/src/ui/ui_icons.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// ui_icons.cpp - SVG icon rasterization via lunasvg - -#include "ui/ui_icons.h" -#include -#include -#include - -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"( - - )", - - // UI_ICON_CHECK - checkmark - R"( - - )", - - // UI_ICON_CHEVRON_DOWN - downward arrow - R"( - - )", - - // UI_ICON_KNOB - filled circle with indicator line pointing up (12 o'clock) - R"( - - - )", - - // UI_ICON_SLIDER_THUMB - solid body with grip ridges - R"( - - - - - - )", - - // UI_ICON_FADER - exact asset fader cap from assets/fader.svg - R"SVG( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)SVG", - - // UI_ICON_TRANSPORT_REWIND - skip to start (|<<) - R"( - - - - )", - - // UI_ICON_TRANSPORT_STOP - filled square - R"( - - )", - - // UI_ICON_TRANSPORT_PLAY - right-pointing triangle - R"( - - )", - - // UI_ICON_TRANSPORT_RECORD - filled circle - R"( - - )", - - // UI_ICON_POP_OUT - box with arrow pointing out (top-right) - R"( - - - - )", - - // UI_ICON_POP_IN - arrow pointing into a box (bottom-left) - R"( - - - - )", -}; - -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 * 4, 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; - - // Copy BGRA premultiplied → RGBA straight (un-premultiply) - 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++) { - U8 *s = &src[y * stride + x * 4]; - S32 dst_idx = (y * atlas_w + pen_x + x) * 4; - U8 b = s[0], g = s[1], r = s[2], a = s[3]; - if (a > 0 && a < 255) { - r = (U8)((r * 255) / a); - g = (U8)((g * 255) / a); - b = (U8)((b * 255) / a); - } - atlas[dst_idx + 0] = r; - atlas[dst_idx + 1] = g; - atlas[dst_idx + 2] = b; - atlas[dst_idx + 3] = 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; -} diff --git a/src/ui/ui_icons.h b/src/ui/ui_icons.h index 0bf8973..243a2ba 100644 --- a/src/ui/ui_icons.h +++ b/src/ui/ui_icons.h @@ -1,9 +1,9 @@ #pragma once -// ui_icons.h - SVG icon definitions and atlas rasterization via lunasvg +// ui_icons.h - SVG icon definitions and atlas rasterization via nanosvg #include "base/base_inc.h" -enum UI_IconID { +typedef enum UI_IconID { UI_ICON_CLOSE, UI_ICON_CHECK, UI_ICON_CHEVRON_DOWN, @@ -17,12 +17,12 @@ enum UI_IconID { UI_ICON_POP_OUT, UI_ICON_POP_IN, UI_ICON_COUNT -}; +} UI_IconID; -struct UI_IconInfo { +typedef struct UI_IconInfo { F32 u0, v0, u1, v1; // UV coordinates in icon atlas F32 w, h; // pixel dimensions at rasterized size -}; +} UI_IconInfo; extern UI_IconInfo g_icons[UI_ICON_COUNT]; diff --git a/src/ui/ui_piano.cpp b/src/ui/ui_piano.c similarity index 96% rename from src/ui/ui_piano.cpp rename to src/ui/ui_piano.c index 7354348..ac3240f 100644 --- a/src/ui/ui_piano.cpp +++ b/src/ui/ui_piano.c @@ -23,7 +23,7 @@ Clay_Color piano_velocity_color(S32 velocity) { g = 175.0f + s * (50.0f - 175.0f); b = 80.0f + s * (40.0f - 80.0f); } - return Clay_Color{r, g, b, 255}; + return (Clay_Color){r, g, b, 255}; } void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) { @@ -54,7 +54,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) } else if (mouse_held) { bg = g_theme.accent; } else { - bg = Clay_Color{240, 240, 240, 255}; + bg = (Clay_Color){240, 240, 240, 255}; } CLAY(CLAY_IDI("PKey", note), @@ -79,7 +79,7 @@ void ui_piano(UI_PianoState *state, MidiEngine *midi, F32 avail_w, F32 avail_h) } else if (mouse_held) { bg = g_theme.accent; } else { - bg = Clay_Color{25, 25, 30, 255}; + bg = (Clay_Color){25, 25, 30, 255}; } CLAY(CLAY_IDI("PKey", note), diff --git a/src/ui/ui_piano.h b/src/ui/ui_piano.h index d883365..ddc4bfa 100644 --- a/src/ui/ui_piano.h +++ b/src/ui/ui_piano.h @@ -5,9 +5,9 @@ #define PIANO_FIRST_NOTE 21 // A0 #define PIANO_LAST_NOTE 108 // C8 -struct UI_PianoState { +typedef struct UI_PianoState { S32 mouse_note; // MIDI note held by mouse click (-1 = none) -}; +} UI_PianoState; B32 piano_is_black_key(S32 note); Clay_Color piano_velocity_color(S32 velocity); diff --git a/src/ui/ui_popups.cpp b/src/ui/ui_popups.c similarity index 91% rename from src/ui/ui_popups.cpp rename to src/ui/ui_popups.c index 4f938d8..b2a0d6b 100644 --- a/src/ui/ui_popups.cpp +++ b/src/ui/ui_popups.c @@ -12,7 +12,7 @@ PopupWindow *popup_find_by_flag(B32 *flag) { for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) if (g_popups[i].alive && g_popups[i].open_flag == flag) return &g_popups[i]; - return nullptr; + return NULL; } PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer, @@ -21,16 +21,16 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer UI_WindowContentFn content_fn, void *user_data, PlatformWindowStyle style, B32 independent) { // Find free slot - PopupWindow *popup = nullptr; + PopupWindow *popup = NULL; for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) { if (!g_popups[i].alive) { popup = &g_popups[i]; break; } } - if (!popup) return nullptr; + if (!popup) return NULL; memset(popup, 0, sizeof(*popup)); // Create native popup window - PlatformWindowDesc desc = {}; + PlatformWindowDesc desc = {0}; desc.title = title; desc.width = width; desc.height = height; @@ -38,20 +38,20 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer desc.parent = parent_window; desc.independent = independent; popup->platform_window = platform_create_window(&desc); - if (!popup->platform_window) return nullptr; + if (!popup->platform_window) return NULL; // Create shared renderer S32 pw, ph; platform_get_size(popup->platform_window, &pw, &ph); - RendererDesc rdesc = {}; + RendererDesc rdesc = {0}; rdesc.window_handle = platform_get_native_handle(popup->platform_window); rdesc.width = pw; rdesc.height = ph; popup->renderer = renderer_create_shared(parent_renderer, &rdesc); if (!popup->renderer) { platform_destroy_window(popup->platform_window); - return nullptr; + return NULL; } // Create UI context @@ -67,7 +67,7 @@ PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer popup->last_w = pw; popup->last_h = ph; popup->title = title; - popup->wstate = {}; + memset(&popup->wstate, 0, sizeof(popup->wstate)); platform_set_frame_callback(popup->platform_window, popup_frame_callback, popup); @@ -81,7 +81,7 @@ void popup_close(PopupWindow *popup) { *popup->open_flag = 0; popup->alive = 0; - platform_set_frame_callback(popup->platform_window, nullptr, nullptr); + platform_set_frame_callback(popup->platform_window, NULL, NULL); ui_destroy(popup->ui_ctx); renderer_destroy(popup->renderer); @@ -151,15 +151,15 @@ void popup_do_frame(PopupWindow *popup, F32 dt) { g_wstate = saved_wstate; // Render - renderer_end_frame(popup->renderer, render_commands); + renderer_end_frame(popup->renderer, &render_commands); } -void popup_close_all() { +void popup_close_all(void) { for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) if (g_popups[i].alive) popup_close(&g_popups[i]); } -void popup_close_check() { +void popup_close_check(void) { for (S32 i = 0; i < MAX_POPUP_WINDOWS; i++) { if (g_popups[i].alive && platform_window_should_close(g_popups[i].platform_window)) popup_close(&g_popups[i]); diff --git a/src/ui/ui_popups.h b/src/ui/ui_popups.h index b4c8396..e81285b 100644 --- a/src/ui/ui_popups.h +++ b/src/ui/ui_popups.h @@ -6,7 +6,7 @@ #define MAX_POPUP_WINDOWS 4 -struct PopupWindow { +typedef struct PopupWindow { B32 alive; B32 *open_flag; // e.g. &app->show_settings_window PlatformWindow *platform_window; @@ -18,14 +18,14 @@ struct PopupWindow { S32 width, height; S32 last_w, last_h; const char *title; -}; +} PopupWindow; PopupWindow *popup_open(PlatformWindow *parent_window, Renderer *parent_renderer, const char *title, B32 *open_flag, S32 width, S32 height, UI_WindowContentFn content_fn, void *user_data, - PlatformWindowStyle style = PLATFORM_WINDOW_STYLE_POPUP, - B32 independent = 0); + PlatformWindowStyle style, + B32 independent); void popup_close(PopupWindow *popup); PopupWindow *popup_find_by_flag(B32 *flag); void popup_do_frame(PopupWindow *popup, F32 dt); diff --git a/src/ui/ui_widgets.cpp b/src/ui/ui_widgets.c similarity index 96% rename from src/ui/ui_widgets.cpp rename to src/ui/ui_widgets.c index 7444758..d96ca51 100644 --- a/src/ui/ui_widgets.cpp +++ b/src/ui/ui_widgets.c @@ -10,7 +10,7 @@ #include #include -UI_WidgetState g_wstate = {}; +UI_WidgetState g_wstate = {0}; // Icon per-frame pool (forward declaration for begin_frame) #define UI_MAX_ICONS_PER_FRAME 32 @@ -33,7 +33,7 @@ static char g_knob_text_bufs[UI_MAX_KNOB_TEXT_BUFS][32]; static S32 g_knob_text_buf_count = 0; static CustomGradientData *alloc_gradient(Clay_Color top, Clay_Color bottom) { - if (g_grad_pool_count >= UI_MAX_GRADIENTS_PER_FRAME) return nullptr; + if (g_grad_pool_count >= UI_MAX_GRADIENTS_PER_FRAME) return NULL; CustomGradientData *g = &g_grad_pool[g_grad_pool_count++]; g->type = CUSTOM_RENDER_VGRADIENT; g->top_color = top; @@ -120,7 +120,7 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius, } void ui_widgets_init() { - g_wstate = {}; + memset(&g_wstate, 0, sizeof(g_wstate)); } void ui_widgets_begin_frame(PlatformInput input) { @@ -171,43 +171,43 @@ static void ensure_widget_text_configs() { if (g_widget_text_configs_scale == g_ui_scale) return; g_widget_text_configs_scale = g_ui_scale; - g_widget_text_config = {}; + memset(&g_widget_text_config, 0, sizeof(g_widget_text_config)); g_widget_text_config.textColor = g_theme.text; g_widget_text_config.fontSize = FONT_SIZE_NORMAL; g_widget_text_config.wrapMode = CLAY_TEXT_WRAP_NONE; - g_widget_text_config_dim = {}; + memset(&g_widget_text_config_dim, 0, sizeof(g_widget_text_config_dim)); g_widget_text_config_dim.textColor = g_theme.text_dim; g_widget_text_config_dim.fontSize = FONT_SIZE_NORMAL; g_widget_text_config_dim.wrapMode = CLAY_TEXT_WRAP_NONE; // Selected text: white on accent background (background set on parent element) - g_widget_text_config_sel = {}; - g_widget_text_config_sel.textColor = Clay_Color{255, 255, 255, 255}; + memset(&g_widget_text_config_sel, 0, sizeof(g_widget_text_config_sel)); + g_widget_text_config_sel.textColor = (Clay_Color){255, 255, 255, 255}; g_widget_text_config_sel.fontSize = FONT_SIZE_NORMAL; g_widget_text_config_sel.wrapMode = CLAY_TEXT_WRAP_NONE; // Button text (always readable on accent background) - g_widget_text_config_btn = {}; + memset(&g_widget_text_config_btn, 0, sizeof(g_widget_text_config_btn)); g_widget_text_config_btn.textColor = g_theme.button_text; g_widget_text_config_btn.fontSize = FONT_SIZE_NORMAL; g_widget_text_config_btn.wrapMode = CLAY_TEXT_WRAP_NONE; // Tab text (always light — readable on colored tab gradient) - g_widget_text_config_tab = {}; + memset(&g_widget_text_config_tab, 0, sizeof(g_widget_text_config_tab)); g_widget_text_config_tab.textColor = g_theme.tab_text; g_widget_text_config_tab.fontSize = FONT_SIZE_TAB; g_widget_text_config_tab.wrapMode = CLAY_TEXT_WRAP_NONE; // Inactive tab text (theme text color, smaller font) - g_widget_text_config_tab_inactive = {}; + memset(&g_widget_text_config_tab_inactive, 0, sizeof(g_widget_text_config_tab_inactive)); g_widget_text_config_tab_inactive.textColor = g_theme.text; g_widget_text_config_tab_inactive.fontSize = FONT_SIZE_TAB; g_widget_text_config_tab_inactive.wrapMode = CLAY_TEXT_WRAP_NONE; } static Clay_String clay_str(const char *s) { - Clay_String r = {}; + Clay_String r = {0}; r.isStaticallyAllocated = false; r.length = (S32)strlen(s); r.chars = s; @@ -926,7 +926,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected) Clay_TextElementConfig *item_text = (is_item_selected && !item_hovered) ? &g_widget_text_config_btn : &g_widget_text_config; - Clay_CornerRadius item_radius = {}; + Clay_CornerRadius item_radius = {0}; if (i == 0) { item_radius.topLeft = CORNER_RADIUS; item_radius.topRight = CORNER_RADIUS; } if (i == count - 1) { item_radius.bottomLeft = CORNER_RADIUS; item_radius.bottomRight = CORNER_RADIUS; } @@ -1008,7 +1008,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { CLAY_TEXT(lbl_str, &g_widget_text_config_tab); } } else { - Clay_Color bg = hovered ? (Clay_Color)TAB_INACTIVE_HOVER : (Clay_Color)TAB_INACTIVE_BG; + Clay_Color bg = hovered ? TAB_INACTIVE_HOVER : TAB_INACTIVE_BG; CLAY(tab_eid, .layout = { .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIXED(TAB_HEIGHT) }, @@ -1033,6 +1033,18 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) { //////////////////////////////// // Shared value-edit helpers (used by knob, sliders, fader) +// Helper: delete selected range from knob edit buffer. +// Returns new string length. +static S32 ke_delete_sel(char *ebuf, S32 elen, S32 *ecur, S32 *esel0, S32 *esel1) { + S32 lo = (*esel0 < *esel1 ? *esel0 : *esel1); + S32 hi = (*esel0 < *esel1 ? *esel1 : *esel0); + if (lo == hi) return elen; + memmove(&ebuf[lo], &ebuf[hi], elen - hi + 1); + *ecur = lo; + *esel0 = *ecur; *esel1 = *ecur; + return elen - (hi - lo); +} + // Process keyboard input for value text editing. // Returns 0 = still editing, 1 = committed, 2 = cancelled. static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *changed) { @@ -1054,15 +1066,6 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * #define KE_SEL_HI() (*esel0 < *esel1 ? *esel1 : *esel0) #define KE_CLEAR_SEL() do { *esel0 = *ecur; *esel1 = *ecur; } while(0) - auto ke_delete_sel = [&]() -> S32 { - S32 lo = KE_SEL_LO(), hi = KE_SEL_HI(); - if (lo == hi) return elen; - memmove(&ebuf[lo], &ebuf[hi], elen - hi + 1); - *ecur = lo; - KE_CLEAR_SEL(); - return elen - (hi - lo); - }; - for (S32 k = 0; k < g_wstate.input.key_count; k++) { U8 key = g_wstate.input.keys[k]; @@ -1085,14 +1088,14 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * char tmp[32]; S32 n = hi - lo; if (n > 31) n = 31; memcpy(tmp, &ebuf[lo], n); tmp[n] = '\0'; platform_clipboard_set(tmp); - elen = ke_delete_sel(); + elen = ke_delete_sel(ebuf, elen, ecur, esel0, esel1); } continue; } if (key == PKEY_V) { const char *clip = platform_clipboard_get(); if (clip) { - if (KE_HAS_SEL()) elen = ke_delete_sel(); + if (KE_HAS_SEL()) elen = ke_delete_sel(ebuf, elen, ecur, esel0, esel1); S32 clip_len = (S32)strlen(clip); char filtered[32]; S32 flen = 0; for (S32 i = 0; i < clip_len && flen < 30; i++) { @@ -1117,14 +1120,14 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * if (key == PKEY_RETURN) { commit = 1; } else if (key == PKEY_ESCAPE) { cancel = 1; } else if (key == PKEY_BACKSPACE) { - if (KE_HAS_SEL()) { elen = ke_delete_sel(); } + if (KE_HAS_SEL()) { elen = ke_delete_sel(ebuf, elen, ecur, esel0, esel1); } else if (*ecur > 0) { memmove(&ebuf[*ecur - 1], &ebuf[*ecur], elen - *ecur + 1); (*ecur)--; elen--; } KE_CLEAR_SEL(); } else if (key == PKEY_DELETE) { - if (KE_HAS_SEL()) { elen = ke_delete_sel(); } + if (KE_HAS_SEL()) { elen = ke_delete_sel(ebuf, elen, ecur, esel0, esel1); } else if (*ecur < elen) { memmove(&ebuf[*ecur], &ebuf[*ecur + 1], elen - *ecur); elen--; @@ -1146,7 +1149,7 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * U16 ch = g_wstate.input.chars[c]; B32 valid = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-'; if (valid) { - if (KE_HAS_SEL()) elen = ke_delete_sel(); + if (KE_HAS_SEL()) elen = ke_delete_sel(ebuf, elen, ecur, esel0, esel1); if (elen < 30) { memmove(&ebuf[*ecur + 1], &ebuf[*ecur], elen - *ecur + 1); ebuf[*ecur] = (char)ch; (*ecur)++; elen++; @@ -1162,7 +1165,7 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 * #undef KE_CLEAR_SEL if (commit) { - char *end = nullptr; + char *end = NULL; F32 parsed = strtof(ebuf, &end); if (end != ebuf) { F32 lo = is_signed ? -max_val : 0.0f; @@ -1199,13 +1202,13 @@ static void value_edit_render(U32 hash, F32 width) { static char ke_dbuf_sel[32]; static char ke_dbuf_after[32]; - static Clay_TextElementConfig edit_cfg = {}; + static Clay_TextElementConfig edit_cfg = {0}; edit_cfg.textColor = g_theme.text; edit_cfg.fontSize = FONT_SIZE_SMALL; edit_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; - static Clay_TextElementConfig edit_sel_cfg = {}; - edit_sel_cfg.textColor = Clay_Color{255, 255, 255, 255}; + static Clay_TextElementConfig edit_sel_cfg = {0}; + edit_sel_cfg.textColor = (Clay_Color){255, 255, 255, 255}; edit_sel_cfg.fontSize = FONT_SIZE_SMALL; edit_sel_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1266,7 +1269,7 @@ static Clay_ElementId value_edit_eid(U32 hash) { // Handle click-outside to commit the edit. static void value_edit_click_away(Clay_ElementId eid, F32 *value, F32 max_val, B32 is_signed, B32 *changed) { if (g_wstate.mouse_clicked && !Clay_PointerOver(eid)) { - char *end = nullptr; + char *end = NULL; F32 parsed = strtof(g_wstate.knob_edit_buf, &end); if (end != g_wstate.knob_edit_buf) { F32 lo = is_signed ? -max_val : 0.0f; @@ -1305,7 +1308,7 @@ static F32 value_normalize(F32 value, F32 max_val, B32 is_signed) { // Format a value into a text buf from the pool. Returns null if pool exhausted. static char *value_format_text(F32 value, B32 is_signed, S32 *out_len) { - if (g_knob_text_buf_count >= UI_MAX_KNOB_TEXT_BUFS) return nullptr; + if (g_knob_text_buf_count >= UI_MAX_KNOB_TEXT_BUFS) return NULL; char *buf = g_knob_text_bufs[g_knob_text_buf_count++]; if (is_signed) { *out_len = snprintf(buf, 32, "%+.1f", value); } else { *out_len = snprintf(buf, 32, "%.1f", value); } @@ -1364,7 +1367,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s // Format value text S32 val_len = 0; - char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); + char *val_text = is_editing ? NULL : value_format_text(*value, is_signed, &val_len); // Layout: vertical column (knob → value text → label) CLAY(WID(id), @@ -1432,7 +1435,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s B32 val_hovered = Clay_PointerOver(val_eid); Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; - static Clay_TextElementConfig knob_val_cfg = {}; + static Clay_TextElementConfig knob_val_cfg = {0}; knob_val_cfg.textColor = g_theme.text; knob_val_cfg.fontSize = FONT_SIZE_SMALL; knob_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1507,7 +1510,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 // Format value text S32 val_len = 0; - char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); + char *val_text = is_editing ? NULL : value_format_text(*value, is_signed, &val_len); // Dimmed accent for fill bar Clay_Color fill_color = g_theme.accent; @@ -1611,7 +1614,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 B32 val_hovered = Clay_PointerOver(val_eid); Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; - static Clay_TextElementConfig sl_val_cfg = {}; + static Clay_TextElementConfig sl_val_cfg = {0}; sl_val_cfg.textColor = g_theme.text; sl_val_cfg.fontSize = FONT_SIZE_SMALL; sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1683,7 +1686,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 // Format value text S32 val_len = 0; - char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); + char *val_text = is_editing ? NULL : value_format_text(*value, is_signed, &val_len); // Dimmed accent for fill bar Clay_Color fill_color = g_theme.accent; @@ -1788,7 +1791,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 B32 val_hovered = Clay_PointerOver(val_eid); Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; - static Clay_TextElementConfig sl_val_cfg = {}; + static Clay_TextElementConfig sl_val_cfg = {0}; sl_val_cfg.textColor = g_theme.text; sl_val_cfg.fontSize = FONT_SIZE_SMALL; sl_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; @@ -1860,7 +1863,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_ // Format value text S32 val_len = 0; - char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len); + char *val_text = is_editing ? NULL : value_format_text(*value, is_signed, &val_len); // Tick mark dimensions F32 tick_major_w = WIDGET_FADER_TICK_MAJOR_W; @@ -1954,7 +1957,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_ CustomIconData *idata = &g_icon_pool[g_icon_pool_count++]; idata->type = CUSTOM_RENDER_ICON; idata->icon_id = (S32)UI_ICON_FADER; - idata->color = Clay_Color{255, 255, 255, 255}; + idata->color = (Clay_Color){255, 255, 255, 255}; F32 cap_y = (1.0f - normalized) * (track_h - cap_h); @@ -2026,7 +2029,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_ B32 val_hovered = Clay_PointerOver(val_eid); Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; - static Clay_TextElementConfig fdr_val_cfg = {}; + static Clay_TextElementConfig fdr_val_cfg = {0}; fdr_val_cfg.textColor = g_theme.text; fdr_val_cfg.fontSize = FONT_SIZE_SMALL; fdr_val_cfg.wrapMode = CLAY_TEXT_WRAP_NONE; diff --git a/src/ui/ui_widgets.h b/src/ui/ui_widgets.h index 90dca99..d3e2248 100644 --- a/src/ui/ui_widgets.h +++ b/src/ui/ui_widgets.h @@ -16,7 +16,7 @@ #define UI_WIDGET_MAX_DROPDOWN_ITEMS 32 #define UI_WIDGET_MAX_TEXT_INPUTS 16 -struct UI_KnobDragState { +typedef struct UI_KnobDragState { U32 dragging_id; // Hash of the knob being dragged (0 = none) F32 drag_start_y; // Mouse Y when drag started F32 drag_start_x; // Mouse X when drag started (for h-slider) @@ -24,9 +24,9 @@ struct UI_KnobDragState { B32 was_shift; // Shift state last frame (to re-anchor on change) U32 last_click_id; // Knob hash of last click (for F64-click detection) S32 last_click_frame; // Frame number of last click -}; +} UI_KnobDragState; -struct UI_WidgetState { +typedef struct UI_WidgetState { // Text input focus U32 focused_id; // Clay element ID hash of the focused text input (0 = none) S32 cursor_pos; // Cursor position in focused text input @@ -59,7 +59,7 @@ struct UI_WidgetState { S32 knob_edit_cursor; // Cursor position in edit buffer S32 knob_edit_sel_start; // Selection anchor S32 knob_edit_sel_end; // Selection extent -}; +} UI_WidgetState; extern UI_WidgetState g_wstate; @@ -118,13 +118,13 @@ typedef void (*UI_WindowContentFn)(void *user_data); // editable: if true, clicking the value text opens a text input for direct entry. // Hold Shift while dragging for fine control. // Returns true if value changed this frame. -B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable = 0); +B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable); // Horizontal slider. Drag left/right to change value. -B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable = 0); +B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable); // Vertical slider. Drag up/down to change value. -B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable = 0); +B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable); // DAW-style fader (vertical slider with fader cap icon). -B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable = 0); +B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_signed, F32 default_val, B32 editable); diff --git a/vendor/clay/clay.h b/vendor/clay/clay.h index 40a22a0..c5b6fd3 100644 --- a/vendor/clay/clay.h +++ b/vendor/clay/clay.h @@ -154,14 +154,7 @@ static inline void Clay__SuppressUnusedLatchDefinitionVariableWarning(void) { (v // CLAY(declarationStruct); #define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper #define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type) - -// In C++, bypass the wrapper struct - MSVC doesn't support designated initializers -// in function-style casts through wrapper structs. Direct construction works fine. -#ifdef __cplusplus -#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(type) { __VA_ARGS__ }) -#else #define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped -#endif #define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig) @@ -3152,14 +3145,6 @@ CLAY_DLL_EXPORT Clay_ElementIdArray Clay_GetPointerOverIds(void) { return Clay_GetCurrentContext()->pointerOverIds; } -// MSVC C++ cannot compile the debug view because it uses CLAY() macros with -// designated initializers in function-style casts. Stub it out in C++ mode. -#ifdef __cplusplus -void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) { (void)elementId; (void)type; } -void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) { (void)color; (void)textConfig; } -void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) { (void)cornerRadius; (void)textConfig; } -void Clay__RenderDebugView(void) {} -#else #pragma region DebugTools Clay_Color CLAY__DEBUGVIEW_COLOR_1 = {58, 56, 52, 255}; Clay_Color CLAY__DEBUGVIEW_COLOR_2 = {62, 60, 58, 255}; @@ -3387,6 +3372,12 @@ void Clay__RenderDebugLayoutSizing(Clay_SizingAxis sizing, Clay_TextElementConfi } } +#ifdef CLAY_DISABLE_DEBUG_VIEW +void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) { (void)elementId; (void)type; } +void Clay__RenderDebugViewColor(Clay_Color color, Clay_TextElementConfig *textConfig) { (void)color; (void)textConfig; } +void Clay__RenderDebugViewCornerRadius(Clay_CornerRadius cornerRadius, Clay_TextElementConfig *textConfig) { (void)cornerRadius; (void)textConfig; } +void Clay__RenderDebugView(void) {} +#else void Clay__RenderDebugViewElementConfigHeader(Clay_String elementId, Clay__ElementConfigType type) { Clay__DebugElementConfigTypeLabelConfig config = Clay__DebugGetElementConfigTypeLabel(type); Clay_Color backgroundColor = config.color; @@ -3857,7 +3848,7 @@ void Clay__RenderDebugView(void) { } } #pragma endregion -#endif // __cplusplus (debug view) +#endif // CLAY_DISABLE_DEBUG_VIEW uint32_t Clay__debugViewWidth = 400; Clay_Color Clay__debugViewHighlightColor = { 168, 66, 28, 100 }; @@ -4434,7 +4425,7 @@ void Clay_ResetMeasureTextCache(void) { context->measureTextHashMap.length = 0; context->measuredWords.length = 0; context->measuredWordsFreeList.length = 0; - + for (int32_t i = 0; i < context->measureTextHashMap.capacity; ++i) { context->measureTextHashMap.internalArray[i] = 0; } diff --git a/vendor/lunasvg/LICENSE b/vendor/lunasvg/LICENSE deleted file mode 100644 index f535cea..0000000 --- a/vendor/lunasvg/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020-2026 Samuel Ugochukwu - -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. diff --git a/vendor/lunasvg/include/lunasvg.h b/vendor/lunasvg/include/lunasvg.h deleted file mode 100644 index cf871ad..0000000 --- a/vendor/lunasvg/include/lunasvg.h +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (c) 2020-2026 Samuel Ugochukwu - * - * 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 -#include -#include -#include - -#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; - -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; - -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 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 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 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 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 m_rootElement; - friend class SVGURIReference; - friend class SVGNode; -}; - -} // namespace lunasvg - -#endif // LUNASVG_H diff --git a/vendor/lunasvg/plutovg/include/plutovg.h b/vendor/lunasvg/plutovg/include/plutovg.h deleted file mode 100644 index c34d0c0..0000000 --- a/vendor/lunasvg/plutovg/include/plutovg.h +++ /dev/null @@ -1,2548 +0,0 @@ -/* - * Copyright (c) 2020-2025 Samuel Ugochukwu - * - * 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 PLUTOVG_H -#define PLUTOVG_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(PLUTOVG_BUILD_STATIC) -#define PLUTOVG_EXPORT -#define PLUTOVG_IMPORT -#elif (defined(_WIN32) || defined(__CYGWIN__)) -#define PLUTOVG_EXPORT __declspec(dllexport) -#define PLUTOVG_IMPORT __declspec(dllimport) -#elif defined(__GNUC__) && (__GNUC__ >= 4) -#define PLUTOVG_EXPORT __attribute__((__visibility__("default"))) -#define PLUTOVG_IMPORT -#else -#define PLUTOVG_EXPORT -#define PLUTOVG_IMPORT -#endif - -#ifdef PLUTOVG_BUILD -#define PLUTOVG_API PLUTOVG_EXPORT -#else -#define PLUTOVG_API PLUTOVG_IMPORT -#endif - -#define PLUTOVG_VERSION_MAJOR 1 -#define PLUTOVG_VERSION_MINOR 3 -#define PLUTOVG_VERSION_MICRO 2 - -#define PLUTOVG_VERSION_ENCODE(major, minor, micro) (((major) * 10000) + ((minor) * 100) + ((micro) * 1)) -#define PLUTOVG_VERSION PLUTOVG_VERSION_ENCODE(PLUTOVG_VERSION_MAJOR, PLUTOVG_VERSION_MINOR, PLUTOVG_VERSION_MICRO) - -#define PLUTOVG_VERSION_XSTRINGIZE(major, minor, micro) #major"."#minor"."#micro -#define PLUTOVG_VERSION_STRINGIZE(major, minor, micro) PLUTOVG_VERSION_XSTRINGIZE(major, minor, micro) -#define PLUTOVG_VERSION_STRING PLUTOVG_VERSION_STRINGIZE(PLUTOVG_VERSION_MAJOR, PLUTOVG_VERSION_MINOR, PLUTOVG_VERSION_MICRO) - -/** - * @brief Gets the version of the plutovg library. - * @return An integer representing the version of the plutovg library. - */ -PLUTOVG_API int plutovg_version(void); - -/** - * @brief Gets the version of the plutovg library as a string. - * @return A string representing the version of the plutovg library. - */ -PLUTOVG_API const char* plutovg_version_string(void); - -/** - * @brief A function pointer type for a cleanup callback. - * @param closure A pointer to the resource to be cleaned up. - */ -typedef void (*plutovg_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 (*plutovg_write_func_t)(void* closure, void* data, int size); - -#define PLUTOVG_PI 3.14159265358979323846f -#define PLUTOVG_TWO_PI 6.28318530717958647693f -#define PLUTOVG_HALF_PI 1.57079632679489661923f -#define PLUTOVG_SQRT2 1.41421356237309504880f -#define PLUTOVG_KAPPA 0.55228474983079339840f - -#define PLUTOVG_DEG2RAD(x) ((x) * (PLUTOVG_PI / 180.0f)) -#define PLUTOVG_RAD2DEG(x) ((x) * (180.0f / PLUTOVG_PI)) - -/** - * @brief A structure representing a point in 2D space. - */ -typedef struct plutovg_point { - float x; ///< The x-coordinate of the point. - float y; ///< The y-coordinate of the point. -} plutovg_point_t; - -#define PLUTOVG_MAKE_POINT(x, y) ((plutovg_point_t){x, y}) -#define PLUTOVG_EMPTY_POINT PLUTOVG_MAKE_POINT(0, 0) - -/** - * @brief A structure representing a rectangle in 2D space. - */ -typedef struct plutovg_rect { - float x; ///< The x-coordinate of the top-left corner of the rectangle. - float y; ///< The y-coordinate of the top-left corner of the rectangle. - float w; ///< The width of the rectangle. - float h; ///< The height of the rectangle. -} plutovg_rect_t; - -#define PLUTOVG_MAKE_RECT(x, y, w, h) ((plutovg_rect_t){x, y, w, h}) -#define PLUTOVG_EMPTY_RECT PLUTOVG_MAKE_RECT(0, 0, 0, 0) - -/** - * @brief A structure representing a 2D transformation matrix. - */ -typedef struct plutovg_matrix { - float a; ///< The horizontal scaling factor. - float b; ///< The vertical shearing factor. - float c; ///< The horizontal shearing factor. - float d; ///< The vertical scaling factor. - float e; ///< The horizontal translation offset. - float f; ///< The vertical translation offset. -} plutovg_matrix_t; - -#define PLUTOVG_MAKE_MATRIX(a, b, c, d, e, f) ((plutovg_matrix_t){a, b, c, d, e, f}) -#define PLUTOVG_MAKE_SCALE(x, y) PLUTOVG_MAKE_MATRIX(x, 0, 0, y, 0, 0) -#define PLUTOVG_MAKE_TRANSLATE(x, y) PLUTOVG_MAKE_MATRIX(1, 0, 0, 1, x, y) -#define PLUTOVG_IDENTITY_MATRIX PLUTOVG_MAKE_MATRIX(1, 0, 0, 1, 0, 0) - -/** - * @brief Initializes a 2D transformation matrix. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - * @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. - */ -PLUTOVG_API void plutovg_matrix_init(plutovg_matrix_t* matrix, float a, float b, float c, float d, float e, float f); - -/** - * @brief Initializes a 2D transformation matrix to the identity matrix. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - */ -PLUTOVG_API void plutovg_matrix_init_identity(plutovg_matrix_t* matrix); - -/** - * @brief Initializes a 2D transformation matrix for translation. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - * @param tx The translation offset in the x-direction. - * @param ty The translation offset in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, float tx, float ty); - -/** - * @brief Initializes a 2D transformation matrix for scaling. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - * @param sx The scaling factor in the x-direction. - * @param sy The scaling factor in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_init_scale(plutovg_matrix_t* matrix, float sx, float sy); - -/** - * @brief Initializes a 2D transformation matrix for rotation. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - * @param angle The rotation angle in radians. - */ -PLUTOVG_API void plutovg_matrix_init_rotate(plutovg_matrix_t* matrix, float angle); - -/** - * @brief Initializes a 2D transformation matrix for shearing. - * @param matrix A pointer to the `plutovg_matrix_t` object to be initialized. - * @param shx The shearing factor in the x-direction. - * @param shy The shearing factor in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_init_shear(plutovg_matrix_t* matrix, float shx, float shy); - -/** - * @brief Adds a translation with offsets `tx` and `ty` to the matrix. - * @param matrix A pointer to the `plutovg_matrix_t` object to be modified. - * @param tx The translation offset in the x-direction. - * @param ty The translation offset in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_translate(plutovg_matrix_t* matrix, float tx, float ty); - -/** - * @brief Scales the matrix by factors `sx` and `sy` - * @param matrix A pointer to the `plutovg_matrix_t` object to be modified. - * @param sx The scaling factor in the x-direction. - * @param sy The scaling factor in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_scale(plutovg_matrix_t* matrix, float sx, float sy); - -/** - * @brief Rotates the matrix by the specified angle (in radians). - * @param matrix A pointer to the `plutovg_matrix_t` object to be modified. - * @param angle The rotation angle in radians. - */ -PLUTOVG_API void plutovg_matrix_rotate(plutovg_matrix_t* matrix, float angle); - -/** - * @brief Shears the matrix by factors `shx` and `shy`. - * @param matrix A pointer to the `plutovg_matrix_t` object to be modified. - * @param shx The shearing factor in the x-direction. - * @param shy The shearing factor in the y-direction. - */ -PLUTOVG_API void plutovg_matrix_shear(plutovg_matrix_t* matrix, float shx, float shy); - -/** - * @brief Multiplies `left` and `right` matrices and stores the result in `matrix`. - * @note `matrix` can be identical to either `left` or `right`. - * @param matrix A pointer to the `plutovg_matrix_t` object to store the result. - * @param left A pointer to the first `plutovg_matrix_t` matrix. - * @param right A pointer to the second `plutovg_matrix_t` matrix. - */ -PLUTOVG_API void plutovg_matrix_multiply(plutovg_matrix_t* matrix, const plutovg_matrix_t* left, const plutovg_matrix_t* right); - -/** - * @brief Calculates the inverse of `matrix` and stores it in `inverse`. - * - * If `inverse` is `NULL`, the function only checks if the matrix is invertible. - * - * @note `matrix` and `inverse` can be identical. - * @param matrix A pointer to the `plutovg_matrix_t` object to invert. - * @param inverse A pointer to the `plutovg_matrix_t` object to store the result, or `NULL`. - * @return `true` if the matrix is invertible; `false` otherwise. - */ -PLUTOVG_API bool plutovg_matrix_invert(const plutovg_matrix_t* matrix, plutovg_matrix_t* inverse); - -/** - * @brief Transforms the point `(x, y)` using `matrix` and stores the result in `(xx, yy)`. - * @param matrix A pointer to a `plutovg_matrix_t` object. - * @param x The x-coordinate of the point to transform. - * @param y The y-coordinate of the point to transform. - * @param xx A pointer to store the transformed x-coordinate. - * @param yy A pointer to store the transformed y-coordinate. - */ -PLUTOVG_API void plutovg_matrix_map(const plutovg_matrix_t* matrix, float x, float y, float* xx, float* yy); - -/** - * @brief Transforms the `src` point using `matrix` and stores the result in `dst`. - * @note `src` and `dst` can be identical. - * @param matrix A pointer to a `plutovg_matrix_t` object. - * @param src A pointer to the `plutovg_point_t` object to transform. - * @param dst A pointer to the `plutovg_point_t` to store the transformed point. - */ -PLUTOVG_API void plutovg_matrix_map_point(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst); - -/** - * @brief Transforms an array of `src` points using `matrix` and stores the results in `dst`. - * @note `src` and `dst` can be identical. - * @param matrix A pointer to a `plutovg_matrix_t` object. - * @param src A pointer to the array of `plutovg_point_t` objects to transform. - * @param dst A pointer to the array of `plutovg_point_t` to store the transformed points. - * @param count The number of points to transform. - */ -PLUTOVG_API void plutovg_matrix_map_points(const plutovg_matrix_t* matrix, const plutovg_point_t* src, plutovg_point_t* dst, int count); - -/** - * @brief Transforms the `src` rectangle using `matrix` and stores the result in `dst`. - * @note `src` and `dst` can be identical. - * @param matrix A pointer to a `plutovg_matrix_t` object. - * @param src A pointer to the `plutovg_rect_t` object to transform. - * @param dst A pointer to the `plutovg_rect_t` to store the transformed rectangle. - */ -PLUTOVG_API void plutovg_matrix_map_rect(const plutovg_matrix_t* matrix, const plutovg_rect_t* src, plutovg_rect_t* dst); - -/** - * @brief Parses an SVG transform string into a matrix. - * - * @param matrix A pointer to a `plutovg_matrix_t` object to store the result. - * @param data Input SVG transform string. - * @param length Length of the string, or `-1` if null-terminated. - * - * @return `true` on success, `false` on failure. - */ -PLUTOVG_API bool plutovg_matrix_parse(plutovg_matrix_t* matrix, const char* data, int length); - -/** - * @brief Represents a 2D path for drawing operations. - */ -typedef struct plutovg_path plutovg_path_t; - -/** - * @brief Enumeration defining path commands. - */ -typedef enum plutovg_path_command { - PLUTOVG_PATH_COMMAND_MOVE_TO, ///< Moves the current point to a new position. - PLUTOVG_PATH_COMMAND_LINE_TO, ///< Draws a straight line to a new point. - PLUTOVG_PATH_COMMAND_CUBIC_TO, ///< Draws a cubic Bézier curve to a new point. - PLUTOVG_PATH_COMMAND_CLOSE ///< Closes the current path by drawing a line to the starting point. -} plutovg_path_command_t; - -/** - * @brief Union representing a path element. - * - * A path element can be a command with a length or a coordinate point. - * Each command type in the path element array is followed by a specific number of points: - * - `PLUTOVG_PATH_COMMAND_MOVE_TO`: 1 point - * - `PLUTOVG_PATH_COMMAND_LINE_TO`: 1 point - * - `PLUTOVG_PATH_COMMAND_CUBIC_TO`: 3 points - * - `PLUTOVG_PATH_COMMAND_CLOSE`: 1 point - * - * @example - * const plutovg_path_element_t* elements; - * int count = plutovg_path_get_elements(path, &elements); - * for(int i = 0; i < count; i += elements[i].header.length) { - * plutovg_path_command_t command = elements[i].header.command; - * switch(command) { - * case PLUTOVG_PATH_COMMAND_MOVE_TO: - * printf("MoveTo: %g %g\n", elements[i + 1].point.x, elements[i + 1].point.y); - * break; - * case PLUTOVG_PATH_COMMAND_LINE_TO: - * printf("LineTo: %g %g\n", elements[i + 1].point.x, elements[i + 1].point.y); - * break; - * case PLUTOVG_PATH_COMMAND_CUBIC_TO: - * printf("CubicTo: %g %g %g %g %g %g\n", - * elements[i + 1].point.x, elements[i + 1].point.y, - * elements[i + 2].point.x, elements[i + 2].point.y, - * elements[i + 3].point.x, elements[i + 3].point.y); - * break; - * case PLUTOVG_PATH_COMMAND_CLOSE: - * printf("Close: %g %g\n", elements[i + 1].point.x, elements[i + 1].point.y); - * break; - * } - * } - */ -typedef union plutovg_path_element { - struct { - plutovg_path_command_t command; ///< The path command. - int length; ///< Number of elements including the header. - } header; ///< Header for path commands. - plutovg_point_t point; ///< A coordinate point in the path. -} plutovg_path_element_t; - -/** - * @brief Iterator for traversing path elements in a path. - */ -typedef struct plutovg_path_iterator { - const plutovg_path_element_t* elements; ///< Pointer to the array of path elements. - int size; ///< Total number of elements in the array. - int index; ///< Current position in the array. -} plutovg_path_iterator_t; - -/** - * @brief Initializes a path iterator for a given path. - * - * @param it The path iterator to initialize. - * @param path The path to iterate over. - */ -PLUTOVG_API void plutovg_path_iterator_init(plutovg_path_iterator_t* it, const plutovg_path_t* path); - -/** - * @brief Checks if there are more elements to iterate over. - * - * @param it The path iterator. - * @return `true` if there are more elements; otherwise, `false`. - */ -PLUTOVG_API bool plutovg_path_iterator_has_next(const plutovg_path_iterator_t* it); - -/** - * @brief Retrieves the current command and its associated points, then advances the iterator. - * - * @param it The path iterator. - * @param points An array to store the points for the current command. - * @return The path command for the current element. - */ -PLUTOVG_API plutovg_path_command_t plutovg_path_iterator_next(plutovg_path_iterator_t* it, plutovg_point_t points[3]); - -/** - * @brief Creates a new path object. - * - * @return A pointer to the newly created path object. - */ -PLUTOVG_API plutovg_path_t* plutovg_path_create(void); - -/** - * @brief Increases the reference count of a path object. - * - * @param path A pointer to a `plutovg_path_t` object. - * @return A pointer to the same `plutovg_path_t` object. - */ -PLUTOVG_API plutovg_path_t* plutovg_path_reference(plutovg_path_t* path); - -/** - * @brief Decreases the reference count of a path object. - * - * This function decrements the reference count of the given path object. If - * the reference count reaches zero, the path object is destroyed and its - * resources are freed. - * - * @param path A pointer to the `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_path_destroy(plutovg_path_t* path); - -/** - * @brief Retrieves the reference count of a path object. - * - * @param path A pointer to a `plutovg_path_t` object. - * @return The current reference count of the path object. - */ -PLUTOVG_API int plutovg_path_get_reference_count(const plutovg_path_t* path); - -/** - * @brief Retrieves the elements of a path. - * - * Provides access to the array of path elements. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param elements A pointer to a pointer that will be set to the array of path elements. - * @return The number of elements in the path. - */ -PLUTOVG_API int plutovg_path_get_elements(const plutovg_path_t* path, const plutovg_path_element_t** elements); - -/** - * @brief Moves the current point to a new position. - * - * This function moves the current point to the specified coordinates without - * drawing a line. This is equivalent to the `M` command in SVG path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x The x-coordinate of the new position. - * @param y The y-coordinate of the new position. - */ -PLUTOVG_API void plutovg_path_move_to(plutovg_path_t* path, float x, float y); - -/** - * @brief Adds a straight line segment to the path. - * - * This function adds a straight line segment from the current point to the - * specified coordinates. This is equivalent to the `L` command in SVG path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x The x-coordinate of the end point of the line segment. - * @param y The y-coordinate of the end point of the line segment. - */ -PLUTOVG_API void plutovg_path_line_to(plutovg_path_t* path, float x, float y); - -/** - * @brief Adds a quadratic Bézier curve to the path. - * - * This function adds a quadratic Bézier curve segment from the current point - * to the specified end point, using the given control point. This is equivalent - * to the `Q` command in SVG path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x1 The x-coordinate of the control point. - * @param y1 The y-coordinate of the control point. - * @param x2 The x-coordinate of the end point of the curve. - * @param y2 The y-coordinate of the end point of the curve. - */ -PLUTOVG_API void plutovg_path_quad_to(plutovg_path_t* path, float x1, float y1, float x2, float y2); - -/** - * @brief Adds a cubic Bézier curve to the path. - * - * This function adds a cubic Bézier curve segment from the current point - * to the specified end point, using the given two control points. This is - * equivalent to the `C` command in SVG path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x1 The x-coordinate of the first control point. - * @param y1 The y-coordinate of the first control point. - * @param x2 The x-coordinate of the second control point. - * @param y2 The y-coordinate of the second control point. - * @param x3 The x-coordinate of the end point of the curve. - * @param y3 The y-coordinate of the end point of the curve. - */ -PLUTOVG_API void plutovg_path_cubic_to(plutovg_path_t* path, float x1, float y1, float x2, float y2, float x3, float y3); - -/** - * @brief Adds an elliptical arc to the path. - * - * This function adds an elliptical arc segment from the current point to the - * specified end point. The arc is defined by the radii, rotation angle, and - * flags for large arc and sweep. This is equivalent to the `A` command in SVG - * path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param rx The x-radius of the ellipse. - * @param ry The y-radius of the ellipse. - * @param angle The rotation angle of the ellipse in radians. - * @param large_arc_flag If true, draw the large arc; otherwise, draw the small arc. - * @param sweep_flag If true, draw the arc in the positive-angle direction; otherwise, in the negative-angle direction. - * @param x The x-coordinate of the end point of the arc. - * @param y The y-coordinate of the end point of the arc. - */ -PLUTOVG_API 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); - -/** - * @brief Closes the current sub-path. - * - * This function closes the current sub-path by drawing a straight line back to - * the start point of the sub-path. This is equivalent to the `Z` command in SVG - * path syntax. - * - * @param path A pointer to a `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_path_close(plutovg_path_t* path); - -/** - * @brief Retrieves the current point of the path. - * - * Gets the current point's coordinates in the path. This point is the last - * position used or the point where the path was last moved to. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x The x-coordinate of the current point. - * @param y The y-coordinate of the current point. - */ -PLUTOVG_API void plutovg_path_get_current_point(const plutovg_path_t* path, float* x, float* y); - -/** - * @brief Reserves space for path elements. - * - * Reserves space for a specified number of elements in the path. This helps optimize - * memory allocation for future path operations. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param count The number of path elements to reserve space for. - */ -PLUTOVG_API void plutovg_path_reserve(plutovg_path_t* path, int count); - -/** - * @brief Resets the path. - * - * Clears all path data, effectively resetting the `plutovg_path_t` object to its initial state. - * - * @param path A pointer to a `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_path_reset(plutovg_path_t* path); - -/** - * @brief Adds a rectangle to the path. - * - * Adds a rectangle defined by the top-left corner (x, y) and dimensions (w, h) to the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x The x-coordinate of the rectangle's top-left corner. - * @param y The y-coordinate of the rectangle's top-left corner. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - */ -PLUTOVG_API void plutovg_path_add_rect(plutovg_path_t* path, float x, float y, float w, float h); - -/** - * @brief Adds a rounded rectangle to the path. - * - * Adds a rounded rectangle defined by the top-left corner (x, y), dimensions (w, h), - * and corner radii (rx, ry) to the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param x The x-coordinate of the rectangle's top-left corner. - * @param y The y-coordinate of the rectangle's top-left corner. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - * @param rx The x-radius of the rectangle's corners. - * @param ry The y-radius of the rectangle's corners. - */ -PLUTOVG_API void plutovg_path_add_round_rect(plutovg_path_t* path, float x, float y, float w, float h, float rx, float ry); - -/** - * @brief Adds an ellipse to the path. - * - * Adds an ellipse defined by the center (cx, cy) and radii (rx, ry) to the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param cx The x-coordinate of the ellipse's center. - * @param cy The y-coordinate of the ellipse's center. - * @param rx The x-radius of the ellipse. - * @param ry The y-radius of the ellipse. - */ -PLUTOVG_API void plutovg_path_add_ellipse(plutovg_path_t* path, float cx, float cy, float rx, float ry); - -/** - * @brief Adds a circle to the path. - * - * Adds a circle defined by its center (cx, cy) and radius (r) to the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param cx The x-coordinate of the circle's center. - * @param cy The y-coordinate of the circle's center. - * @param r The radius of the circle. - */ -PLUTOVG_API void plutovg_path_add_circle(plutovg_path_t* path, float cx, float cy, float r); - -/** - * @brief Adds an arc to the path. - * - * Adds an arc defined by the center (cx, cy), radius (r), start angle (a0), end angle (a1), - * and direction (ccw) to the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param cx The x-coordinate of the arc's center. - * @param cy The y-coordinate of the arc's center. - * @param r The radius of the arc. - * @param a0 The start angle of the arc in radians. - * @param a1 The end angle of the arc in radians. - * @param ccw If true, the arc is drawn counter-clockwise; if false, clockwise. - */ -PLUTOVG_API void plutovg_path_add_arc(plutovg_path_t* path, float cx, float cy, float r, float a0, float a1, bool ccw); - -/** - * @brief Adds a sub-path to the path. - * - * Adds all elements from another path (`source`) to the current path, optionally - * applying a transformation matrix. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param source A pointer to the `plutovg_path_t` object to copy elements from. - * @param matrix A pointer to a `plutovg_matrix_t` object, or `NULL` to apply no transformation. - */ -PLUTOVG_API void plutovg_path_add_path(plutovg_path_t* path, const plutovg_path_t* source, const plutovg_matrix_t* matrix); - -/** - * @brief Applies a transformation matrix to the path. - * - * Transforms the entire path using the provided transformation matrix. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param matrix A pointer to a `plutovg_matrix_t` object. - */ -PLUTOVG_API void plutovg_path_transform(plutovg_path_t* path, const plutovg_matrix_t* matrix); - -/** - * @brief Callback function type for traversing a path. - * - * This function type defines a callback used to traverse path elements. - * - * @param closure A pointer to user-defined data passed to the callback. - * @param command The current path command. - * @param points An array of points associated with the command. - * @param npoints The number of points in the array. - */ -typedef void (*plutovg_path_traverse_func_t)(void* closure, plutovg_path_command_t command, const plutovg_point_t* points, int npoints); - -/** - * @brief Traverses the path and calls the callback for each element. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param traverse_func The callback function to be called for each element of the path. - * @param closure User-defined data passed to the callback. - */ -PLUTOVG_API void plutovg_path_traverse(const plutovg_path_t* path, plutovg_path_traverse_func_t traverse_func, void* closure); - -/** - * @brief Traverses the path with Bézier curves flattened to line segments. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param traverse_func The callback function to be called for each element of the path. - * @param closure User-defined data passed to the callback. - */ -PLUTOVG_API void plutovg_path_traverse_flatten(const plutovg_path_t* path, plutovg_path_traverse_func_t traverse_func, void* closure); - -/** - * @brief Traverses the path with a dashed pattern and calls the callback for each segment. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param offset The starting offset into the dash pattern. - * @param dashes An array of dash lengths. - * @param ndashes The number of elements in the `dashes` array. - * @param traverse_func The callback function to be called for each element of the path. - * @param closure User-defined data passed to the callback. - */ -PLUTOVG_API 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); - -/** - * @brief Creates a copy of the path. - * - * @param path A pointer to the `plutovg_path_t` object to clone. - * @return A pointer to the newly created path clone. - */ -PLUTOVG_API plutovg_path_t* plutovg_path_clone(const plutovg_path_t* path); - -/** - * @brief Creates a copy of the path with Bézier curves flattened to line segments. - * - * @param path A pointer to the `plutovg_path_t` object to clone. - * @return A pointer to the newly created path clone with flattened curves. - */ -PLUTOVG_API plutovg_path_t* plutovg_path_clone_flatten(const plutovg_path_t* path); - -/** - * @brief Creates a copy of the path with a dashed pattern applied. - * - * @param path A pointer to the `plutovg_path_t` object to clone. - * @param offset The starting offset into the dash pattern. - * @param dashes An array of dash lengths. - * @param ndashes The number of elements in the `dashes` array. - * @return A pointer to the newly created path clone with dashed pattern. - */ -PLUTOVG_API plutovg_path_t* plutovg_path_clone_dashed(const plutovg_path_t* path, float offset, const float* dashes, int ndashes); - -/** - * @brief Computes the bounding box and total length of the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @param extents A pointer to a `plutovg_rect_t` object to store the bounding box. - * @param tight If `true`, computes a precise bounding box; otherwise, aligns to control points. - * @return The total length of the path. - */ -PLUTOVG_API float plutovg_path_extents(const plutovg_path_t* path, plutovg_rect_t* extents, bool tight); - -/** - * @brief Calculates the total length of the path. - * - * @param path A pointer to a `plutovg_path_t` object. - * @return The total length of the path. - */ -PLUTOVG_API float plutovg_path_length(const plutovg_path_t* path); - -/** - * @brief Parses SVG path data into a `plutovg_path_t` object. - * - * @param path A pointer to the `plutovg_path_t` object to populate. - * @param data The SVG path data string. - * @param length The length of `data`, or `-1` for null-terminated data. - * @return `true` if successful; `false` otherwise. - */ -PLUTOVG_API bool plutovg_path_parse(plutovg_path_t* path, const char* data, int length); - -/** - * @brief Text encodings used for converting text data to code points. - */ -typedef enum plutovg_text_encoding { - PLUTOVG_TEXT_ENCODING_LATIN1, ///< Latin-1 encoding - PLUTOVG_TEXT_ENCODING_UTF8, ///< UTF-8 encoding - PLUTOVG_TEXT_ENCODING_UTF16, ///< UTF-16 encoding - PLUTOVG_TEXT_ENCODING_UTF32 ///< UTF-32 encoding -} plutovg_text_encoding_t; - -/** - * @brief Iterator for traversing code points in text data. - */ -typedef struct plutovg_text_iterator { - const void* text; ///< Pointer to the text data. - int length; ///< Length of the text data. - plutovg_text_encoding_t encoding; ///< Encoding format of the text data. - int index; ///< Current position in the text data. -} plutovg_text_iterator_t; - -/** - * @brief Represents a Unicode code point. - */ -typedef unsigned int plutovg_codepoint_t; - -/** - * @brief Initializes a text iterator. - * - * @param it Pointer to the text iterator. - * @param text Pointer to the text data. - * @param length Length of the text data, or -1 if the data is null-terminated. - * @param encoding Encoding of the text data. - */ -PLUTOVG_API void plutovg_text_iterator_init(plutovg_text_iterator_t* it, const void* text, int length, plutovg_text_encoding_t encoding); - -/** - * @brief Checks if there are more code points to iterate. - * - * @param it Pointer to the text iterator. - * @return `true` if more code points are available; otherwise, `false`. - */ -PLUTOVG_API bool plutovg_text_iterator_has_next(const plutovg_text_iterator_t* it); - -/** - * @brief Retrieves the next code point and advances the iterator. - * - * @param it Pointer to the text iterator. - * @return The next code point. - */ -PLUTOVG_API plutovg_codepoint_t plutovg_text_iterator_next(plutovg_text_iterator_t* it); - -/** - * @brief Represents a font face. - */ -typedef struct plutovg_font_face plutovg_font_face_t; - -/** - * @brief Loads a font face from a file. - * - * @param filename Path to the font file. - * @param ttcindex Index of the font face within a TrueType Collection (TTC). - * @return A pointer to the loaded `plutovg_font_face_t` object, or `NULL` on failure. - */ -PLUTOVG_API plutovg_font_face_t* plutovg_font_face_load_from_file(const char* filename, int ttcindex); - -/** - * @brief Loads a font face from memory. - * - * @param data Pointer to the font data. - * @param length Length of the font data. - * @param ttcindex Index of the font face within a TrueType Collection (TTC). - * @param destroy_func Function to free the font data when no longer needed. - * @param closure User-defined data passed to `destroy_func`. - * @return A pointer to the loaded `plutovg_font_face_t` object, or `NULL` on failure. - */ -PLUTOVG_API plutovg_font_face_t* plutovg_font_face_load_from_data(const void* data, unsigned int length, int ttcindex, plutovg_destroy_func_t destroy_func, void* closure); - -/** - * @brief Increments the reference count of a font face. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @return A pointer to the same `plutovg_font_face_t` object with an incremented reference count. - */ -PLUTOVG_API plutovg_font_face_t* plutovg_font_face_reference(plutovg_font_face_t* face); - -/** - * @brief Decrements the reference count and potentially destroys the font face. - * - * @param face A pointer to a `plutovg_font_face_t` object. - */ -PLUTOVG_API void plutovg_font_face_destroy(plutovg_font_face_t* face); - -/** - * @brief Retrieves the current reference count of a font face. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @return The reference count of the font face. - */ -PLUTOVG_API int plutovg_font_face_get_reference_count(const plutovg_font_face_t* face); - -/** - * @brief Retrieves metrics for a font face at a specified size. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @param size The font size in pixels. - * @param ascent Pointer to store the ascent metric. - * @param descent Pointer to store the descent metric. - * @param line_gap Pointer to store the line gap metric. - * @param extents Pointer to a `plutovg_rect_t` object to store the font bounding box. - */ -PLUTOVG_API void plutovg_font_face_get_metrics(const plutovg_font_face_t* face, float size, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents); - -/** - * @brief Retrieves metrics for a specified glyph at a given size. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @param size The font size in pixels. - * @param codepoint The Unicode code point of the glyph. - * @param advance_width Pointer to store the advance width of the glyph. - * @param left_side_bearing Pointer to store the left side bearing of the glyph. - * @param extents Pointer to a `plutovg_rect_t` object to store the glyph bounding box. - */ -PLUTOVG_API void plutovg_font_face_get_glyph_metrics(plutovg_font_face_t* face, float size, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents); - -/** - * @brief Retrieves the path of a glyph and its advance width. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @param size The font size in pixels. - * @param x The x-coordinate for positioning the glyph. - * @param y The y-coordinate for positioning the glyph. - * @param codepoint The Unicode code point of the glyph. - * @param path Pointer to a `plutovg_path_t` object to store the glyph path. - * @return The advance width of the glyph. - */ -PLUTOVG_API float plutovg_font_face_get_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_t* path); - -/** - * @brief Traverses the path of a glyph and calls a callback for each path element. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @param size The font size in pixels. - * @param x The x-coordinate for positioning the glyph. - * @param y The y-coordinate for positioning the glyph. - * @param codepoint The Unicode code point of the glyph. - * @param traverse_func The callback function to be called for each path element. - * @param closure User-defined data passed to the callback function. - * @return The advance width of the glyph. - */ -PLUTOVG_API float plutovg_font_face_traverse_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_traverse_func_t traverse_func, void* closure); - -/** - * @brief Computes the bounding box of a text string and its advance width. - * - * @param face A pointer to a `plutovg_font_face_t` object. - * @param size The font size in pixels. - * @param text Pointer to the text data. - * @param length Length of the text data, or -1 if null-terminated. - * @param encoding Encoding of the text data. - * @param extents Pointer to a `plutovg_rect_t` object to store the bounding box of the text. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_font_face_text_extents(plutovg_font_face_t* face, float size, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents); - -/** - * @brief Represents a cache of loaded font faces. - */ -typedef struct plutovg_font_face_cache plutovg_font_face_cache_t; - -/** - * @brief Create a new, empty font‐face cache. - * - * @return Pointer to a newly allocated `plutovg_font_face_cache_t` object. - */ -PLUTOVG_API plutovg_font_face_cache_t* plutovg_font_face_cache_create(void); - -/** - * @brief Increments the reference count of a font‐face cache. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @return A pointer to the same `plutovg_font_face_cache_t` object with an incremented reference count. - */ -PLUTOVG_API plutovg_font_face_cache_t* plutovg_font_face_cache_reference(plutovg_font_face_cache_t* cache); - -/** - * @brief Decrement the reference count of a font‐face cache and destroy it when it reaches zero. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object to release. - */ -PLUTOVG_API void plutovg_font_face_cache_destroy(plutovg_font_face_cache_t* cache); - -/** - * @brief Retrieve the current reference count of a font‐face cache. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @return The current reference count, or 0 if cache is NULL. - */ -PLUTOVG_API int plutovg_font_face_cache_reference_count(const plutovg_font_face_cache_t* cache); - -/** - * @brief Remove all entries from a font‐face cache. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object to reset. - */ -PLUTOVG_API void plutovg_font_face_cache_reset(plutovg_font_face_cache_t* cache); - -/** - * @brief Add a font face to the cache with the specified family and style. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @param family The font family name. - * @param bold Whether the font is bold. - * @param italic Whether the font is italic. - * @param face A pointer to the `plutovg_font_face_t` to add. The cache increments its reference count. - */ -PLUTOVG_API void plutovg_font_face_cache_add(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic, plutovg_font_face_t* face); - -/** - * @brief Load a font face from a file and add it to the cache with the specified family and style. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @param family The font family name to associate with the face. - * @param bold Whether the font is bold. - * @param italic Whether the font is italic. - * @param filename Path to the font file. - * @param ttcindex Index of the face in a TrueType collection (use 0 for non-TTC fonts). - * @return `true` on success, `false` if the file could not be loaded. - */ -PLUTOVG_API bool plutovg_font_face_cache_add_file(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic, const char* filename, int ttcindex); - -/** - * @brief Retrieve a font face from the cache by family and style. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @param family The font family name. - * @param bold Whether the font is bold. - * @param italic Whether the font is italic. - * @return A pointer to the matching `plutovg_font_face_t` object, or NULL if not found. The returned face is owned by the cache and must not be destroyed by the caller. - */ -PLUTOVG_API plutovg_font_face_t* plutovg_font_face_cache_get(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic); - -/** - * @brief Load all font faces from a file and add them to the cache. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @param filename Path to the font file (TrueType, OpenType, or font collection). - * @return The number of faces successfully loaded, or `-1` if font face cache loading is disabled. - */ -PLUTOVG_API int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const char* filename); - -/** - * @brief Load all font faces from files in a directory recursively and add them to the cache. - * - * This scans the specified directory recursively and loads all supported font files. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @param dirname Path to the directory containing font files. - * @return The number of faces successfully loaded, or `-1` if font face cache loading is disabled. - */ -PLUTOVG_API int plutovg_font_face_cache_load_dir(plutovg_font_face_cache_t* cache, const char* dirname); - -/** - * @brief Load all available system font faces and add them to the cache. - * - * This scans standard system font directories recursively and loads all supported font files. - * - * @param cache A pointer to a `plutovg_font_face_cache_t` object. - * @return The number of faces successfully loaded, or `-1` if font face cache loading is disabled. - */ -PLUTOVG_API int plutovg_font_face_cache_load_sys(plutovg_font_face_cache_t* cache); - -/** - * @brief Represents a color with red, green, blue, and alpha components. - */ -typedef struct plutovg_color { - float r; ///< Red component (0 to 1). - float g; ///< Green component (0 to 1). - float b; ///< Blue component (0 to 1). - float a; ///< Alpha (opacity) component (0 to 1). -} plutovg_color_t; - -#define PLUTOVG_MAKE_COLOR(r, g, b, a) ((plutovg_color_t){r, g, b, a}) - -#define PLUTOVG_BLACK_COLOR PLUTOVG_MAKE_COLOR(0, 0, 0, 1) -#define PLUTOVG_WHITE_COLOR PLUTOVG_MAKE_COLOR(1, 1, 1, 1) -#define PLUTOVG_RED_COLOR PLUTOVG_MAKE_COLOR(1, 0, 0, 1) -#define PLUTOVG_GREEN_COLOR PLUTOVG_MAKE_COLOR(0, 1, 0, 1) -#define PLUTOVG_BLUE_COLOR PLUTOVG_MAKE_COLOR(0, 0, 1, 1) -#define PLUTOVG_YELLOW_COLOR PLUTOVG_MAKE_COLOR(1, 1, 0, 1) -#define PLUTOVG_CYAN_COLOR PLUTOVG_MAKE_COLOR(0, 1, 1, 1) -#define PLUTOVG_MAGENTA_COLOR PLUTOVG_MAKE_COLOR(1, 0, 1, 1) - -/** - * @brief Initializes a color using RGB components in the 0-1 range. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param r Red component (0 to 1). - * @param g Green component (0 to 1). - * @param b Blue component (0 to 1). - */ -PLUTOVG_API void plutovg_color_init_rgb(plutovg_color_t* color, float r, float g, float b); - -/** - * @brief Initializes a color using RGBA components in the 0-1 range. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param r Red component (0 to 1). - * @param g Green component (0 to 1). - * @param b Blue component (0 to 1). - * @param a Alpha component (0 to 1). - */ -PLUTOVG_API void plutovg_color_init_rgba(plutovg_color_t* color, float r, float g, float b, float a); - -/** - * @brief Initializes a color using RGB components in the 0-255 range. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param r Red component (0 to 255). - * @param g Green component (0 to 255). - * @param b Blue component (0 to 255). - */ -PLUTOVG_API void plutovg_color_init_rgb8(plutovg_color_t* color, int r, int g, int b); - -/** - * @brief Initializes a color using RGBA components in the 0-255 range. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param r Red component (0 to 255). - * @param g Green component (0 to 255). - * @param b Blue component (0 to 255). - * @param a Alpha component (0 to 255). - */ -PLUTOVG_API void plutovg_color_init_rgba8(plutovg_color_t* color, int r, int g, int b, int a); - -/** - * @brief Initializes a color from a 32-bit unsigned RGBA value. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param value 32-bit unsigned RGBA value. - */ -PLUTOVG_API void plutovg_color_init_rgba32(plutovg_color_t* color, unsigned int value); - -/** - * @brief Initializes a color from a 32-bit unsigned ARGB value. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param value 32-bit unsigned ARGB value. - */ -PLUTOVG_API void plutovg_color_init_argb32(plutovg_color_t* color, unsigned int value); - -/** - * @brief Initializes a color with the specified HSL color values. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param h Hue component in degrees (0 to 360). - * @param s Saturation component (0 to 1). - * @param l Lightness component (0 to 1). - */ -PLUTOVG_API void plutovg_color_init_hsl(plutovg_color_t* color, float h, float s, float l); - -/** - * @brief Initializes a color with the specified HSLA color values. - * - * @param color A pointer to a `plutovg_color_t` object. - * @param h Hue component in degrees (0 to 360). - * @param s Saturation component (0 to 1). - * @param l Lightness component (0 to 1). - * @param a Alpha component (0 to 1). - */ -PLUTOVG_API void plutovg_color_init_hsla(plutovg_color_t* color, float h, float s, float l, float a); - -/** - * @brief Converts a color to a 32-bit unsigned RGBA value. - * - * @param color A pointer to a `plutovg_color_t` object. - * - * @return 32-bit unsigned RGBA value. - */ -PLUTOVG_API unsigned int plutovg_color_to_rgba32(const plutovg_color_t* color); - -/** - * @brief Converts a color to a 32-bit unsigned ARGB value. - * - * @param color A pointer to a `plutovg_color_t` object. - * - * @return 32-bit unsigned ARGB value. - */ -PLUTOVG_API unsigned int plutovg_color_to_argb32(const plutovg_color_t* color); - -/** - * @brief Parses a color from a string using CSS color syntax. - * - * @param color A pointer to a `plutovg_color_t` object to store the parsed color. - * @param data A pointer to the input string containing the color data. - * @param length The length of the input string in bytes, or `-1` if the string is null-terminated. - * - * @return The number of characters consumed on success (including leading/trailing spaces), or 0 on failure. - */ -PLUTOVG_API int plutovg_color_parse(plutovg_color_t* color, const char* data, int length); - -/** - * @brief Represents an image surface for drawing operations. - * - * Stores pixel data in a 32-bit premultiplied ARGB format (0xAARRGGBB), - * where red, green, and blue channels are multiplied by the alpha channel - * and divided by 255. - */ -typedef struct plutovg_surface plutovg_surface_t; - -/** - * @brief Creates a new image surface with the specified dimensions. - * - * @param width The width of the surface in pixels. - * @param height The height of the surface in pixels. - * @return A pointer to the newly created `plutovg_surface_t` object. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_create(int width, int height); - -/** - * @brief Creates an image surface using existing pixel data. - * - * @param data Pointer to the pixel data. - * @param width The width of the surface in pixels. - * @param height The height of the surface in pixels. - * @param stride The number of bytes per row in the pixel data. - * @return A pointer to the newly created `plutovg_surface_t` object. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_create_for_data(unsigned char* data, int width, int height, int stride); - -/** - * @brief Loads an image surface from a file. - * - * @param filename Path to the image file. - * @return Pointer to the surface, or `NULL` on failure. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_load_from_image_file(const char* filename); - -/** - * @brief Loads an image surface from raw image data. - * - * @param data Pointer to the image data. - * @param length Length of the data in bytes. - * @return Pointer to the surface, or `NULL` on failure. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_load_from_image_data(const void* data, int length); - -/** - * @brief Loads an image surface from base64-encoded data. - * - * @param data Pointer to the base64-encoded image data. - * @param length Length of the data in bytes, or `-1` if null-terminated. - * @return Pointer to the surface, or `NULL` on failure. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_load_from_image_base64(const char* data, int length); - -/** - * @brief Increments the reference count for a surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return Pointer to the `plutovg_surface_t` object. - */ -PLUTOVG_API plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface); - -/** - * @brief Decrements the reference count and destroys the surface if the count reaches zero. - * - * @param surface Pointer to the `plutovg_surface_t` object . - */ -PLUTOVG_API void plutovg_surface_destroy(plutovg_surface_t* surface); - -/** - * @brief Gets the current reference count of a surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return The reference count of the surface. - */ -PLUTOVG_API int plutovg_surface_get_reference_count(const plutovg_surface_t* surface); - -/** - * @brief Gets the pixel data of the surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return Pointer to the pixel data. - */ -PLUTOVG_API unsigned char* plutovg_surface_get_data(const plutovg_surface_t* surface); - -/** - * @brief Gets the width of the surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return Width of the surface in pixels. - */ -PLUTOVG_API int plutovg_surface_get_width(const plutovg_surface_t* surface); - -/** - * @brief Gets the height of the surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return Height of the surface in pixels. - */ -PLUTOVG_API int plutovg_surface_get_height(const plutovg_surface_t* surface); - -/** - * @brief Gets the stride of the surface. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @return Number of bytes per row. - */ -PLUTOVG_API int plutovg_surface_get_stride(const plutovg_surface_t* surface); - -/** - * @brief Clears the entire surface with the specified color. - * - * @param surface Pointer to the target surface. - * @param color Pointer to the color used for clearing. - */ -PLUTOVG_API void plutovg_surface_clear(plutovg_surface_t* surface, const plutovg_color_t* color); - -/** - * @brief Writes the surface to a PNG file. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @param filename Path to the output PNG file. - * @return `true` if successful, `false` otherwise. - */ -PLUTOVG_API bool plutovg_surface_write_to_png(const plutovg_surface_t* surface, const char* filename); - -/** - * @brief Writes the surface to a JPEG file. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @param filename Path to the output JPEG file. - * @param quality JPEG quality (0 to 100). - * @return `true` if successful, `false` otherwise. - */ -PLUTOVG_API bool plutovg_surface_write_to_jpg(const plutovg_surface_t* surface, const char* filename, int quality); - -/** - * @brief Writes the surface to a PNG stream. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @param write_func Callback function for writing data. - * @param closure User-defined data passed to the callback. - * @return `true` if successful, `false` otherwise. - */ -PLUTOVG_API bool plutovg_surface_write_to_png_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure); - -/** - * @brief Writes the surface to a JPEG stream. - * - * @param surface Pointer to the `plutovg_surface_t` object. - * @param write_func Callback function for writing data. - * @param closure User-defined data passed to the callback. - * @param quality JPEG quality (0 to 100). - * @return `true` if successful, `false` otherwise. - */ -PLUTOVG_API bool plutovg_surface_write_to_jpg_stream(const plutovg_surface_t* surface, plutovg_write_func_t write_func, void* closure, int quality); - -/** - * @brief Converts pixel data from premultiplied ARGB to RGBA format. - * - * Transforms pixel data from native-endian 32-bit ARGB premultiplied format - * to a non-premultiplied RGBA byte sequence. - * - * @param dst Pointer to the destination buffer (can overlap with `src`). - * @param src Pointer to the source buffer in ARGB premultiplied format. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param stride Number of bytes per image row in the buffers. - */ -PLUTOVG_API void plutovg_convert_argb_to_rgba(unsigned char* dst, const unsigned char* src, int width, int height, int stride); - -/** - * @brief Converts pixel data from RGBA to premultiplied ARGB format. - * - * Transforms pixel data from a non-premultiplied RGBA byte sequence - * to a native-endian 32-bit ARGB premultiplied format. - * - * @param dst Pointer to the destination buffer (can overlap with `src`). - * @param src Pointer to the source buffer in RGBA format. - * @param width Image width in pixels. - * @param height Image height in pixels. - * @param stride Number of bytes per image row in the buffers. - */ -PLUTOVG_API void plutovg_convert_rgba_to_argb(unsigned char* dst, const unsigned char* src, int width, int height, int stride); - -/** - * @brief Defines the type of texture, either plain or tiled. - */ -typedef enum { - PLUTOVG_TEXTURE_TYPE_PLAIN, ///< Plain texture. - PLUTOVG_TEXTURE_TYPE_TILED ///< Tiled texture. -} plutovg_texture_type_t; - -/** - * @brief Defines the spread method for gradients. - */ -typedef enum { - PLUTOVG_SPREAD_METHOD_PAD, ///< Pad the gradient's edges. - PLUTOVG_SPREAD_METHOD_REFLECT, ///< Reflect the gradient beyond its bounds. - PLUTOVG_SPREAD_METHOD_REPEAT ///< Repeat the gradient pattern. -} plutovg_spread_method_t; - -/** - * @brief Represents a gradient stop. - */ -typedef struct { - float offset; ///< The offset of the gradient stop, as a value between 0 and 1. - plutovg_color_t color; ///< The color of the gradient stop. -} plutovg_gradient_stop_t; - -/** - * @brief Represents a paint object used for drawing operations. - */ -typedef struct plutovg_paint plutovg_paint_t; - -/** - * @brief Creates a solid RGB paint. - * - * @param r The red component (0 to 1). - * @param g The green component (0 to 1). - * @param b The blue component (0 to 1). - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API plutovg_paint_t* plutovg_paint_create_rgb(float r, float g, float b); - -/** - * @brief Creates a solid RGBA paint. - * - * @param r The red component (0 to 1). - * @param g The green component (0 to 1). - * @param b The blue component (0 to 1). - * @param a The alpha component (0 to 1). - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API plutovg_paint_t* plutovg_paint_create_rgba(float r, float g, float b, float a); - -/** - * @brief Creates a solid color paint. - * - * @param color A pointer to the `plutovg_color_t` object. - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API plutovg_paint_t* plutovg_paint_create_color(const plutovg_color_t* color); - -/** - * @brief Creates a linear gradient paint. - * - * @param x1 The x coordinate of the gradient start. - * @param y1 The y coordinate of the gradient start. - * @param x2 The x coordinate of the gradient end. - * @param y2 The y coordinate of the gradient end. - * @param spread The gradient spread method. - * @param stops Array of gradient stops. - * @param nstops Number of gradient stops. - * @param matrix Optional transformation matrix. - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API 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); - -/** - * @brief Creates a radial gradient paint. - * - * @param cx The x coordinate of the gradient center. - * @param cy The y coordinate of the gradient center. - * @param cr The radius of the gradient. - * @param fx The x coordinate of the focal point. - * @param fy The y coordinate of the focal point. - * @param fr The radius of the focal point. - * @param spread The gradient spread method. - * @param stops Array of gradient stops. - * @param nstops Number of gradient stops. - * @param matrix Optional transformation matrix. - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API 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); - -/** - * @brief Creates a texture paint from a surface. - * - * @param surface The texture surface. - * @param type The texture type (plain or tiled). - * @param opacity The opacity of the texture (0 to 1). - * @param matrix Optional transformation matrix. - * @return A pointer to the created `plutovg_paint_t` object. - */ -PLUTOVG_API plutovg_paint_t* plutovg_paint_create_texture(plutovg_surface_t* surface, plutovg_texture_type_t type, float opacity, const plutovg_matrix_t* matrix); - -/** - * @brief Increments the reference count of a paint object. - * - * @param paint A pointer to the `plutovg_paint_t` object. - * @return A pointer to the referenced `plutovg_paint_t` object. - */ -PLUTOVG_API plutovg_paint_t* plutovg_paint_reference(plutovg_paint_t* paint); - -/** - * @brief Decrements the reference count and destroys the paint if the count reaches zero. - * - * @param paint A pointer to the `plutovg_paint_t` object. - */ -PLUTOVG_API void plutovg_paint_destroy(plutovg_paint_t* paint); - -/** - * @brief Retrieves the reference count of a paint object. - * - * @param paint A pointer to the `plutovg_paint_t` object. - * @return The reference count of the `plutovg_paint_t` object. - */ -PLUTOVG_API int plutovg_paint_get_reference_count(const plutovg_paint_t* paint); - -/** - * @brief Defines fill rule types for filling paths. - */ -typedef enum { - PLUTOVG_FILL_RULE_NON_ZERO, ///< Non-zero winding fill rule. - PLUTOVG_FILL_RULE_EVEN_ODD ///< Even-odd fill rule. -} plutovg_fill_rule_t; - -/** - * @brief Defines compositing operations. - */ -typedef enum { - PLUTOVG_OPERATOR_CLEAR, ///< Clears the destination (resulting in a fully transparent image). - PLUTOVG_OPERATOR_SRC, ///< Source replaces destination. - PLUTOVG_OPERATOR_DST, ///< Destination is kept, source is ignored. - PLUTOVG_OPERATOR_SRC_OVER, ///< Source is composited over destination. - PLUTOVG_OPERATOR_DST_OVER, ///< Destination is composited over source. - PLUTOVG_OPERATOR_SRC_IN, ///< Source within destination (only the overlapping part of source is shown). - PLUTOVG_OPERATOR_DST_IN, ///< Destination within source. - PLUTOVG_OPERATOR_SRC_OUT, ///< Source outside destination (non-overlapping part of source is shown). - PLUTOVG_OPERATOR_DST_OUT, ///< Destination outside source. - PLUTOVG_OPERATOR_SRC_ATOP, ///< Source atop destination (source shown over destination but only in the destination's bounds). - PLUTOVG_OPERATOR_DST_ATOP, ///< Destination atop source (destination shown over source but only in the source's bounds). - PLUTOVG_OPERATOR_XOR ///< Source and destination are combined, but their overlapping regions are cleared. -} plutovg_operator_t; - -/** - * @brief Defines the shape used at the ends of open subpaths. - */ -typedef enum { - PLUTOVG_LINE_CAP_BUTT, ///< Flat edge at the end of the stroke. - PLUTOVG_LINE_CAP_ROUND, ///< Rounded ends at the end of the stroke. - PLUTOVG_LINE_CAP_SQUARE ///< Square ends at the end of the stroke. -} plutovg_line_cap_t; - -/** - * @brief Defines the shape used at the corners of paths. - */ -typedef enum { - PLUTOVG_LINE_JOIN_MITER, ///< Miter join with sharp corners. - PLUTOVG_LINE_JOIN_ROUND, ///< Rounded join. - PLUTOVG_LINE_JOIN_BEVEL ///< Beveled join with a flattened corner. -} plutovg_line_join_t; - -/** - * @brief Represents a drawing context. - */ -typedef struct plutovg_canvas plutovg_canvas_t; - -/** - * @brief Creates a drawing context on a surface. - * - * @param surface A pointer to a `plutovg_surface_t` object. - * @return A pointer to the newly created `plutovg_canvas_t` object. - */ -PLUTOVG_API plutovg_canvas_t* plutovg_canvas_create(plutovg_surface_t* surface); - -/** - * @brief Increases the reference count of the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The same pointer to the `plutovg_canvas_t` object. - */ -PLUTOVG_API plutovg_canvas_t* plutovg_canvas_reference(plutovg_canvas_t* canvas); - -/** - * @brief Decreases the reference count and destroys the canvas when it reaches zero. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_destroy(plutovg_canvas_t* canvas); - -/** - * @brief Retrieves the reference count of the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current reference count. - */ -PLUTOVG_API int plutovg_canvas_get_reference_count(const plutovg_canvas_t* canvas); - -/** - * @brief Gets the surface associated with the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return A pointer to the `plutovg_surface_t` object. - */ -PLUTOVG_API plutovg_surface_t* plutovg_canvas_get_surface(const plutovg_canvas_t* canvas); - -/** - * @brief Saves the current state of the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_save(plutovg_canvas_t* canvas); - -/** - * @brief Restores the canvas to the most recently saved state. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_restore(plutovg_canvas_t* canvas); - -/** - * @brief Sets the current paint to a solid color. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param r The red component (0 to 1). - * @param g The green component (0 to 1). - * @param b The blue component (0 to 1). - */ -PLUTOVG_API void plutovg_canvas_set_rgb(plutovg_canvas_t* canvas, float r, float g, float b); - -/** - * @brief Sets the current paint to a solid color. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param r The red component (0 to 1). - * @param g The green component (0 to 1). - * @param b The blue component (0 to 1). - * @param a The alpha component (0 to 1). - */ -PLUTOVG_API void plutovg_canvas_set_rgba(plutovg_canvas_t* canvas, float r, float g, float b, float a); - -/** - * @brief Sets the current paint to a solid color. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param color A pointer to a `plutovg_color_t` object. - */ -PLUTOVG_API void plutovg_canvas_set_color(plutovg_canvas_t* canvas, const plutovg_color_t* color); - -/** - * @brief Sets the current paint to a linear gradient. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x1 The x coordinate of the start point. - * @param y1 The y coordinate of the start point. - * @param x2 The x coordinate of the end point. - * @param y2 The y coordinate of the end point. - * @param spread The gradient spread method. - * @param stops Array of gradient stops. - * @param nstops Number of gradient stops. - * @param matrix Optional transformation matrix. - */ -PLUTOVG_API 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); - -/** - * @brief Sets the current paint to a radial gradient. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param cx The x coordinate of the center. - * @param cy The y coordinate of the center. - * @param cr The radius of the gradient. - * @param fx The x coordinate of the focal point. - * @param fy The y coordinate of the focal point. - * @param fr The radius of the focal point. - * @param spread The gradient spread method. - * @param stops Array of gradient stops. - * @param nstops Number of gradient stops. - * @param matrix Optional transformation matrix. - */ -PLUTOVG_API 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); - -/** - * @brief Sets the current paint to a texture. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param surface The texture surface. - * @param type The texture type (plain or tiled). - * @param opacity The opacity of the texture (0 to 1). - * @param matrix Optional transformation matrix. - */ -PLUTOVG_API 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); - -/** - * @brief Sets the current paint. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param paint The paint to be used for subsequent drawing operations. - */ -PLUTOVG_API void plutovg_canvas_set_paint(plutovg_canvas_t* canvas, plutovg_paint_t* paint); - -/** - * @brief Retrieves the current paint. - * - * If not set, the default paint is opaque black color. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param color A pointer to a `plutovg_color_t` object where the current color will be stored. - * @return The current `plutovg_paint_t` used for drawing operations. If no paint is set, `NULL` is returned. - */ -PLUTOVG_API plutovg_paint_t* plutovg_canvas_get_paint(const plutovg_canvas_t* canvas, plutovg_color_t* color); - -/** - * @brief Assigns a font-face cache to the canvas for font management. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param cache A pointer to a `plutovg_font_face_cache_t` object, or NULL to unset the current cache. - */ -PLUTOVG_API void plutovg_canvas_set_font_face_cache(plutovg_canvas_t* canvas, plutovg_font_face_cache_t* cache); - -/** - * @brief Returns the font-face cache associated with the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return A pointer to the associated `plutovg_font_face_cache_t` object, or NULL if none is set. - */ -PLUTOVG_API plutovg_font_face_cache_t* plutovg_canvas_get_font_face_cache(const plutovg_canvas_t* canvas); - -/** - * @brief Add a font face to the canvas using the specified family and style. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param family The font family name to associate with the face. - * @param bold Whether the font is bold. - * @param italic Whether the font is italic. - * @param face A pointer to the `plutovg_font_face_t` object to add. - */ -PLUTOVG_API void plutovg_canvas_add_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, plutovg_font_face_t* face); - -/** - * @brief Load a font face from a file and add it to the canvas using the specified family and style. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param family The font family name to associate with the face. - * @param bold Whether the font is bold. - * @param italic Whether the font is italic. - * @param filename Path to the font file. - * @param ttcindex Index within a TrueType Collection (use 0 for regular font files). - * @return `true` on success, or `false` if the font could not be loaded. - */ -PLUTOVG_API bool plutovg_canvas_add_font_file(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, const char* filename, int ttcindex); - -/** - * @brief Selects and sets the current font face on the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param family The font family name to select. - * @param bold Whether to match a bold variant. - * @param italic Whether to match an italic variant. - * @return `true` if a matching font was found and set, `false` otherwise. - */ -PLUTOVG_API bool plutovg_canvas_select_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic); - -/** - * @brief Sets the font face and size for text rendering on the canvas. - * - * If not set, the default font face is `NULL`, and the default face size is 12. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param face A pointer to a `plutovg_font_face_t` object representing the font face to use. - * @param size The size of the font, in pixels. This determines the height of the rendered text. - */ -PLUTOVG_API void plutovg_canvas_set_font(plutovg_canvas_t* canvas, plutovg_font_face_t* face, float size); - -/** - * @brief Sets the font face for text rendering on the canvas. - * - * If not set, the default font face is `NULL`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param face A pointer to a `plutovg_font_face_t` object representing the font face to use. - */ -PLUTOVG_API void plutovg_canvas_set_font_face(plutovg_canvas_t* canvas, plutovg_font_face_t* face); - -/** - * @brief Retrieves the current font face used for text rendering on the canvas. - * - * If not set, the default font face is `NULL`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return A pointer to a `plutovg_font_face_t` object representing the current font face. - */ -PLUTOVG_API plutovg_font_face_t* plutovg_canvas_get_font_face(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the font size for text rendering on the canvas. - * - * If not set, the default font size is 12. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param size The size of the font, in pixels. This value defines the height of the rendered text. - */ -PLUTOVG_API void plutovg_canvas_set_font_size(plutovg_canvas_t* canvas, float size); - -/** - * @brief Retrieves the current font size used for text rendering on the canvas. - * - * If not set, the default font size is 12. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current font size, in pixels. This value represents the height of the rendered text. - */ -PLUTOVG_API float plutovg_canvas_get_font_size(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the fill rule. - * - * If not set, the default fill rule is `PLUTOVG_FILL_RULE_NON_ZERO`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param winding The fill rule. - */ -PLUTOVG_API void plutovg_canvas_set_fill_rule(plutovg_canvas_t* canvas, plutovg_fill_rule_t winding); - -/** - * @brief Retrieves the current fill rule. - * - * If not set, the default fill rule is `PLUTOVG_FILL_RULE_NON_ZERO`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current fill rule. - */ -PLUTOVG_API plutovg_fill_rule_t plutovg_canvas_get_fill_rule(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the compositing operator. - * - * If not set, the default compositing operator is `PLUTOVG_OPERATOR_SRC_OVER`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param op The compositing operator. - */ -PLUTOVG_API void plutovg_canvas_set_operator(plutovg_canvas_t* canvas, plutovg_operator_t op); - -/** - * @brief Retrieves the current compositing operator. - * - * If not set, the default compositing operator is `PLUTOVG_OPERATOR_SRC_OVER`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current compositing operator. - */ -PLUTOVG_API plutovg_operator_t plutovg_canvas_get_operator(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the global opacity. - * - * If not set, the default global opacity is 1. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param opacity The opacity value (0 to 1). - */ -PLUTOVG_API void plutovg_canvas_set_opacity(plutovg_canvas_t* canvas, float opacity); - -/** - * @brief Retrieves the current global opacity. - * - * If not set, the default global opacity is 1. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current opacity value. - */ -PLUTOVG_API float plutovg_canvas_get_opacity(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the line width. - * - * If not set, the default line width is 1. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param line_width The width of the stroke. - */ -PLUTOVG_API void plutovg_canvas_set_line_width(plutovg_canvas_t* canvas, float line_width); - -/** - * @brief Retrieves the current line width. - * - * If not set, the default line width is 1. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current line width. - */ -PLUTOVG_API float plutovg_canvas_get_line_width(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the line cap style. - * - * If not set, the default line cap is `PLUTOVG_LINE_CAP_BUTT`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param line_cap The line cap style. - */ -PLUTOVG_API void plutovg_canvas_set_line_cap(plutovg_canvas_t* canvas, plutovg_line_cap_t line_cap); - -/** - * @brief Retrieves the current line cap style. - * - * If not set, the default line cap is `PLUTOVG_LINE_CAP_BUTT`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current line cap style. - */ -PLUTOVG_API plutovg_line_cap_t plutovg_canvas_get_line_cap(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the line join style. - * - * If not set, the default line join is `PLUTOVG_LINE_JOIN_MITER`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param line_join The line join style. - */ -PLUTOVG_API void plutovg_canvas_set_line_join(plutovg_canvas_t* canvas, plutovg_line_join_t line_join); - -/** - * @brief Retrieves the current line join style. - * - * If not set, the default line join is `PLUTOVG_LINE_JOIN_MITER`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current line join style. - */ -PLUTOVG_API plutovg_line_join_t plutovg_canvas_get_line_join(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the miter limit. - * - * If not set, the default miter limit is 10. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param miter_limit The miter limit value. - */ -PLUTOVG_API void plutovg_canvas_set_miter_limit(plutovg_canvas_t* canvas, float miter_limit); - -/** - * @brief Retrieves the current miter limit. - * - * If not set, the default miter limit is 10. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current miter limit value. - */ -PLUTOVG_API float plutovg_canvas_get_miter_limit(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the dash pattern. - * - * If not set, the default dash offset is 0, and the default dash array is `NULL`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param offset The dash offset. - * @param dashes Array of dash lengths. - * @param ndashes Number of dash lengths. - */ -PLUTOVG_API void plutovg_canvas_set_dash(plutovg_canvas_t* canvas, float offset, const float* dashes, int ndashes); - -/** - * @brief Sets the dash offset. - * - * If not set, the default dash offset is 0. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param offset The dash offset. - */ -PLUTOVG_API void plutovg_canvas_set_dash_offset(plutovg_canvas_t* canvas, float offset); - -/** - * @brief Retrieves the current dash offset. - * - * If not set, the default dash offset is 0. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current dash offset. - */ -PLUTOVG_API float plutovg_canvas_get_dash_offset(const plutovg_canvas_t* canvas); - -/** - * @brief Sets the dash pattern. - * - * If not set, the default dash array is `NULL`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param dashes Array of dash lengths. - * @param ndashes Number of dash lengths. - */ -PLUTOVG_API void plutovg_canvas_set_dash_array(plutovg_canvas_t* canvas, const float* dashes, int ndashes); - -/** - * @brief Retrieves the current dash pattern. - * - * If not set, the default dash array is `NULL`. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param dashes Pointer to store the dash array. - * @return The number of dash lengths. - */ -PLUTOVG_API int plutovg_canvas_get_dash_array(const plutovg_canvas_t* canvas, const float** dashes); - -/** - * @brief Translates the current transformation matrix by offsets `tx` and `ty`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param tx The translation offset in the x-direction. - * @param ty The translation offset in the y-direction. - */ -PLUTOVG_API void plutovg_canvas_translate(plutovg_canvas_t* canvas, float tx, float ty); - -/** - * @brief Scales the current transformation matrix by factors `sx` and `sy`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param sx The scaling factor in the x-direction. - * @param sy The scaling factor in the y-direction. - */ -PLUTOVG_API void plutovg_canvas_scale(plutovg_canvas_t* canvas, float sx, float sy); - -/** - * @brief Shears the current transformation matrix by factors `shx` and `shy`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param shx The shearing factor in the x-direction. - * @param shy The shearing factor in the y-direction. - */ -PLUTOVG_API void plutovg_canvas_shear(plutovg_canvas_t* canvas, float shx, float shy); - -/** - * @brief Rotates the current transformation matrix by the specified angle (in radians). - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param angle The rotation angle in radians. - */ -PLUTOVG_API void plutovg_canvas_rotate(plutovg_canvas_t* canvas, float angle); - -/** - * @brief Multiplies the current transformation matrix with the specified `matrix`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param matrix A pointer to the `plutovg_matrix_t` object. - */ -PLUTOVG_API void plutovg_canvas_transform(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix); - -/** - * @brief Resets the current transformation matrix to the identity matrix. - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_reset_matrix(plutovg_canvas_t* canvas); - -/** - * @brief Resets the current transformation matrix to the specified `matrix`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param matrix A pointer to the `plutovg_matrix_t` object. - */ -PLUTOVG_API void plutovg_canvas_set_matrix(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix); - -/** - * @brief Stores the current transformation matrix in `matrix`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param A pointer to the `plutovg_matrix_t` to store the matrix. - */ -PLUTOVG_API void plutovg_canvas_get_matrix(const plutovg_canvas_t* canvas, plutovg_matrix_t* matrix); - -/** - * @brief Transforms the point `(x, y)` using the current transformation matrix and stores the result in `(xx, yy)`. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the point to transform. - * @param y The y-coordinate of the point to transform. - * @param xx A pointer to store the transformed x-coordinate. - * @param yy A pointer to store the transformed y-coordinate. - */ -PLUTOVG_API void plutovg_canvas_map(const plutovg_canvas_t* canvas, float x, float y, float* xx, float* yy); - -/** - * @brief Transforms the `src` point using the current transformation matrix and stores the result in `dst`. - * @note `src` and `dst` can be identical. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param src A pointer to the `plutovg_point_t` point to transform. - * @param dst A pointer to the `plutovg_point_t` to store the transformed point. - */ -PLUTOVG_API void plutovg_canvas_map_point(const plutovg_canvas_t* canvas, const plutovg_point_t* src, plutovg_point_t* dst); - -/** - * @brief Transforms the `src` rectangle using the current transformation matrix and stores the result in `dst`. - * @note `src` and `dst` can be identical. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param src A pointer to the `plutovg_rect_t` rectangle to transform. - * @param dst A pointer to the `plutovg_rect_t` to store the transformed rectangle. - */ -PLUTOVG_API void plutovg_canvas_map_rect(const plutovg_canvas_t* canvas, const plutovg_rect_t* src, plutovg_rect_t* dst); - -/** - * @brief Moves the current point to a new position. - * - * Moves the current point to the specified coordinates without adding a line. - * This operation is added to the current path. Equivalent to the SVG `M` command. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the new position. - * @param y The y-coordinate of the new position. - */ -PLUTOVG_API void plutovg_canvas_move_to(plutovg_canvas_t* canvas, float x, float y); - -/** - * @brief Adds a straight line segment to the current path. - * - * Adds a straight line from the current point to the specified coordinates. - * This segment is added to the current path. Equivalent to the SVG `L` command. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the end point of the line. - * @param y The y-coordinate of the end point of the line. - */ -PLUTOVG_API void plutovg_canvas_line_to(plutovg_canvas_t* canvas, float x, float y); - -/** - * @brief Adds a quadratic Bézier curve to the current path. - * - * Adds a quadratic Bézier curve from the current point to the specified end point, - * using the given control point. This curve is added to the current path. Equivalent to the SVG `Q` command. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x1 The x-coordinate of the control point. - * @param y1 The y-coordinate of the control point. - * @param x2 The x-coordinate of the end point of the curve. - * @param y2 The y-coordinate of the end point of the curve. - */ -PLUTOVG_API void plutovg_canvas_quad_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2); - -/** - * @brief Adds a cubic Bézier curve to the current path. - * - * Adds a cubic Bézier curve from the current point to the specified end point, - * using the given control points. This curve is added to the current path. Equivalent to the SVG `C` command. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x1 The x-coordinate of the first control point. - * @param y1 The y-coordinate of the first control point. - * @param x2 The x-coordinate of the second control point. - * @param y2 The y-coordinate of the second control point. - * @param x3 The x-coordinate of the end point of the curve. - * @param y3 The y-coordinate of the end point of the curve. - */ -PLUTOVG_API void plutovg_canvas_cubic_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2, float x3, float y3); - -/** - * @brief Adds an elliptical arc to the current path. - * - * Adds an elliptical arc from the current point to the specified end point, - * defined by radii, rotation angle, and flags for arc type and direction. - * This arc segment is added to the current path. Equivalent to the SVG `A` command. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param rx The x-radius of the ellipse. - * @param ry The y-radius of the ellipse. - * @param angle The rotation angle of the ellipse in degrees. - * @param large_arc_flag If true, add the large arc; otherwise, add the small arc. - * @param sweep_flag If true, add the arc in the positive-angle direction; otherwise, in the negative-angle direction. - * @param x The x-coordinate of the end point. - * @param y The y-coordinate of the end point. - */ -PLUTOVG_API 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); - -/** - * @brief Adds a rectangle to the current path. - * - * Adds a rectangle with the specified position and dimensions to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the rectangle's origin. - * @param y The y-coordinate of the rectangle's origin. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - */ -PLUTOVG_API void plutovg_canvas_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h); - -/** - * @brief Adds a rounded rectangle to the current path. - * - * Adds a rectangle with rounded corners defined by the specified position, - * dimensions, and corner radii to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the rectangle's origin. - * @param y The y-coordinate of the rectangle's origin. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - * @param rx The x-radius of the corners. - * @param ry The y-radius of the corners. - */ -PLUTOVG_API void plutovg_canvas_round_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h, float rx, float ry); - -/** - * @brief Adds an ellipse to the current path. - * - * Adds an ellipse centered at the specified coordinates with the given radii to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param cx The x-coordinate of the ellipse's center. - * @param cy The y-coordinate of the ellipse's center. - * @param rx The x-radius of the ellipse. - * @param ry The y-radius of the ellipse. - */ -PLUTOVG_API void plutovg_canvas_ellipse(plutovg_canvas_t* canvas, float cx, float cy, float rx, float ry); - -/** - * @brief Adds a circle to the current path. - * - * Adds a circle centered at the specified coordinates with the given radius to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param cx The x-coordinate of the circle's center. - * @param cy The y-coordinate of the circle's center. - * @param r The radius of the circle. - */ -PLUTOVG_API void plutovg_canvas_circle(plutovg_canvas_t* canvas, float cx, float cy, float r); - -/** - * @brief Adds an arc to the current path. - * - * Adds an arc centered at the specified coordinates, with a given radius, - * starting and ending at the specified angles. The direction of the arc is - * determined by `ccw`. This arc segment is added to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param cx The x-coordinate of the arc's center. - * @param cy The y-coordinate of the arc's center. - * @param r The radius of the arc. - * @param a0 The starting angle of the arc in radians. - * @param a1 The ending angle of the arc in radians. - * @param ccw If true, add the arc counter-clockwise; otherwise, clockwise. - */ -PLUTOVG_API void plutovg_canvas_arc(plutovg_canvas_t* canvas, float cx, float cy, float r, float a0, float a1, bool ccw); - -/** - * @brief Adds a path to the current path. - * - * Appends the elements of the specified path to the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param path A pointer to the `plutovg_path_t` object to be added. - */ -PLUTOVG_API void plutovg_canvas_add_path(plutovg_canvas_t* canvas, const plutovg_path_t* path); - -/** - * @brief Starts a new path on the canvas. - * - * Begins a new path, clearing any existing path data. The new path starts with no commands. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_new_path(plutovg_canvas_t* canvas); - -/** - * @brief Closes the current path. - * - * Closes the current path by adding a straight line back to the starting point. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_close_path(plutovg_canvas_t* canvas); - -/** - * @brief Retrieves the current point of the canvas. - * - * Gets the coordinates of the current point in the canvas, which is the last point - * added or moved to in the current path. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the current point. - * @param y The y-coordinate of the current point. - */ -PLUTOVG_API void plutovg_canvas_get_current_point(const plutovg_canvas_t* canvas, float* x, float* y); - -/** - * @brief Gets the current path from the canvas. - * - * Retrieves the path object representing the sequence of path commands added to the canvas. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @return The current path. - */ -PLUTOVG_API plutovg_path_t* plutovg_canvas_get_path(const plutovg_canvas_t* canvas); - -/** - * @brief Tests whether a point lies within the current fill region. - * - * Determines whether the point at coordinates `(x, y)` falls within the area - * that would be filled by a `plutovg_canvas_fill()` operation, given the current path, - * fill rule, and transformation state. - * - * @note Clipping and surface dimensions are not considered in this test. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The X coordinate of the point, in user space. - * @param y The Y coordinate of the point, in user space. - * @return `true` if the point is within the fill region, `false` otherwise. - */ -PLUTOVG_API bool plutovg_canvas_fill_contains(plutovg_canvas_t* canvas, float x, float y); - -/** - * @brief Tests whether a point lies within the current stroke region. - * - * Determines whether the point at coordinates `(x, y)` falls within the area - * that would be stroked by a `plutovg_canvas_stroke()` operation, given the current path, - * stroke width, joins, caps, miter limit, dash pattern, and transformation state. - * - * @note Clipping and surface dimensions are not considered in this test. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The X coordinate of the point, in user space. - * @param y The Y coordinate of the point, in user space. - * @return `true` if the point is within the stroke region, `false` otherwise. - */ -PLUTOVG_API bool plutovg_canvas_stroke_contains(plutovg_canvas_t* canvas, float x, float y); - -/** - * @brief Tests whether a point lies within the current clipping region. - * - * Determines whether the point at coordinates `(x, y)` falls within the active clipping - * region on the canvas. - * - * If no clipping is applied, the default clipping region covers the entire canvas - * area starting at `(0, 0)` with width and height equal to the canvas dimensions. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The X coordinate of the point, in user space. - * @param y The Y coordinate of the point, in user space. - * @return `true` if the point is within the clipping region, `false` otherwise. - */ -PLUTOVG_API bool plutovg_canvas_clip_contains(plutovg_canvas_t* canvas, float x, float y); - -/** - * @brief Computes the bounding box of the area that would be affected by a fill operation. - * - * Computes an axis-aligned bounding box in user space that encloses the area - * which would be affected by a fill operation (`plutovg_canvas_fill()`) given the current path, - * fill rule, and transformation state. - * - * @note Clipping and surface dimensions are not considered in this calculation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param extents A pointer to a `plutovg_rect_t` structure that receives the bounding box. - */ -PLUTOVG_API void plutovg_canvas_fill_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents); - -/** - * @brief Computes the bounding box of the area that would be affected by a stroke operation. - * - * Computes an axis-aligned bounding box in user space that encloses the area - * which would be affected by a stroke operation (`plutovg_canvas_stroke()`) given the current path, - * stroke width, joins, caps, miter limit, dash pattern, and transformation state. - * - * @note Clipping and surface dimensions are not considered in this calculation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param extents A pointer to a `plutovg_rect_t` structure that receives the bounding box. - */ -PLUTOVG_API void plutovg_canvas_stroke_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents); - -/** - * @brief Gets the bounding box of the current clipping region. - * - * Computes an axis-aligned bounding box in user space that encloses the currently active - * clipping region on the canvas. - * - * If no clip is applied, the returned rectangle covers the entire canvas area, - * starting at `(0, 0)` with width and height equal to the canvas dimensions. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param extents A pointer to a `plutovg_rect_t` structure that receives the bounding box. - */ -PLUTOVG_API void plutovg_canvas_clip_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents); - -/** - * @brief A drawing operator that fills the current path according to the current fill rule. - * - * The current path will be cleared after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_fill(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that strokes the current path according to the current stroke settings. - * - * The current path will be cleared after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_stroke(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that intersects the current clipping region with the current path according to the current fill rule. - * - * The current path will be cleared after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_clip(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that paints the current clipping region using the current paint. - * - * @note The current path will not be affected by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_paint(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that fills the current path according to the current fill rule. - * - * The current path will be preserved after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_fill_preserve(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that strokes the current path according to the current stroke settings. - * - * The current path will be preserved after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_stroke_preserve(plutovg_canvas_t* canvas); - -/** - * @brief A drawing operator that intersects the current clipping region with the current path according to the current fill rule. - * - * The current path will be preserved after this operation. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - */ -PLUTOVG_API void plutovg_canvas_clip_preserve(plutovg_canvas_t* canvas); - -/** - * @brief Fills a rectangle according to the current fill rule. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the rectangle's origin. - * @param y The y-coordinate of the rectangle's origin. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - */ -PLUTOVG_API void plutovg_canvas_fill_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h); - -/** - * @brief Fills a path according to the current fill rule. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param path The `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_canvas_fill_path(plutovg_canvas_t* canvas, const plutovg_path_t* path); - -/** - * @brief Strokes a rectangle with the current stroke settings. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the rectangle's origin. - * @param y The y-coordinate of the rectangle's origin. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - */ -PLUTOVG_API void plutovg_canvas_stroke_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h); - -/** - * @brief Strokes a path with the current stroke settings. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param path The `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_canvas_stroke_path(plutovg_canvas_t* canvas, const plutovg_path_t* path); - -/** - * @brief Intersects the current clipping region with a rectangle according to the current fill rule. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param x The x-coordinate of the rectangle's origin. - * @param y The y-coordinate of the rectangle's origin. - * @param w The width of the rectangle. - * @param h The height of the rectangle. - */ -PLUTOVG_API void plutovg_canvas_clip_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h); - -/** - * @brief Intersects the current clipping region with a path according to the current fill rule. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param path The `plutovg_path_t` object. - */ -PLUTOVG_API void plutovg_canvas_clip_path(plutovg_canvas_t* canvas, const plutovg_path_t* path); - -/** - * @brief Adds a glyph to the current path at the specified origin. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param codepoint The glyph codepoint. - * @param x The x-coordinate of the origin. - * @param y The y-coordinate of the origin. - * @return The advance width of the glyph. - */ -PLUTOVG_API float plutovg_canvas_add_glyph(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float x, float y); - -/** - * @brief Adds text to the current path at the specified origin. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param text The text data. - * @param length The length of the text data, or -1 if null-terminated. - * @param encoding The encoding of the text data. - * @param x The x-coordinate of the origin. - * @param y The y-coordinate of the origin. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_canvas_add_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y); - -/** - * @brief Fills a text at the specified origin. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param text The text data. - * @param length The length of the text data, or -1 if null-terminated. - * @param encoding The encoding of the text data. - * @param x The x-coordinate of the origin. - * @param y The y-coordinate of the origin. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_canvas_fill_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y); - -/** - * @brief Strokes a text at the specified origin. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param text The text data. - * @param length The length of the text data, or -1 if null-terminated. - * @param encoding The encoding of the text data. - * @param x The x-coordinate of the origin. - * @param y The y-coordinate of the origin. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_canvas_stroke_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y); - -/** - * @brief Intersects the current clipping region with text at the specified origin. - * - * @note The current path will be cleared by this operation. - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param text The text data. - * @param length The length of the text data, or -1 if null-terminated. - * @param encoding The encoding of the text data. - * @param x The x-coordinate of the origin. - * @param y The y-coordinate of the origin. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_canvas_clip_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y); - -/** - * @brief Retrieves font metrics for the current font. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param ascent The ascent of the font. - * @param descent The descent of the font. - * @param line_gap The line gap of the font. - * @param extents The bounding box of the font. - */ -PLUTOVG_API void plutovg_canvas_font_metrics(const plutovg_canvas_t* canvas, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents); - -/** - * @brief Retrieves metrics for a specific glyph. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param codepoint The glyph codepoint. - * @param advance_width The advance width of the glyph. - * @param left_side_bearing The left side bearing of the glyph. - * @param extents The bounding box of the glyph. - */ -PLUTOVG_API void plutovg_canvas_glyph_metrics(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents); - -/** - * @brief Retrieves the extents of a text. - * - * @param canvas A pointer to a `plutovg_canvas_t` object. - * @param text The text data. - * @param length The length of the text data, or -1 if null-terminated. - * @param encoding The encoding of the text data. - * @param extents The bounding box of the text. - * @return The total advance width of the text. - */ -PLUTOVG_API float plutovg_canvas_text_extents(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents); - -#ifdef __cplusplus -} -#endif - -#endif // PLUTOVG_H diff --git a/vendor/lunasvg/plutovg/source/plutovg-blend.c b/vendor/lunasvg/plutovg/source/plutovg-blend.c deleted file mode 100644 index 77d8628..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-blend.c +++ /dev/null @@ -1,1144 +0,0 @@ -#include "plutovg-private.h" -#include "plutovg-utils.h" - -#include -#include - -#define COLOR_TABLE_SIZE 1024 -typedef struct { - plutovg_matrix_t matrix; - plutovg_spread_method_t spread; - uint32_t colortable[COLOR_TABLE_SIZE]; - union { - struct { - float x1, y1; - float x2, y2; - } linear; - struct { - float cx, cy, cr; - float fx, fy, fr; - } radial; - } values; -} gradient_data_t; - -typedef struct { - plutovg_matrix_t matrix; - uint8_t* data; - int width; - int height; - int stride; - int const_alpha; -} texture_data_t; - -typedef struct { - float dx; - float dy; - float l; - float off; -} linear_gradient_values_t; - -typedef struct { - float dx; - float dy; - float dr; - float sqrfr; - float a; - bool extended; -} radial_gradient_values_t; - -static inline uint32_t premultiply_color_with_opacity(const plutovg_color_t* color, float opacity) -{ - uint32_t alpha = lroundf(color->a * opacity * 255); - uint32_t pr = lroundf(color->r * alpha); - uint32_t pg = lroundf(color->g * alpha); - uint32_t pb = lroundf(color->b * alpha); - return (alpha << 24) | (pr << 16) | (pg << 8) | (pb); -} - -static inline uint32_t INTERPOLATE_PIXEL_255(uint32_t x, uint32_t a, uint32_t y, uint32_t b) -{ - uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; - t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; - t &= 0xff00ff; - - x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; - x = (x + ((x >> 8) & 0xff00ff) + 0x800080); - x &= 0xff00ff00; - x |= t; - return x; -} - -static inline uint32_t INTERPOLATE_PIXEL_256(uint32_t x, uint32_t a, uint32_t y, uint32_t b) -{ - uint32_t t = (x & 0xff00ff) * a + (y & 0xff00ff) * b; - t >>= 8; - t &= 0xff00ff; - - x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b; - x &= 0xff00ff00; - x |= t; - return x; -} - -static inline uint32_t BYTE_MUL(uint32_t x, uint32_t a) -{ - uint32_t t = (x & 0xff00ff) * a; - t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; - t &= 0xff00ff; - - x = ((x >> 8) & 0xff00ff) * a; - x = (x + ((x >> 8) & 0xff00ff) + 0x800080); - x &= 0xff00ff00; - x |= t; - return x; -} - -#ifdef __SSE2__ - -#include - -void plutovg_memfill32(unsigned int* dest, int length, unsigned int value) -{ - __m128i vector_data = _mm_set_epi32(value, value, value, value); - while(length && ((uintptr_t)dest & 0xf)) { - *dest++ = value; - length--; - } - - while(length >= 32) { - _mm_store_si128((__m128i*)(dest), vector_data); - _mm_store_si128((__m128i*)(dest + 4), vector_data); - _mm_store_si128((__m128i*)(dest + 8), vector_data); - _mm_store_si128((__m128i*)(dest + 12), vector_data); - _mm_store_si128((__m128i*)(dest + 16), vector_data); - _mm_store_si128((__m128i*)(dest + 20), vector_data); - _mm_store_si128((__m128i*)(dest + 24), vector_data); - _mm_store_si128((__m128i*)(dest + 28), vector_data); - - dest += 32; - length -= 32; - } - - if(length >= 16) { - _mm_store_si128((__m128i*)(dest), vector_data); - _mm_store_si128((__m128i*)(dest + 4), vector_data); - _mm_store_si128((__m128i*)(dest + 8), vector_data); - _mm_store_si128((__m128i*)(dest + 12), vector_data); - - dest += 16; - length -= 16; - } - - if(length >= 8) { - _mm_store_si128((__m128i*)(dest), vector_data); - _mm_store_si128((__m128i*)(dest + 4), vector_data); - - dest += 8; - length -= 8; - } - - if(length >= 4) { - _mm_store_si128((__m128i*)(dest), vector_data); - - dest += 4; - length -= 4; - } - - while(length) { - *dest++ = value; - length--; - } -} - -#else - -void plutovg_memfill32(unsigned int* dest, int length, unsigned int value) -{ - while(length--) { - *dest++ = value; - } -} - -#endif // __SSE2__ - -static inline int gradient_clamp(const gradient_data_t* gradient, int ipos) -{ - if(gradient->spread == PLUTOVG_SPREAD_METHOD_REPEAT) { - ipos = ipos % COLOR_TABLE_SIZE; - ipos = ipos < 0 ? COLOR_TABLE_SIZE + ipos : ipos; - } else if(gradient->spread == PLUTOVG_SPREAD_METHOD_REFLECT) { - const int limit = COLOR_TABLE_SIZE * 2; - ipos = ipos % limit; - ipos = ipos < 0 ? limit + ipos : ipos; - ipos = ipos >= COLOR_TABLE_SIZE ? limit - 1 - ipos : ipos; - } else { - if(ipos < 0) { - ipos = 0; - } else if(ipos >= COLOR_TABLE_SIZE) { - ipos = COLOR_TABLE_SIZE - 1; - } - } - - return ipos; -} - -#define FIXPT_BITS 8 -#define FIXPT_SIZE (1 << FIXPT_BITS) -static inline uint32_t gradient_pixel_fixed(const gradient_data_t* gradient, int fixed_pos) -{ - int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; - return gradient->colortable[gradient_clamp(gradient, ipos)]; -} - -static inline uint32_t gradient_pixel(const gradient_data_t* gradient, float pos) -{ - int ipos = (int)(pos * (COLOR_TABLE_SIZE - 1) + 0.5f); - return gradient->colortable[gradient_clamp(gradient, ipos)]; -} - -static void fetch_linear_gradient(uint32_t* buffer, const linear_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length) -{ - float t, inc; - float rx = 0, ry = 0; - - if(v->l == 0.f) { - t = inc = 0; - } else { - rx = gradient->matrix.c * (y + 0.5f) + gradient->matrix.a * (x + 0.5f) + gradient->matrix.e; - ry = gradient->matrix.d * (y + 0.5f) + gradient->matrix.b * (x + 0.5f) + gradient->matrix.f; - t = v->dx * rx + v->dy * ry + v->off; - inc = v->dx * gradient->matrix.a + v->dy * gradient->matrix.b; - t *= (COLOR_TABLE_SIZE - 1); - inc *= (COLOR_TABLE_SIZE - 1); - } - - const uint32_t* end = buffer + length; - if(inc > -1e-5f && inc < 1e-5f) { - plutovg_memfill32(buffer, length, gradient_pixel_fixed(gradient, (int)(t * FIXPT_SIZE))); - } else { - if(t + inc * length < (float)(INT_MAX >> (FIXPT_BITS + 1)) && t + inc * length > (float)(INT_MIN >> (FIXPT_BITS + 1))) { - int t_fixed = (int)(t * FIXPT_SIZE); - int inc_fixed = (int)(inc * FIXPT_SIZE); - while(buffer < end) { - *buffer = gradient_pixel_fixed(gradient, t_fixed); - t_fixed += inc_fixed; - ++buffer; - } - } else { - while(buffer < end) { - *buffer = gradient_pixel(gradient, t / COLOR_TABLE_SIZE); - t += inc; - ++buffer; - } - } - } -} - -static void fetch_radial_gradient(uint32_t* buffer, const radial_gradient_values_t* v, const gradient_data_t* gradient, int y, int x, int length) -{ - if(v->a == 0.f) { - plutovg_memfill32(buffer, length, 0); - return; - } - - float rx = gradient->matrix.c * (y + 0.5f) + gradient->matrix.e + gradient->matrix.a * (x + 0.5f); - float ry = gradient->matrix.d * (y + 0.5f) + gradient->matrix.f + gradient->matrix.b * (x + 0.5f); - - rx -= gradient->values.radial.fx; - ry -= gradient->values.radial.fy; - - float inv_a = 1.f / (2.f * v->a); - float delta_rx = gradient->matrix.a; - float delta_ry = gradient->matrix.b; - - float b = 2 * (v->dr * gradient->values.radial.fr + rx * v->dx + ry * v->dy); - float delta_b = 2 * (delta_rx * v->dx + delta_ry * v->dy); - float b_delta_b = 2 * b * delta_b; - float delta_b_delta_b = 2 * delta_b * delta_b; - - float bb = b * b; - float delta_bb = delta_b * delta_b; - - b *= inv_a; - delta_b *= inv_a; - - float rxrxryry = rx * rx + ry * ry; - float delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry; - float rx_plus_ry = 2 * (rx * delta_rx + ry * delta_ry); - float delta_rx_plus_ry = 2 * delta_rxrxryry; - - inv_a *= inv_a; - - float det = (bb - 4 * v->a * (v->sqrfr - rxrxryry)) * inv_a; - float delta_det = (b_delta_b + delta_bb + 4 * v->a * (rx_plus_ry + delta_rxrxryry)) * inv_a; - float delta_delta_det = (delta_b_delta_b + 4 * v->a * delta_rx_plus_ry) * inv_a; - - const uint32_t* end = buffer + length; - if(v->extended) { - while(buffer < end) { - uint32_t result = 0; - if(det >= 0) { - float w = sqrtf(det) - b; - if(gradient->values.radial.fr + v->dr * w >= 0) { - result = gradient_pixel(gradient, w); - } - } - - *buffer = result; - det += delta_det; - delta_det += delta_delta_det; - b += delta_b; - ++buffer; - } - } else { - while(buffer < end) { - *buffer++ = gradient_pixel(gradient, sqrtf(det) - b); - det += delta_det; - delta_det += delta_delta_det; - b += delta_b; - } - } -} - -static void composition_solid_clear(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha == 255) { - plutovg_memfill32(dest, length, 0); - } else { - uint32_t ialpha = 255 - const_alpha; - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], ialpha); - } - } -} - -static void composition_solid_source(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha == 255) { - plutovg_memfill32(dest, length, color); - } else { - uint32_t ialpha = 255 - const_alpha; - color = BYTE_MUL(color, const_alpha); - for(int i = 0; i < length; i++) { - dest[i] = color + BYTE_MUL(dest[i], ialpha); - } - } -} - -static void composition_solid_destination(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ -} - -static void composition_solid_source_over(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - uint32_t ialpha = 255 - plutovg_alpha(color); - for(int i = 0; i < length; i++) { - dest[i] = color + BYTE_MUL(dest[i], ialpha); - } -} - -static void composition_solid_destination_over(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = d + BYTE_MUL(color, plutovg_alpha(~d)); - } -} - -static void composition_solid_source_in(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(color, plutovg_alpha(dest[i])); - } - } else { - color = BYTE_MUL(color, const_alpha); - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(d), d, cia); - } - } -} - -static void composition_solid_destination_in(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - uint32_t a = plutovg_alpha(color); - if(const_alpha != 255) - a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], a); - } -} - -static void composition_solid_source_out(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(color, plutovg_alpha(~dest[i])); - } - } else { - color = BYTE_MUL(color, const_alpha); - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(~d), d, cia); - } - } -} - -static void composition_solid_destination_out(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - uint32_t a = plutovg_alpha(~color); - if(const_alpha != 255) - a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], a); - } -} - -static void composition_solid_source_atop(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - uint32_t sia = plutovg_alpha(~color); - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(d), d, sia); - } -} - -static void composition_solid_destination_atop(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - uint32_t a = plutovg_alpha(color); - if(const_alpha != 255) { - color = BYTE_MUL(color, const_alpha); - a = plutovg_alpha(color) + 255 - const_alpha; - } - - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(d, a, color, plutovg_alpha(~d)); - } -} - -static void composition_solid_xor(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha) -{ - if(const_alpha != 255) - color = BYTE_MUL(color, const_alpha); - uint32_t sia = plutovg_alpha(~color); - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(~d), d, sia); - } -} - -typedef void(*composition_solid_function_t)(uint32_t* dest, int length, uint32_t color, uint32_t const_alpha); - -static const composition_solid_function_t composition_solid_table[] = { - composition_solid_clear, - composition_solid_source, - composition_solid_destination, - composition_solid_source_over, - composition_solid_destination_over, - composition_solid_source_in, - composition_solid_destination_in, - composition_solid_source_out, - composition_solid_destination_out, - composition_solid_source_atop, - composition_solid_destination_atop, - composition_solid_xor -}; - -static void composition_clear(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - plutovg_memfill32(dest, length, 0); - } else { - uint32_t ialpha = 255 - const_alpha; - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], ialpha); - } - } -} - -static void composition_source(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - memcpy(dest, src, length * sizeof(uint32_t)); - } else { - uint32_t ialpha = 255 - const_alpha; - for(int i = 0; i < length; i++) { - dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha); - } - } -} - -static void composition_destination(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ -} - -static void composition_source_over(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - uint32_t s = src[i]; - if(s >= 0xff000000) { - dest[i] = s; - } else if(s != 0) { - dest[i] = s + BYTE_MUL(dest[i], plutovg_alpha(~s)); - } - } - } else { - for(int i = 0; i < length; i++) { - uint32_t s = BYTE_MUL(src[i], const_alpha); - dest[i] = s + BYTE_MUL(dest[i], plutovg_alpha(~s)); - } - } -} - -static void composition_destination_over(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - dest[i] = d + BYTE_MUL(src[i], plutovg_alpha(~d)); - } - } else { - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - uint32_t s = BYTE_MUL(src[i], const_alpha); - dest[i] = d + BYTE_MUL(s, plutovg_alpha(~d)); - } - } -} - -static void composition_source_in(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(src[i], plutovg_alpha(dest[i])); - } - } else { - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - uint32_t s = BYTE_MUL(src[i], const_alpha); - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(d), d, cia); - } - } -} - -static void composition_destination_in(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], plutovg_alpha(src[i])); - } - } else { - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t a = BYTE_MUL(plutovg_alpha(src[i]), const_alpha) + cia; - dest[i] = BYTE_MUL(dest[i], a); - } - } -} - -static void composition_source_out(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(src[i], plutovg_alpha(~dest[i])); - } - } else { - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t s = BYTE_MUL(src[i], const_alpha); - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(~d), d, cia); - } - } -} - -static void composition_destination_out(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - dest[i] = BYTE_MUL(dest[i], plutovg_alpha(~src[i])); - } - } else { - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t sia = BYTE_MUL(plutovg_alpha(~src[i]), const_alpha) + cia; - dest[i] = BYTE_MUL(dest[i], sia); - } - } -} - -static void composition_source_atop(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - uint32_t s = src[i]; - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(d), d, plutovg_alpha(~s)); - } - } else { - for(int i = 0; i < length; i++) { - uint32_t s = BYTE_MUL(src[i], const_alpha); - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(d), d, plutovg_alpha(~s)); - } - } -} - -static void composition_destination_atop(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - uint32_t s = src[i]; - uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL_255(d, plutovg_alpha(s), s, plutovg_alpha(~d)); - } - } else { - uint32_t cia = 255 - const_alpha; - for(int i = 0; i < length; i++) { - uint32_t s = BYTE_MUL(src[i], const_alpha); - uint32_t d = dest[i]; - uint32_t a = plutovg_alpha(s) + cia; - dest[i] = INTERPOLATE_PIXEL_255(d, a, s, plutovg_alpha(~d)); - } - } -} - -static void composition_xor(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha) -{ - if(const_alpha == 255) { - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - uint32_t s = src[i]; - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(~d), d, plutovg_alpha(~s)); - } - } else { - for(int i = 0; i < length; i++) { - uint32_t d = dest[i]; - uint32_t s = BYTE_MUL(src[i], const_alpha); - dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(~d), d, plutovg_alpha(~s)); - } - } -} - -typedef void(*composition_function_t)(uint32_t* dest, int length, const uint32_t* src, uint32_t const_alpha); - -static const composition_function_t composition_table[] = { - composition_clear, - composition_source, - composition_destination, - composition_source_over, - composition_destination_over, - composition_source_in, - composition_destination_in, - composition_source_out, - composition_destination_out, - composition_source_atop, - composition_destination_atop, - composition_xor -}; - -static void blend_solid(plutovg_surface_t* surface, plutovg_operator_t op, uint32_t solid, const plutovg_span_buffer_t* span_buffer) -{ - composition_solid_function_t func = composition_solid_table[op]; - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x; - func(target, spans->len, solid, spans->coverage); - ++spans; - } -} - -#define BUFFER_SIZE 1024 -static void blend_linear_gradient(plutovg_surface_t* surface, plutovg_operator_t op, const gradient_data_t* gradient, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - unsigned int buffer[BUFFER_SIZE]; - - linear_gradient_values_t v; - v.dx = gradient->values.linear.x2 - gradient->values.linear.x1; - v.dy = gradient->values.linear.y2 - gradient->values.linear.y1; - v.l = v.dx * v.dx + v.dy * v.dy; - v.off = 0.f; - if(v.l != 0.f) { - v.dx /= v.l; - v.dy /= v.l; - v.off = -v.dx * gradient->values.linear.x1 - v.dy * gradient->values.linear.y1; - } - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - int length = spans->len; - int x = spans->x; - while(length) { - int l = plutovg_min(length, BUFFER_SIZE); - fetch_linear_gradient(buffer, &v, gradient, spans->y, x, l); - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x; - func(target, l, buffer, spans->coverage); - x += l; - length -= l; - } - - ++spans; - } -} - -static void blend_radial_gradient(plutovg_surface_t* surface, plutovg_operator_t op, const gradient_data_t* gradient, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - unsigned int buffer[BUFFER_SIZE]; - - radial_gradient_values_t v; - v.dx = gradient->values.radial.cx - gradient->values.radial.fx; - v.dy = gradient->values.radial.cy - gradient->values.radial.fy; - v.dr = gradient->values.radial.cr - gradient->values.radial.fr; - v.sqrfr = gradient->values.radial.fr * gradient->values.radial.fr; - v.a = v.dr * v.dr - v.dx * v.dx - v.dy * v.dy; - v.extended = gradient->values.radial.fr != 0.f || v.a <= 0.f; - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - int length = spans->len; - int x = spans->x; - while(length) { - int l = plutovg_min(length, BUFFER_SIZE); - fetch_radial_gradient(buffer, &v, gradient, spans->y, x, l); - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + x; - func(target, l, buffer, spans->coverage); - x += l; - length -= l; - } - - ++spans; - } -} - -static void blend_untransformed_argb(plutovg_surface_t* surface, plutovg_operator_t op, const texture_data_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - - const int image_width = texture->width; - const int image_height = texture->height; - - int xoff = (int)(texture->matrix.e); - int yoff = (int)(texture->matrix.f); - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - int x = spans->x; - int length = spans->len; - int sx = xoff + x; - int sy = yoff + spans->y; - if(sy >= 0 && sy < image_height && sx < image_width) { - if(sx < 0) { - x -= sx; - length += sx; - sx = 0; - } - - if(sx + length > image_width) - length = image_width - sx; - if(length > 0) { - const int coverage = (spans->coverage * texture->const_alpha) >> 8; - const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx; - uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x; - func(dest, length, src, coverage); - } - } - - ++spans; - } -} - -#define FIXED_SCALE (1 << 16) -static void blend_transformed_argb(plutovg_surface_t* surface, plutovg_operator_t op, const texture_data_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - uint32_t buffer[BUFFER_SIZE]; - - int image_width = texture->width; - int image_height = texture->height; - - int fdx = (int)(texture->matrix.a * FIXED_SCALE); - int fdy = (int)(texture->matrix.b * FIXED_SCALE); - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x; - - const float cx = spans->x + 0.5f; - const float cy = spans->y + 0.5f; - - int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE); - int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE); - - int length = spans->len; - const int coverage = (spans->coverage * texture->const_alpha) >> 8; - while(length) { - int l = plutovg_min(length, BUFFER_SIZE); - const uint32_t* end = buffer + l; - uint32_t* b = buffer; - while(b < end) { - int px = x >> 16; - int py = y >> 16; - if((px < 0) || (px >= image_width) || (py < 0) || (py >= image_height)) { - *b = 0x00000000; - } else { - *b = ((const uint32_t*)(texture->data + py * texture->stride))[px]; - } - - x += fdx; - y += fdy; - ++b; - } - - func(target, l, buffer, coverage); - target += l; - length -= l; - } - - ++spans; - } -} - -static void blend_untransformed_tiled_argb(plutovg_surface_t* surface, plutovg_operator_t op, const texture_data_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - - int image_width = texture->width; - int image_height = texture->height; - - int xoff = (int)(texture->matrix.e) % image_width; - int yoff = (int)(texture->matrix.f) % image_height; - - if(xoff < 0) - xoff += image_width; - if(yoff < 0) { - yoff += image_height; - } - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - int x = spans->x; - int length = spans->len; - int sx = (xoff + spans->x) % image_width; - int sy = (spans->y + yoff) % image_height; - if(sx < 0) - sx += image_width; - if(sy < 0) { - sy += image_height; - } - - const int coverage = (spans->coverage * texture->const_alpha) >> 8; - while(length) { - int l = plutovg_min(image_width - sx, length); - if(BUFFER_SIZE < l) - l = BUFFER_SIZE; - const uint32_t* src = (const uint32_t*)(texture->data + sy * texture->stride) + sx; - uint32_t* dest = (uint32_t*)(surface->data + spans->y * surface->stride) + x; - func(dest, l, src, coverage); - x += l; - sx += l; - length -= l; - if(sx >= image_width) { - sx = 0; - } - } - - ++spans; - } -} - -static void blend_transformed_tiled_argb(plutovg_surface_t* surface, plutovg_operator_t op, const texture_data_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - uint32_t buffer[BUFFER_SIZE]; - - int image_width = texture->width; - int image_height = texture->height; - const int scanline_offset = texture->stride / 4; - - int fdx = (int)(texture->matrix.a * FIXED_SCALE); - int fdy = (int)(texture->matrix.b * FIXED_SCALE); - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x; - const uint32_t* image_bits = (const uint32_t*)texture->data; - - const float cx = spans->x + 0.5f; - const float cy = spans->y + 0.5f; - - int x = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE); - int y = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE); - - const int coverage = (spans->coverage * texture->const_alpha) >> 8; - int length = spans->len; - while(length) { - int l = plutovg_min(length, BUFFER_SIZE); - const uint32_t* end = buffer + l; - uint32_t* b = buffer; - while(b < end) { - int px = x >> 16; - int py = y >> 16; - px %= image_width; - py %= image_height; - if(px < 0) px += image_width; - if(py < 0) py += image_height; - int y_offset = py * scanline_offset; - - assert(px >= 0 && px < image_width); - assert(py >= 0 && py < image_height); - - *b = image_bits[y_offset + px]; - x += fdx; - y += fdy; - ++b; - } - - func(target, l, buffer, coverage); - target += l; - length -= l; - } - - ++spans; - } -} - -static inline uint32_t interpolate_4_pixels(uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, uint32_t distx, uint32_t disty) -{ - uint32_t idistx = 256 - distx; - uint32_t idisty = 256 - disty; - uint32_t xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); - uint32_t xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); - return INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); -} - -#define HALF_POINT (1 << 15) -static void blend_transformed_bilinear_tiled_argb(plutovg_surface_t* surface, plutovg_operator_t op, const texture_data_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - composition_function_t func = composition_table[op]; - uint32_t buffer[BUFFER_SIZE]; - - int image_width = texture->width; - int image_height = texture->height; - - int fdx = (int)(texture->matrix.a * FIXED_SCALE); - int fdy = (int)(texture->matrix.b * FIXED_SCALE); - - int count = span_buffer->spans.size; - const plutovg_span_t* spans = span_buffer->spans.data; - while(count--) { - uint32_t* target = (uint32_t*)(surface->data + spans->y * surface->stride) + spans->x; - - const float cx = spans->x + 0.5f; - const float cy = spans->y + 0.5f; - - int fx = (int)((texture->matrix.c * cy + texture->matrix.a * cx + texture->matrix.e) * FIXED_SCALE); - int fy = (int)((texture->matrix.d * cy + texture->matrix.b * cx + texture->matrix.f) * FIXED_SCALE); - - fx -= HALF_POINT; - fy -= HALF_POINT; - - const int coverage = (spans->coverage * texture->const_alpha) >> 8; - int length = spans->len; - while(length) { - int l = plutovg_min(length, BUFFER_SIZE); - const uint32_t* end = buffer + l; - uint32_t* b = buffer; - while (b < end) { - int x1 = (fx >> 16) % image_width; - int y1 = (fy >> 16) % image_height; - - if(x1 < 0) x1 += image_width; - if(y1 < 0) y1 += image_height; - - int x2 = (x1 + 1) % image_width; - int y2 = (y1 + 1) % image_height; - - const uint32_t* s1 = (const uint32_t*)(texture->data + y1 * texture->stride); - const uint32_t* s2 = (const uint32_t*)(texture->data + y2 * texture->stride); - - uint32_t tl = s1[x1]; - uint32_t tr = s1[x2]; - uint32_t bl = s2[x1]; - uint32_t br = s2[x2]; - - int distx = (fx & 0x0000ffff) >> 8; - int disty = (fy & 0x0000ffff) >> 8; - *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty); - - fx += fdx; - fy += fdy; - ++b; - } - - func(target, l, buffer, coverage); - target += l; - length -= l; - } - - ++spans; - } -} - -static void plutovg_blend_color(plutovg_canvas_t* canvas, const plutovg_color_t* color, const plutovg_span_buffer_t* span_buffer) -{ - plutovg_state_t* state = canvas->state; - uint32_t solid = premultiply_color_with_opacity(color, state->opacity); - uint32_t alpha = plutovg_alpha(solid); - - if(alpha == 255 && state->op == PLUTOVG_OPERATOR_SRC_OVER) { - blend_solid(canvas->surface, PLUTOVG_OPERATOR_SRC, solid, span_buffer); - } else { - blend_solid(canvas->surface, state->op, solid, span_buffer); - } -} - -static void plutovg_blend_gradient(plutovg_canvas_t* canvas, const plutovg_gradient_paint_t* gradient, const plutovg_span_buffer_t* span_buffer) -{ - if(gradient->nstops == 0) - return; - plutovg_state_t* state = canvas->state; - gradient_data_t data; - data.spread = gradient->spread; - data.matrix = gradient->matrix; - plutovg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix); - if(!plutovg_matrix_invert(&data.matrix, &data.matrix)) - return; - int i, pos = 0, nstops = gradient->nstops; - const plutovg_gradient_stop_t *curr, *next, *start, *last; - uint32_t curr_color, next_color, last_color; - uint32_t dist, idist; - float delta, t, incr, fpos; - float opacity = state->opacity; - - start = gradient->stops; - curr = start; - curr_color = premultiply_color_with_opacity(&curr->color, opacity); - - data.colortable[pos++] = curr_color; - incr = 1.0f / COLOR_TABLE_SIZE; - fpos = 1.5f * incr; - - while(fpos <= curr->offset) { - data.colortable[pos] = data.colortable[pos - 1]; - ++pos; - fpos += incr; - } - - for(i = 0; i < nstops - 1; i++) { - curr = (start + i); - next = (start + i + 1); - if(curr->offset == next->offset) - continue; - delta = 1.f / (next->offset - curr->offset); - next_color = premultiply_color_with_opacity(&next->color, opacity); - while(fpos < next->offset && pos < COLOR_TABLE_SIZE) { - t = (fpos - curr->offset) * delta; - dist = (uint32_t)(255 * t); - idist = 255 - dist; - data.colortable[pos] = INTERPOLATE_PIXEL_255(curr_color, idist, next_color, dist); - ++pos; - fpos += incr; - } - - curr_color = next_color; - } - - last = start + nstops - 1; - last_color = premultiply_color_with_opacity(&last->color, opacity); - for(; pos < COLOR_TABLE_SIZE; ++pos) { - data.colortable[pos] = last_color; - } - - if(gradient->type == PLUTOVG_GRADIENT_TYPE_LINEAR) { - data.values.linear.x1 = gradient->values[0]; - data.values.linear.y1 = gradient->values[1]; - data.values.linear.x2 = gradient->values[2]; - data.values.linear.y2 = gradient->values[3]; - blend_linear_gradient(canvas->surface, state->op, &data, span_buffer); - } else { - data.values.radial.cx = gradient->values[0]; - data.values.radial.cy = gradient->values[1]; - data.values.radial.cr = gradient->values[2]; - data.values.radial.fx = gradient->values[3]; - data.values.radial.fy = gradient->values[4]; - data.values.radial.fr = gradient->values[5]; - blend_radial_gradient(canvas->surface, state->op, &data, span_buffer); - } -} - -static void plutovg_blend_texture(plutovg_canvas_t* canvas, const plutovg_texture_paint_t* texture, const plutovg_span_buffer_t* span_buffer) -{ - if(texture->surface == NULL) - return; - plutovg_state_t* state = canvas->state; - texture_data_t data; - data.matrix = texture->matrix; - data.data = texture->surface->data; - data.width = texture->surface->width; - data.height = texture->surface->height; - data.stride = texture->surface->stride; - data.const_alpha = lroundf(state->opacity * texture->opacity * 256); - - plutovg_matrix_multiply(&data.matrix, &data.matrix, &state->matrix); - if(!plutovg_matrix_invert(&data.matrix, &data.matrix)) - return; - const plutovg_matrix_t* matrix = &data.matrix; - if(matrix->a == 1 && matrix->b == 0 && matrix->c == 0 && matrix->d == 1) { - if(texture->type == PLUTOVG_TEXTURE_TYPE_PLAIN) { - blend_untransformed_argb(canvas->surface, state->op, &data, span_buffer); - } else { - blend_untransformed_tiled_argb(canvas->surface, state->op, &data, span_buffer); - } - } else { - if(texture->type == PLUTOVG_TEXTURE_TYPE_PLAIN) { - blend_transformed_argb(canvas->surface, state->op, &data, span_buffer); - } else if(fabsf(matrix->b) > 1e-6f || fabsf(matrix->c) > 1e-6f) { - blend_transformed_bilinear_tiled_argb(canvas->surface, state->op, &data, span_buffer); - } else { - blend_transformed_tiled_argb(canvas->surface, state->op, &data, span_buffer); - } - } -} - -void plutovg_blend(plutovg_canvas_t* canvas, const plutovg_span_buffer_t* span_buffer) -{ - if(span_buffer->spans.size == 0) - return; - if(canvas->state->paint == NULL) { - plutovg_blend_color(canvas, &canvas->state->color, span_buffer); - return; - } - - plutovg_paint_t* paint = canvas->state->paint; - if(paint->type == PLUTOVG_PAINT_TYPE_COLOR) { - plutovg_solid_paint_t* solid = (plutovg_solid_paint_t*)(paint); - plutovg_blend_color(canvas, &solid->color, span_buffer); - } else if(paint->type == PLUTOVG_PAINT_TYPE_GRADIENT) { - plutovg_gradient_paint_t* gradient = (plutovg_gradient_paint_t*)(paint); - plutovg_blend_gradient(canvas, gradient, span_buffer); - } else { - plutovg_texture_paint_t* texture = (plutovg_texture_paint_t*)(paint); - plutovg_blend_texture(canvas, texture, span_buffer); - } -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-canvas.c b/vendor/lunasvg/plutovg/source/plutovg-canvas.c deleted file mode 100644 index 908ac4e..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-canvas.c +++ /dev/null @@ -1,759 +0,0 @@ -#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; -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-font.c b/vendor/lunasvg/plutovg/source/plutovg-font.c deleted file mode 100644 index 0ad89b1..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-font.c +++ /dev/null @@ -1,1065 +0,0 @@ -#include "plutovg-private.h" -#include "plutovg-utils.h" - -#include -#include - -#define STBTT_STATIC -#define STB_TRUETYPE_IMPLEMENTATION -#include "plutovg-stb-truetype.h" - -static int plutovg_text_iterator_length(const void* data, plutovg_text_encoding_t encoding) -{ - int length = 0; - switch(encoding) { - case PLUTOVG_TEXT_ENCODING_LATIN1: - case PLUTOVG_TEXT_ENCODING_UTF8: { - const uint8_t* text = data; - while(*text++) - length++; - break; - } case PLUTOVG_TEXT_ENCODING_UTF16: { - const uint16_t* text = data; - while(*text++) - length++; - break; - } case PLUTOVG_TEXT_ENCODING_UTF32: { - const uint32_t* text = data; - while(*text++) - length++; - break; - } default: - assert(false); - } - - return length; -} - -void plutovg_text_iterator_init(plutovg_text_iterator_t* it, const void* text, int length, plutovg_text_encoding_t encoding) -{ - if(length == -1) - length = plutovg_text_iterator_length(text, encoding); - it->text = text; - it->length = length; - it->encoding = encoding; - it->index = 0; -} - -bool plutovg_text_iterator_has_next(const plutovg_text_iterator_t* it) -{ - return it->index < it->length; -} - -plutovg_codepoint_t plutovg_text_iterator_next(plutovg_text_iterator_t* it) -{ - plutovg_codepoint_t codepoint = 0; - switch(it->encoding) { - case PLUTOVG_TEXT_ENCODING_LATIN1: { - const uint8_t* text = it->text; - codepoint = text[it->index++]; - break; - } case PLUTOVG_TEXT_ENCODING_UTF8: { - static const uint8_t trailing[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 - }; - - static const uint32_t offsets[6] = { - 0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080, 0x82082080 - }; - - const uint8_t* text = it->text; - uint8_t trailing_offset = trailing[text[it->index]]; - uint32_t offset_value = offsets[trailing_offset]; - while(trailing_offset > 0 && it->index < it->length - 1) { - codepoint += text[it->index++]; - codepoint <<= 6; - trailing_offset--; - } - - codepoint += text[it->index++]; - codepoint -= offset_value; - break; - } case PLUTOVG_TEXT_ENCODING_UTF16: { - const uint16_t* text = it->text; - codepoint = text[it->index++]; - if(((codepoint) & 0xfffffc00) == 0xd800) { - if(it->index < it->length && (((codepoint) & 0xfffffc00) == 0xdc00)) { - uint16_t trail = text[it->index++]; - codepoint = (codepoint << 10) + trail - ((0xD800u << 10) - 0x10000u + 0xDC00u); - } - } - - break; - } case PLUTOVG_TEXT_ENCODING_UTF32: { - const uint32_t* text = it->text; - codepoint = text[it->index++]; - break; - } default: - assert(false); - } - - return codepoint; -} - -#if defined(_WIN32) - -#include - -typedef CRITICAL_SECTION plutovg_mutex_t; - -#define plutovg_mutex_init(mutex) InitializeCriticalSection(mutex) -#define plutovg_mutex_lock(mutex) EnterCriticalSection(mutex) -#define plutovg_mutex_unlock(mutex) LeaveCriticalSection(mutex) -#define plutovg_mutex_destroy(mutex) DeleteCriticalSection(mutex) - -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && defined(HAVE_THREADS_H) && !defined(__STDC_NO_THREADS__) - -#include - -typedef mtx_t plutovg_mutex_t; - -#define plutovg_mutex_init(mutex) mtx_init(mutex, mtx_plain | mtx_recursive) -#define plutovg_mutex_lock(mutex) mtx_lock(mutex) -#define plutovg_mutex_unlock(mutex) mtx_unlock(mutex) -#define plutovg_mutex_destroy(mutex) mtx_destroy(mutex) - -#else - -typedef int plutovg_mutex_t; - -#define plutovg_mutex_init(mutex) ((void)(mutex)) -#define plutovg_mutex_lock(mutex) ((void)(mutex)) -#define plutovg_mutex_unlock(mutex) ((void)(mutex)) -#define plutovg_mutex_destroy(mutex) ((void)(mutex)) - -#endif - -typedef struct plutovg_glyph { - plutovg_codepoint_t codepoint; - stbtt_vertex* vertices; - int nvertices; - int index; - int advance_width; - int left_side_bearing; - int x1; - int y1; - int x2; - int y2; - struct plutovg_glyph* next; -} plutovg_glyph_t; - -typedef struct { - plutovg_glyph_t** glyphs; - size_t size; - size_t capacity; -} plutovg_glyph_cache_t; - -struct plutovg_font_face { - plutovg_ref_count_t ref_count; - int ascent; - int descent; - int line_gap; - int x1; - int y1; - int x2; - int y2; - stbtt_fontinfo info; - plutovg_mutex_t mutex; - plutovg_glyph_cache_t cache; - plutovg_destroy_func_t destroy_func; - void* closure; -}; - -static void plutovg_glyph_cache_init(plutovg_glyph_cache_t* cache) -{ - cache->glyphs = NULL; - cache->size = 0; - cache->capacity = 0; -} - -static void plutovg_glyph_cache_finish(plutovg_glyph_cache_t* cache, plutovg_font_face_t* face) -{ - plutovg_mutex_lock(&face->mutex); - - if(cache->glyphs) { - for(size_t i = 0; i < cache->capacity; ++i) { - plutovg_glyph_t* glyph = cache->glyphs[i]; - while(glyph) { - plutovg_glyph_t* next = glyph->next; - stbtt_FreeShape(&face->info, glyph->vertices); - free(glyph); - glyph = next; - } - } - - free(cache->glyphs); - cache->glyphs = NULL; - cache->capacity = 0; - cache->size = 0; - } - - plutovg_mutex_unlock(&face->mutex); -} - -#define GLYPH_CACHE_INIT_CAPACITY 128 - -static plutovg_glyph_t* plutovg_glyph_cache_get(plutovg_glyph_cache_t* cache, plutovg_font_face_t* face, plutovg_codepoint_t codepoint) -{ - plutovg_mutex_lock(&face->mutex); - - if(cache->glyphs == NULL) { - assert(cache->size == 0); - cache->glyphs = calloc(GLYPH_CACHE_INIT_CAPACITY, sizeof(plutovg_glyph_t*)); - cache->capacity = GLYPH_CACHE_INIT_CAPACITY; - } - - size_t index = codepoint & (cache->capacity - 1); - plutovg_glyph_t* glyph = cache->glyphs[index]; - while(glyph && glyph->codepoint != codepoint) { - glyph = glyph->next; - } - - if(glyph == NULL) { - glyph = malloc(sizeof(plutovg_glyph_t)); - glyph->codepoint = codepoint; - glyph->index = stbtt_FindGlyphIndex(&face->info, codepoint); - glyph->nvertices = stbtt_GetGlyphShape(&face->info, glyph->index, &glyph->vertices); - stbtt_GetGlyphHMetrics(&face->info, glyph->index, &glyph->advance_width, &glyph->left_side_bearing); - if(!stbtt_GetGlyphBox(&face->info, glyph->index, &glyph->x1, &glyph->y1, &glyph->x2, &glyph->y2)) { - glyph->x1 = glyph->y1 = glyph->x2 = glyph->y2 = 0; - } - - glyph->next = cache->glyphs[index]; - cache->glyphs[index] = glyph; - cache->size += 1; - - if(cache->size > (cache->capacity * 3 / 4)) { - size_t newcapacity = cache->capacity << 1; - plutovg_glyph_t** newglyphs = calloc(newcapacity, sizeof(plutovg_glyph_t*)); - - for(size_t i = 0; i < cache->capacity; ++i) { - plutovg_glyph_t* entry = cache->glyphs[i]; - while(entry) { - plutovg_glyph_t* next = entry->next; - size_t newindex = entry->codepoint & (newcapacity - 1); - entry->next = newglyphs[newindex]; - newglyphs[newindex] = entry; - entry = next; - } - } - - free(cache->glyphs); - cache->glyphs = newglyphs; - cache->capacity = newcapacity; - } - } - - plutovg_mutex_unlock(&face->mutex); - return glyph; -} - -plutovg_font_face_t* plutovg_font_face_load_from_file(const char* filename, int ttcindex) -{ - FILE* fp = fopen(filename, "rb"); - if(fp == NULL) { - return NULL; - } - - fseek(fp, 0, SEEK_END); - long length = ftell(fp); - if(length == -1L) { - fclose(fp); - return NULL; - } - - void* data = malloc(length); - if(data == NULL) { - fclose(fp); - return NULL; - } - - fseek(fp, 0, SEEK_SET); - size_t nread = fread(data, 1, length, fp); - fclose(fp); - - if(nread != length) { - free(data); - return NULL; - } - - return plutovg_font_face_load_from_data(data, length, ttcindex, free, data); -} - -plutovg_font_face_t* plutovg_font_face_load_from_data(const void* data, unsigned int length, int ttcindex, plutovg_destroy_func_t destroy_func, void* closure) -{ - stbtt_fontinfo info; - int offset = stbtt_GetFontOffsetForIndex(data, ttcindex); - if(offset == -1 || !stbtt_InitFont(&info, data, offset)) { - if(destroy_func) - destroy_func(closure); - return NULL; - } - - plutovg_font_face_t* face = malloc(sizeof(plutovg_font_face_t)); - plutovg_init_reference(face); - face->info = info; - stbtt_GetFontVMetrics(&face->info, &face->ascent, &face->descent, &face->line_gap); - stbtt_GetFontBoundingBox(&face->info, &face->x1, &face->y1, &face->x2, &face->y2); - plutovg_mutex_init(&face->mutex); - plutovg_glyph_cache_init(&face->cache); - face->destroy_func = destroy_func; - face->closure = closure; - return face; -} - -plutovg_font_face_t* plutovg_font_face_reference(plutovg_font_face_t* face) -{ - plutovg_increment_reference(face); - return face; -} - -void plutovg_font_face_destroy(plutovg_font_face_t* face) -{ - if(plutovg_destroy_reference(face)) { - plutovg_glyph_cache_finish(&face->cache, face); - plutovg_mutex_destroy(&face->mutex); - if(face->destroy_func) - face->destroy_func(face->closure); - free(face); - } -} - -int plutovg_font_face_get_reference_count(const plutovg_font_face_t* face) -{ - return plutovg_get_reference_count(face); -} - -static float plutovg_font_face_get_scale(const plutovg_font_face_t* face, float size) -{ - return stbtt_ScaleForMappingEmToPixels(&face->info, size); -} - -void plutovg_font_face_get_metrics(const plutovg_font_face_t* face, float size, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents) -{ - float scale = plutovg_font_face_get_scale(face, size); - if(ascent) *ascent = face->ascent * scale; - if(descent) *descent = face->descent * scale; - if(line_gap) *line_gap = face->line_gap * scale; - if(extents) { - extents->x = face->x1 * scale; - extents->y = face->y2 * -scale; - extents->w = (face->x2 - face->x1) * scale; - extents->h = (face->y1 - face->y2) * -scale; - } -} - -static plutovg_glyph_t* plutovg_font_face_get_glyph(plutovg_font_face_t* face, plutovg_codepoint_t codepoint) -{ - return plutovg_glyph_cache_get(&face->cache, face, codepoint); -} - -void plutovg_font_face_get_glyph_metrics(plutovg_font_face_t* face, float size, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents) -{ - float scale = plutovg_font_face_get_scale(face, size); - plutovg_glyph_t* glyph = plutovg_font_face_get_glyph(face, codepoint); - if(advance_width) *advance_width = glyph->advance_width * scale; - if(left_side_bearing) *left_side_bearing = glyph->left_side_bearing * scale; - if(extents) { - extents->x = glyph->x1 * scale; - extents->y = glyph->y2 * -scale; - extents->w = (glyph->x2 - glyph->x1) * scale; - extents->h = (glyph->y1 - glyph->y2) * -scale; - } -} - -static void glyph_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: - assert(false); - } -} - -float plutovg_font_face_get_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_t* path) -{ - return plutovg_font_face_traverse_glyph_path(face, size, x, y, codepoint, glyph_traverse_func, path); -} - -float plutovg_font_face_traverse_glyph_path(plutovg_font_face_t* face, float size, float x, float y, plutovg_codepoint_t codepoint, plutovg_path_traverse_func_t traverse_func, void* closure) -{ - float scale = plutovg_font_face_get_scale(face, size); - plutovg_matrix_t matrix; - plutovg_matrix_init_translate(&matrix, x, y); - plutovg_matrix_scale(&matrix, scale, -scale); - - plutovg_point_t points[3]; - plutovg_point_t current_point = {0, 0}; - plutovg_glyph_t* glyph = plutovg_font_face_get_glyph(face, codepoint); - for(int i = 0; i < glyph->nvertices; i++) { - switch(glyph->vertices[i].type) { - case STBTT_vmove: - points[0].x = glyph->vertices[i].x; - points[0].y = glyph->vertices[i].y; - current_point = points[0]; - plutovg_matrix_map_points(&matrix, points, points, 1); - traverse_func(closure, PLUTOVG_PATH_COMMAND_MOVE_TO, points, 1); - break; - case STBTT_vline: - points[0].x = glyph->vertices[i].x; - points[0].y = glyph->vertices[i].y; - current_point = points[0]; - plutovg_matrix_map_points(&matrix, points, points, 1); - traverse_func(closure, PLUTOVG_PATH_COMMAND_LINE_TO, points, 1); - break; - case STBTT_vcurve: - points[0].x = 2.f / 3.f * glyph->vertices[i].cx + 1.f / 3.f * current_point.x; - points[0].y = 2.f / 3.f * glyph->vertices[i].cy + 1.f / 3.f * current_point.y; - points[1].x = 2.f / 3.f * glyph->vertices[i].cx + 1.f / 3.f * glyph->vertices[i].x; - points[1].y = 2.f / 3.f * glyph->vertices[i].cy + 1.f / 3.f * glyph->vertices[i].y; - points[2].x = glyph->vertices[i].x; - points[2].y = glyph->vertices[i].y; - current_point = points[2]; - plutovg_matrix_map_points(&matrix, points, points, 3); - traverse_func(closure, PLUTOVG_PATH_COMMAND_CUBIC_TO, points, 3); - break; - case STBTT_vcubic: - points[0].x = glyph->vertices[i].cx; - points[0].y = glyph->vertices[i].cy; - points[1].x = glyph->vertices[i].cx1; - points[1].y = glyph->vertices[i].cy1; - points[2].x = glyph->vertices[i].x; - points[2].y = glyph->vertices[i].y; - current_point = points[2]; - plutovg_matrix_map_points(&matrix, points, points, 3); - traverse_func(closure, PLUTOVG_PATH_COMMAND_CUBIC_TO, points, 3); - break; - default: - assert(false); - } - } - - return glyph->advance_width * scale; -} - -float plutovg_font_face_text_extents(plutovg_font_face_t* face, float size, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents) -{ - plutovg_text_iterator_t it; - plutovg_text_iterator_init(&it, text, length, encoding); - plutovg_rect_t* text_extents = NULL; - float total_advance_width = 0.f; - while(plutovg_text_iterator_has_next(&it)) { - plutovg_codepoint_t codepoint = plutovg_text_iterator_next(&it); - - float advance_width; - if(extents == NULL) { - plutovg_font_face_get_glyph_metrics(face, size, codepoint, &advance_width, NULL, NULL); - total_advance_width += advance_width; - continue; - } - - plutovg_rect_t glyph_extents; - plutovg_font_face_get_glyph_metrics(face, size, codepoint, &advance_width, NULL, &glyph_extents); - - glyph_extents.x += total_advance_width; - total_advance_width += advance_width; - if(text_extents == NULL) { - text_extents = extents; - *text_extents = glyph_extents; - continue; - } - - float x1 = plutovg_min(text_extents->x, glyph_extents.x); - float y1 = plutovg_min(text_extents->y, glyph_extents.y); - float x2 = plutovg_max(text_extents->x + text_extents->w, glyph_extents.x + glyph_extents.w); - float y2 = plutovg_max(text_extents->y + text_extents->h, glyph_extents.y + glyph_extents.h); - - text_extents->x = x1; - text_extents->y = y1; - text_extents->w = x2 - x1; - text_extents->h = y2 - y1; - } - - if(extents && !text_extents) { - extents->x = 0; - extents->y = 0; - extents->w = 0; - extents->h = 0; - } - - return total_advance_width; -} - -typedef struct plutovg_font_face_entry { - plutovg_font_face_t* face; - char* family; - char* filename; - int ttcindex; - bool bold; - bool italic; - struct plutovg_font_face_entry* next; -} plutovg_font_face_entry_t; - -struct plutovg_font_face_cache { - plutovg_ref_count_t ref_count; - plutovg_mutex_t mutex; - plutovg_font_face_entry_t** entries; - int size; - int capacity; - bool is_sorted; -}; - -plutovg_font_face_cache_t* plutovg_font_face_cache_create(void) -{ - plutovg_font_face_cache_t* cache = malloc(sizeof(plutovg_font_face_cache_t)); - plutovg_init_reference(cache); - plutovg_mutex_init(&cache->mutex); - cache->entries = NULL; - cache->size = 0; - cache->capacity = 0; - cache->is_sorted = false; - return cache; -} - -plutovg_font_face_cache_t* plutovg_font_face_cache_reference(plutovg_font_face_cache_t* cache) -{ - plutovg_increment_reference(cache); - return cache; -} - -void plutovg_font_face_cache_destroy(plutovg_font_face_cache_t* cache) -{ - if(plutovg_destroy_reference(cache)) { - plutovg_font_face_cache_reset(cache); - plutovg_mutex_destroy(&cache->mutex); - free(cache); - } -} - -int plutovg_font_face_cache_reference_count(const plutovg_font_face_cache_t* cache) -{ - return plutovg_get_reference_count(cache); -} - -void plutovg_font_face_cache_reset(plutovg_font_face_cache_t* cache) -{ - plutovg_mutex_lock(&cache->mutex); - - for(int i = 0; i < cache->size; ++i) { - plutovg_font_face_entry_t* entry = cache->entries[i]; - do { - plutovg_font_face_entry_t* next = entry->next; - plutovg_font_face_destroy(entry->face); - free(entry); - entry = next; - } while(entry); - } - - free(cache->entries); - cache->entries = NULL; - cache->size = 0; - cache->capacity = 0; - cache->is_sorted = false; - - plutovg_mutex_unlock(&cache->mutex); -} - -static void plutovg_font_face_cache_add_entry(plutovg_font_face_cache_t* cache, plutovg_font_face_entry_t* entry) -{ - plutovg_mutex_lock(&cache->mutex); - - for(int i = 0; i < cache->size; ++i) { - if(strcmp(entry->family, cache->entries[i]->family) == 0) { - entry->next = cache->entries[i]; - cache->entries[i] = entry; - goto unlock; - } - } - - if(cache->size >= cache->capacity) { - cache->capacity = cache->capacity == 0 ? 8 : cache->capacity << 2; - cache->entries = realloc(cache->entries, cache->capacity * sizeof(plutovg_font_face_entry_t*)); - } - - entry->next = NULL; - cache->entries[cache->size++] = entry; - cache->is_sorted = false; - -unlock: - plutovg_mutex_unlock(&cache->mutex); -} - -void plutovg_font_face_cache_add(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic, plutovg_font_face_t* face) -{ - if(family == NULL) family = ""; - size_t family_length = strlen(family) + 1; - - plutovg_font_face_entry_t* entry = malloc(family_length + sizeof(plutovg_font_face_entry_t)); - entry->face = plutovg_font_face_reference(face); - entry->family = (char*)(entry + 1); - memcpy(entry->family, family, family_length); - - entry->filename = NULL; - entry->ttcindex = 0; - entry->bold = bold; - entry->italic = italic; - - plutovg_font_face_cache_add_entry(cache, entry); -} - -bool plutovg_font_face_cache_add_file(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic, const char* filename, int ttcindex) -{ - plutovg_font_face_t* face = plutovg_font_face_load_from_file(filename, ttcindex); - if(face == NULL) - return false; - plutovg_font_face_cache_add(cache, family, bold, italic, face); - plutovg_font_face_destroy(face); - return true; -} - -static plutovg_font_face_entry_t* plutovg_font_face_entry_select(plutovg_font_face_entry_t* a, plutovg_font_face_entry_t* b, bool bold, bool italic) -{ - int a_score = (bold == a->bold) + (italic == a->italic); - int b_score = (bold == b->bold) + (italic == b->italic); - return a_score > b_score ? a : b; -} - -static int plutovg_font_face_entry_compare(const void* a, const void* b) -{ - const plutovg_font_face_entry_t* a_entry = *(const plutovg_font_face_entry_t**)a; - const plutovg_font_face_entry_t* b_entry = *(const plutovg_font_face_entry_t**)b; - return strcmp(a_entry->family, b_entry->family); -} - -plutovg_font_face_t* plutovg_font_face_cache_get(plutovg_font_face_cache_t* cache, const char* family, bool bold, bool italic) -{ - plutovg_mutex_lock(&cache->mutex); - - if(!cache->is_sorted && cache->size > 0) { - qsort(cache->entries, cache->size, sizeof(cache->entries[0]), plutovg_font_face_entry_compare); - cache->is_sorted = true; - } - - plutovg_font_face_entry_t entry_key; - entry_key.family = (char*)(family); - - plutovg_font_face_entry_t* entry_key_ptr = &entry_key; - plutovg_font_face_entry_t** entry_result = bsearch( - &entry_key_ptr, - cache->entries, - cache->size, - sizeof(cache->entries[0]), - plutovg_font_face_entry_compare - ); - - plutovg_font_face_t* face = NULL; - if(entry_result) { - plutovg_font_face_entry_t* selected = *entry_result; - plutovg_font_face_entry_t* entry = selected->next; - while(entry) { - selected = plutovg_font_face_entry_select(entry, selected, bold, italic); - entry = entry->next; - } - - if(selected->filename && selected->face == NULL) - selected->face = plutovg_font_face_load_from_file(selected->filename, selected->ttcindex); - face = selected->face; - } - - plutovg_mutex_unlock(&cache->mutex); - return face; -} - -#ifndef PLUTOVG_DISABLE_FONT_FACE_CACHE_LOAD - -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include - -#ifdef __linux__ -#include -#else -#include -#endif - -#include -#include -#endif - -#ifdef _WIN32 - -static void* plutovg_mmap(const char* filename, long* length) -{ - HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if(file == INVALID_HANDLE_VALUE) - return NULL; - DWORD size = GetFileSize(file, NULL); - if(size == INVALID_FILE_SIZE) { - CloseHandle(file); - return NULL; - } - - HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL); - if(mapping == NULL) { - CloseHandle(file); - return NULL; - } - - void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); - CloseHandle(mapping); - CloseHandle(file); - - if(data == NULL) - return NULL; - *length = size; - return data; -} - -static void plutovg_unmap(void* data, long length) -{ - UnmapViewOfFile(data); -} - -#else - -static void* plutovg_mmap(const char* filename, long* length) -{ - int fd = open(filename, O_RDONLY); - if(fd < 0) - return NULL; - struct stat st; - if(fstat(fd, &st) < 0) { - close(fd); - return NULL; - } - - if(st.st_size == 0) { - close(fd); - return NULL; - } - - void* data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - - if(data == MAP_FAILED) - return NULL; - *length = st.st_size; - return data; -} - -static void plutovg_unmap(void* data, long length) -{ - munmap(data, length); -} - -#endif // _WIN32 - -int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const char* filename) -{ - long length; - stbtt_uint8* data = plutovg_mmap(filename, &length); - if(data == NULL) { - return 0; - } - - int num_faces = 0; - - int num_fonts = stbtt_GetNumberOfFonts(data); - for(int index = 0; index < num_fonts; ++index) { - int offset = stbtt_GetFontOffsetForIndex(data, index); - if(offset == -1 || !stbtt__isfont(data + offset)) { - continue; - } - - stbtt_uint32 nm = stbtt__find_table(data, offset, "name"); - stbtt_uint16 nm_count = ttUSHORT(data + nm + 2); - - const stbtt_uint8* unicode_family_name = NULL; - const stbtt_uint8* roman_family_name = NULL; - - size_t family_length = 0; - for(stbtt_int32 i = 0; i < nm_count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_uint16 nm_id = ttUSHORT(data + loc + 6); - if(nm_id != 1) { - continue; - } - - stbtt_uint16 platform = ttUSHORT(data + loc + 0); - stbtt_uint16 encoding = ttUSHORT(data + loc + 2); - - const stbtt_uint8* family_name = data + nm + ttUSHORT(data + nm + 4) + ttUSHORT(data + loc + 10); - if(platform == 1 && encoding == 0) { - family_length = ttUSHORT(data + loc + 8); - roman_family_name = family_name; - continue; - } - - if(platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - family_length = ttUSHORT(data + loc + 8); - unicode_family_name = family_name; - break; - } - } - - if(unicode_family_name == NULL && roman_family_name == NULL) - continue; - size_t filename_length = strlen(filename) + 1; - size_t max_family_length = (unicode_family_name ? 3 * (family_length / 2) : family_length * 3) + 1; - - plutovg_font_face_entry_t* entry = malloc(max_family_length + filename_length + sizeof(plutovg_font_face_entry_t)); - entry->family = (char*)(entry + 1); - entry->filename = entry->family + max_family_length; - memcpy(entry->filename, filename, filename_length); - - size_t family_index = 0; - if(unicode_family_name) { - const stbtt_uint8* family_name = unicode_family_name; - while(family_length) { - stbtt_uint16 ch = family_name[0] * 256 + family_name[1]; - if(ch < 0x80) { - entry->family[family_index++] = ch; - } else if(ch < 0x800) { - entry->family[family_index++] = (0xc0 + (ch >> 6)); - entry->family[family_index++] = (0x80 + (ch & 0x3f)); - } else if(ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint16 ch2 = family_name[2] * 256 + family_name[3]; - stbtt_uint32 c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - - entry->family[family_index++] = (0xf0 + (c >> 18)); - entry->family[family_index++] = (0x80 + ((c >> 12) & 0x3f)); - entry->family[family_index++] = (0x80 + ((c >> 6) & 0x3f)); - entry->family[family_index++] = (0x80 + ((c) & 0x3f)); - - family_name += 2; - family_length -= 2; - } else { - entry->family[family_index++] = (0xe0 + (ch >> 12)); - entry->family[family_index++] = (0x80 + ((ch >> 6) & 0x3f)); - entry->family[family_index++] = (0x80 + ((ch) & 0x3f)); - } - - family_name += 2; - family_length -= 2; - } - - entry->family[family_index] = '\0'; - } else { - static const stbtt_uint16 MAC_ROMAN_TABLE[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x2318, 0x21E7, 0x2325, 0x2303, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, - 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, - 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, - 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, - 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, - 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, - 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, - 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, - 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, - 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, - 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, - 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, - 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, - 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, - 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, - 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, - 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7, - }; - - const stbtt_uint8* family_name = roman_family_name; - while(family_length) { - stbtt_uint16 ch = MAC_ROMAN_TABLE[family_name[0]]; - if(ch < 0x80) { - entry->family[family_index++] = ch; - } else if(ch < 0x800) { - entry->family[family_index++] = (0xc0 + (ch >> 6)); - entry->family[family_index++] = (0x80 + (ch & 0x3f)); - } else { - entry->family[family_index++] = (0xe0 + (ch >> 12)); - entry->family[family_index++] = (0x80 + ((ch >> 6) & 0x3f)); - entry->family[family_index++] = (0x80 + ((ch) & 0x3f)); - } - - family_name += 1; - family_length -= 1; - } - - entry->family[family_index] = '\0'; - } - - entry->face = NULL; - entry->bold = false; - entry->italic = false; - entry->ttcindex = index; - - stbtt_uint32 hd = stbtt__find_table(data, offset, "head"); - stbtt_uint16 style = ttUSHORT(data + hd + 44); - if(style & 0x1) - entry->bold = true; - if(style & 0x2) { - entry->italic = true; - } - - plutovg_font_face_cache_add_entry(cache, entry); - num_faces++; - } - - plutovg_unmap(data, length); - return num_faces; -} - -static bool plutovg_font_face_supports_file(const char* filename) -{ - const char* extension = strrchr(filename, '.'); - if(extension) { - char ext[5]; - size_t length = strlen(extension); - if(length == 4) { - for(size_t i = 0; i < length; ++i) - ext[i] = tolower(extension[i]); - ext[length] = '\0'; - return strcmp(ext, ".ttf") == 0 - || strcmp(ext, ".otf") == 0 - || strcmp(ext, ".ttc") == 0 - || strcmp(ext, ".otc") == 0; - } - } - - return false; -} - -#ifdef _WIN32 - -int plutovg_font_face_cache_load_dir(plutovg_font_face_cache_t* cache, const char* dirname) -{ - char search_path[MAX_PATH]; - snprintf(search_path, sizeof(search_path), "%s\\*", dirname); - - WIN32_FIND_DATAA find_data; - HANDLE handle = FindFirstFileA(search_path, &find_data); - if(handle == INVALID_HANDLE_VALUE) { - return 0; - } - - int num_faces = 0; - - do { - const char* name = find_data.cFileName; - if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) - continue; - char path[MAX_PATH * 2]; - snprintf(path, sizeof(path), "%s\\%s", dirname, name); - - if(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - num_faces += plutovg_font_face_cache_load_dir(cache, path); - } else if(plutovg_font_face_supports_file(path)) { - num_faces += plutovg_font_face_cache_load_file(cache, path); - } - } while(FindNextFileA(handle, &find_data)); - - FindClose(handle); - return num_faces; -} - -#else - -int plutovg_font_face_cache_load_dir(plutovg_font_face_cache_t* cache, const char* dirname) -{ - DIR* dir = opendir(dirname); - if(dir == NULL) { - return 0; - } - - int num_faces = 0; - - struct dirent* entry; - while((entry = readdir(dir)) != NULL) { - if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - char path[PATH_MAX]; - snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); - - struct stat st; - if(stat(path, &st) == -1) - continue; - if(S_ISDIR(st.st_mode)) { - num_faces += plutovg_font_face_cache_load_dir(cache, path); - } else if(S_ISREG(st.st_mode) && plutovg_font_face_supports_file(path)) { - num_faces += plutovg_font_face_cache_load_file(cache, path); - } - } - - closedir(dir); - return num_faces; -} - -#endif // _WIN32 - -int plutovg_font_face_cache_load_sys(plutovg_font_face_cache_t* cache) -{ - int num_faces = 0; -#if defined(_WIN32) - num_faces += plutovg_font_face_cache_load_dir(cache, "C:\\Windows\\Fonts"); -#elif defined(__APPLE__) - num_faces += plutovg_font_face_cache_load_dir(cache, "/Library/Fonts"); - num_faces += plutovg_font_face_cache_load_dir(cache, "/System/Library/Fonts"); -#elif defined(__linux__) - num_faces += plutovg_font_face_cache_load_dir(cache, "/usr/share/fonts"); - num_faces += plutovg_font_face_cache_load_dir(cache, "/usr/local/share/fonts"); -#endif - return num_faces; -} - -#else - -int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const char* filename) -{ - return -1; -} - -int plutovg_font_face_cache_load_dir(plutovg_font_face_cache_t* cache, const char* dirname) -{ - return -1; -} - -int plutovg_font_face_cache_load_sys(plutovg_font_face_cache_t* cache) -{ - return -1; -} - -#endif // PLUTOVG_DISABLE_FONT_FACE_CACHE_LOAD diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-math.c b/vendor/lunasvg/plutovg/source/plutovg-ft-math.c deleted file mode 100644 index c4a1af8..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-math.c +++ /dev/null @@ -1,441 +0,0 @@ -/***************************************************************************/ -/* */ -/* 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 -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 */ diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-math.h b/vendor/lunasvg/plutovg/source/plutovg-ft-math.h deleted file mode 100644 index 2d15c5b..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-math.h +++ /dev/null @@ -1,436 +0,0 @@ -/***************************************************************************/ -/* */ -/* 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 ) ) - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_MulFix */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* a :: The first multiplier. */ -/* b :: The second multiplier. Use a 16.16 factor here whenever */ -/* possible (see note below). */ -/* */ -/* */ -/* The result of `(a*b)/0x10000'. */ -/* */ -/* */ -/* 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 ); - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_MulDiv */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* a :: The first multiplier. */ -/* b :: The second multiplier. */ -/* c :: The divisor. */ -/* */ -/* */ -/* 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 ); - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_DivFix */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* a :: The numerator. */ -/* b :: The denominator. Use a 16.16 factor here. */ -/* */ -/* */ -/* The result of `(a*0x10000)/b'. */ -/* */ -PVG_FT_Long -PVG_FT_DivFix( PVG_FT_Long a, - PVG_FT_Long b ); - - - -/*************************************************************************/ -/* */ -/*
*/ -/* 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 */ diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c deleted file mode 100644 index d746b78..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c +++ /dev/null @@ -1,1889 +0,0 @@ -/***************************************************************************/ -/* */ -/* ftgrays.c */ -/* */ -/* A new `perfect' anti-aliasing renderer (body). */ -/* */ -/* Copyright 2000-2003, 2005-2014 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. */ -/* */ -/***************************************************************************/ - -/*************************************************************************/ -/* */ -/* This is a new anti-aliasing scan-converter for FreeType 2. The */ -/* algorithm used here is _very_ different from the one in the standard */ -/* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ -/* coverage of the outline on each pixel cell. */ -/* */ -/* It is based on ideas that I initially found in Raph Levien's */ -/* excellent LibArt graphics library (see http://www.levien.com/libart */ -/* for more information, though the web pages do not tell anything */ -/* about the renderer; you'll have to dive into the source code to */ -/* understand how it works). */ -/* */ -/* Note, however, that this is a _very_ different implementation */ -/* compared to Raph's. Coverage information is stored in a very */ -/* different way, and I don't use sorted vector paths. Also, it doesn't */ -/* use floating point values. */ -/* */ -/* This renderer has the following advantages: */ -/* */ -/* - It doesn't need an intermediate bitmap. Instead, one can supply a */ -/* callback function that will be called by the renderer to draw gray */ -/* spans on any target surface. You can thus do direct composition on */ -/* any kind of bitmap, provided that you give the renderer the right */ -/* callback. */ -/* */ -/* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ -/* each pixel cell. */ -/* */ -/* - It performs a single pass on the outline (the `standard' FT2 */ -/* renderer makes two passes). */ -/* */ -/* - It can easily be modified to render to _any_ number of gray levels */ -/* cheaply. */ -/* */ -/* - For small (< 20) pixel sizes, it is faster than the standard */ -/* renderer. */ -/* */ -/*************************************************************************/ - -#include "plutovg-ft-raster.h" -#include "plutovg-ft-math.h" - -#include - -#define pvg_ft_setjmp setjmp -#define pvg_ft_longjmp longjmp -#define pvg_ft_jmp_buf jmp_buf - -#include - -typedef ptrdiff_t PVG_FT_PtrDist; - -#define ErrRaster_Invalid_Mode -2 -#define ErrRaster_Invalid_Outline -1 -#define ErrRaster_Invalid_Argument -3 -#define ErrRaster_Memory_Overflow -4 -#define ErrRaster_OutOfMemory -6 - -#include -#include - -#define PVG_FT_MINIMUM_POOL_SIZE 8192 - -#define RAS_ARG PWorker worker -#define RAS_ARG_ PWorker worker, - -#define RAS_VAR worker -#define RAS_VAR_ worker, - -#define ras (*worker) - - /* must be at least 6 bits! */ -#define PIXEL_BITS 8 - -#define ONE_PIXEL ( 1L << PIXEL_BITS ) -#define TRUNC( x ) (TCoord)( (x) >> PIXEL_BITS ) -#define FRACT( x ) (TCoord)( (x) & ( ONE_PIXEL - 1 ) ) - -#if PIXEL_BITS >= 6 -#define UPSCALE( x ) ( (x) * ( ONE_PIXEL >> 6 ) ) -#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) -#else -#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) -#define DOWNSCALE( x ) ( (x) * ( 64 >> PIXEL_BITS ) ) -#endif - -/* Compute `dividend / divisor' and return both its quotient and */ -/* remainder, cast to a specific type. This macro also ensures that */ -/* the remainder is always positive. */ -#define PVG_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \ -PVG_FT_BEGIN_STMNT \ - (quotient) = (type)( (dividend) / (divisor) ); \ - (remainder) = (type)( (dividend) % (divisor) ); \ - if ( (remainder) < 0 ) \ - { \ - (quotient)--; \ - (remainder) += (type)(divisor); \ - } \ -PVG_FT_END_STMNT - - /* These macros speed up repetitive divisions by replacing them */ - /* with multiplications and right shifts. */ -#define PVG_FT_UDIVPREP( b ) \ - long b ## _r = (long)( ULONG_MAX >> PIXEL_BITS ) / ( b ) -#define PVG_FT_UDIV( a, b ) \ - ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ - ( sizeof( long ) * CHAR_BIT - PIXEL_BITS ) ) - - - /*************************************************************************/ - /* */ - /* TYPE DEFINITIONS */ - /* */ - - /* don't change the following types to PVG_FT_Int or PVG_FT_Pos, since we might */ - /* need to define them to "float" or "double" when experimenting with */ - /* new algorithms */ - - typedef long TCoord; /* integer scanline/pixel coordinate */ - typedef long TPos; /* sub-pixel coordinate */ - typedef long TArea ; /* cell areas, coordinate products */ - - /* maximal number of gray spans in a call to the span callback */ -#define PVG_FT_MAX_GRAY_SPANS 256 - - - typedef struct TCell_* PCell; - - typedef struct TCell_ - { - int x; - int cover; - TArea area; - PCell next; - - } TCell; - - - typedef struct TWorker_ - { - TCoord ex, ey; - TPos min_ex, max_ex; - TPos min_ey, max_ey; - TPos count_ex, count_ey; - - TArea area; - int cover; - int invalid; - - PCell cells; - PVG_FT_PtrDist max_cells; - PVG_FT_PtrDist num_cells; - - TPos x, y; - - PVG_FT_Outline outline; - PVG_FT_BBox clip_box; - - int clip_flags; - int clipping; - - PVG_FT_Span gray_spans[PVG_FT_MAX_GRAY_SPANS]; - int num_gray_spans; - int skip_spans; - - PVG_FT_Raster_Span_Func render_span; - void* render_span_data; - - int band_size; - int band_shoot; - - pvg_ft_jmp_buf jump_buffer; - - void* buffer; - long buffer_size; - - PCell* ycells; - TPos ycount; - } TWorker, *PWorker; - - - /*************************************************************************/ - /* */ - /* Initialize the cells table. */ - /* */ - static void - gray_init_cells( RAS_ARG_ void* buffer, - long byte_size ) - { - ras.buffer = buffer; - ras.buffer_size = byte_size; - - ras.ycells = (PCell*) buffer; - ras.cells = NULL; - ras.max_cells = 0; - ras.num_cells = 0; - ras.area = 0; - ras.cover = 0; - ras.invalid = 1; - } - - - /*************************************************************************/ - /* */ - /* Compute the outline bounding box. */ - /* */ - static void - gray_compute_cbox( RAS_ARG ) - { - PVG_FT_Outline* outline = &ras.outline; - PVG_FT_Vector* vec = outline->points; - PVG_FT_Vector* limit = vec + outline->n_points; - - - if ( outline->n_points <= 0 ) - { - ras.min_ex = ras.max_ex = 0; - ras.min_ey = ras.max_ey = 0; - return; - } - - ras.min_ex = ras.max_ex = vec->x; - ras.min_ey = ras.max_ey = vec->y; - - vec++; - - for ( ; vec < limit; vec++ ) - { - TPos x = vec->x; - TPos y = vec->y; - - - if ( x < ras.min_ex ) ras.min_ex = x; - if ( x > ras.max_ex ) ras.max_ex = x; - if ( y < ras.min_ey ) ras.min_ey = y; - if ( y > ras.max_ey ) ras.max_ey = y; - } - - /* truncate the bounding box to integer pixels */ - ras.min_ex = ras.min_ex >> 6; - ras.min_ey = ras.min_ey >> 6; - ras.max_ex = ( ras.max_ex + 63 ) >> 6; - ras.max_ey = ( ras.max_ey + 63 ) >> 6; - } - - - /*************************************************************************/ - /* */ - /* Record the current cell in the table. */ - /* */ - static PCell - gray_find_cell( RAS_ARG ) - { - PCell *pcell, cell; - TPos x = ras.ex; - - - if ( x > ras.count_ex ) - x = ras.count_ex; - - pcell = &ras.ycells[ras.ey]; - for (;;) - { - cell = *pcell; - if ( cell == NULL || cell->x > x ) - break; - - if ( cell->x == x ) - goto Exit; - - pcell = &cell->next; - } - - if ( ras.num_cells >= ras.max_cells ) - pvg_ft_longjmp( ras.jump_buffer, 1 ); - - cell = ras.cells + ras.num_cells++; - cell->x = x; - cell->area = 0; - cell->cover = 0; - - cell->next = *pcell; - *pcell = cell; - - Exit: - return cell; - } - - - static void - gray_record_cell( RAS_ARG ) - { - if ( ras.area | ras.cover ) - { - PCell cell = gray_find_cell( RAS_VAR ); - - - cell->area += ras.area; - cell->cover += ras.cover; - } - } - - - /*************************************************************************/ - /* */ - /* Set the current cell to a new position. */ - /* */ - static void - gray_set_cell( RAS_ARG_ TCoord ex, - TCoord ey ) - { - /* Move the cell pointer to a new position. We set the `invalid' */ - /* flag to indicate that the cell isn't part of those we're interested */ - /* in during the render phase. This means that: */ - /* */ - /* . the new vertical position must be within min_ey..max_ey-1. */ - /* . the new horizontal position must be strictly less than max_ex */ - /* */ - /* Note that if a cell is to the left of the clipping region, it is */ - /* actually set to the (min_ex-1) horizontal position. */ - - /* All cells that are on the left of the clipping region go to the */ - /* min_ex - 1 horizontal position. */ - ey -= ras.min_ey; - - if ( ex > ras.max_ex ) - ex = ras.max_ex; - - ex -= ras.min_ex; - if ( ex < 0 ) - ex = -1; - - /* are we moving to a different cell ? */ - if ( ex != ras.ex || ey != ras.ey ) - { - /* record the current one if it is valid */ - if ( !ras.invalid ) - gray_record_cell( RAS_VAR ); - - ras.area = 0; - ras.cover = 0; - ras.ex = ex; - ras.ey = ey; - } - - ras.invalid = ( (unsigned int)ey >= (unsigned int)ras.count_ey || - ex >= ras.count_ex ); - } - - - /*************************************************************************/ - /* */ - /* Start a new contour at a given cell. */ - /* */ - static void - gray_start_cell( RAS_ARG_ TCoord ex, - TCoord ey ) - { - if ( ex > ras.max_ex ) - ex = (TCoord)( ras.max_ex ); - - if ( ex < ras.min_ex ) - ex = (TCoord)( ras.min_ex - 1 ); - - ras.area = 0; - ras.cover = 0; - ras.ex = ex - ras.min_ex; - ras.ey = ey - ras.min_ey; - ras.invalid = 0; - - gray_set_cell( RAS_VAR_ ex, ey ); - } - -// The new render-line implementation is not yet used -#if 1 - - /*************************************************************************/ - /* */ - /* Render a scanline as one or more cells. */ - /* */ - static void - gray_render_scanline( RAS_ARG_ TCoord ey, - TPos x1, - TCoord y1, - TPos x2, - TCoord y2 ) - { - TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod; - TPos p, dx; - int incr; - - - ex1 = TRUNC( x1 ); - ex2 = TRUNC( x2 ); - - /* trivial case. Happens often */ - if ( y1 == y2 ) - { - gray_set_cell( RAS_VAR_ ex2, ey ); - return; - } - - fx1 = FRACT( x1 ); - fx2 = FRACT( x2 ); - - /* everything is located in a single cell. That is easy! */ - /* */ - if ( ex1 == ex2 ) - goto End; - - /* ok, we'll have to render a run of adjacent cells on the same */ - /* scanline... */ - /* */ - dx = x2 - x1; - dy = y2 - y1; - - if ( dx > 0 ) - { - p = ( ONE_PIXEL - fx1 ) * dy; - first = ONE_PIXEL; - incr = 1; - } else { - p = fx1 * dy; - first = 0; - incr = -1; - dx = -dx; - } - - PVG_FT_DIV_MOD( TCoord, p, dx, delta, mod ); - - ras.area += (TArea)( fx1 + first ) * delta; - ras.cover += delta; - y1 += delta; - ex1 += incr; - gray_set_cell( RAS_VAR_ ex1, ey ); - - if ( ex1 != ex2 ) - { - TCoord lift, rem; - - - p = ONE_PIXEL * dy; - PVG_FT_DIV_MOD( TCoord, p, dx, lift, rem ); - - do - { - delta = lift; - mod += rem; - if ( mod >= (TCoord)dx ) - { - mod -= (TCoord)dx; - delta++; - } - - ras.area += (TArea)( ONE_PIXEL * delta ); - ras.cover += delta; - y1 += delta; - ex1 += incr; - gray_set_cell( RAS_VAR_ ex1, ey ); - } while ( ex1 != ex2 ); - } - fx1 = ONE_PIXEL - first; - - End: - dy = y2 - y1; - - ras.area += (TArea)( ( fx1 + fx2 ) * dy ); - ras.cover += dy; - } - - - /*************************************************************************/ - /* */ - /* Render a given line as a series of scanlines. */ - /* */ - static void - gray_render_line( RAS_ARG_ TPos from_x, TPos from_y, TPos to_x, TPos to_y ) - { - TCoord ey1, ey2, fy1, fy2, first, delta, mod; - TPos p, dx, dy, x, x2; - int incr; - - ey1 = TRUNC( from_y ); - ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ - - /* perform vertical clipping */ - if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || - ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) - return; - - fy1 = FRACT( from_y ); - fy2 = FRACT( to_y ); - - /* everything is on a single scanline */ - if ( ey1 == ey2 ) - { - gray_render_scanline( RAS_VAR_ ey1, from_x, fy1, to_x, fy2 ); - return; - } - - dx = to_x - from_x; - dy = to_y - from_y; - - /* vertical line - avoid calling gray_render_scanline */ - if ( dx == 0 ) - { - TCoord ex = TRUNC( from_x ); - TCoord two_fx = FRACT( from_x ) << 1; - TPos area, max_ey1; - - - if ( dy > 0) - { - first = ONE_PIXEL; - } - else - { - first = 0; - } - - delta = first - fy1; - ras.area += (TArea)two_fx * delta; - ras.cover += delta; - - delta = first + first - ONE_PIXEL; - area = (TArea)two_fx * delta; - max_ey1 = ras.count_ey + ras.min_ey; - if (dy < 0) { - if (ey1 > max_ey1) { - ey1 = (max_ey1 > ey2) ? max_ey1 : ey2; - gray_set_cell( &ras, ex, ey1 ); - } else { - ey1--; - gray_set_cell( &ras, ex, ey1 ); - } - while ( ey1 > ey2 && ey1 >= ras.min_ey) - { - ras.area += area; - ras.cover += delta; - ey1--; - - gray_set_cell( &ras, ex, ey1 ); - } - if (ey1 != ey2) { - ey1 = ey2; - gray_set_cell( &ras, ex, ey1 ); - } - } else { - if (ey1 < ras.min_ey) { - ey1 = (ras.min_ey < ey2) ? ras.min_ey : ey2; - gray_set_cell( &ras, ex, ey1 ); - } else { - ey1++; - gray_set_cell( &ras, ex, ey1 ); - } - while ( ey1 < ey2 && ey1 < max_ey1) - { - ras.area += area; - ras.cover += delta; - ey1++; - - gray_set_cell( &ras, ex, ey1 ); - } - if (ey1 != ey2) { - ey1 = ey2; - gray_set_cell( &ras, ex, ey1 ); - } - } - - delta = (int)( fy2 - ONE_PIXEL + first ); - ras.area += (TArea)two_fx * delta; - ras.cover += delta; - - return; - } - - /* ok, we have to render several scanlines */ - if ( dy > 0) - { - p = ( ONE_PIXEL - fy1 ) * dx; - first = ONE_PIXEL; - incr = 1; - } - else - { - p = fy1 * dx; - first = 0; - incr = -1; - dy = -dy; - } - - /* the fractional part of x-delta is mod/dy. It is essential to */ - /* keep track of its accumulation for accurate rendering. */ - PVG_FT_DIV_MOD( TCoord, p, dy, delta, mod ); - - x = from_x + delta; - gray_render_scanline( RAS_VAR_ ey1, from_x, fy1, x, (TCoord)first ); - - ey1 += incr; - gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); - - if ( ey1 != ey2 ) - { - TCoord lift, rem; - - - p = ONE_PIXEL * dx; - PVG_FT_DIV_MOD( TCoord, p, dy, lift, rem ); - - do - { - delta = lift; - mod += rem; - if ( mod >= (TCoord)dy ) - { - mod -= (TCoord)dy; - delta++; - } - - x2 = x + delta; - gray_render_scanline( RAS_VAR_ ey1, - x, ONE_PIXEL - first, - x2, first ); - x = x2; - - ey1 += incr; - gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); - } while ( ey1 != ey2 ); - } - - gray_render_scanline( RAS_VAR_ ey1, - x, ONE_PIXEL - first, - to_x, fy2 ); - } - - -#else - - /*************************************************************************/ - /* */ - /* Render a straight line across multiple cells in any direction. */ - /* */ - static void - gray_render_line( RAS_ARG_ TPos from_x, TPos from_y, TPos to_x, TPos to_y ) - { - TPos dx, dy, fx1, fy1, fx2, fy2; - TCoord ex1, ex2, ey1, ey2; - - - ex1 = TRUNC( from_x ); - ex2 = TRUNC( to_x ); - ey1 = TRUNC( from_y ); - ey2 = TRUNC( to_y ); - - /* perform vertical clipping */ - if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || - ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) - return; - - dx = to_x - from_x; - dy = to_y - from_y; - - fx1 = FRACT( from_x ); - fy1 = FRACT( from_y ); - - if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */ - ; - else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */ - { - ex1 = ex2; - gray_set_cell( RAS_VAR_ ex1, ey1 ); - } - else if ( dx == 0 ) - { - if ( dy > 0 ) /* vertical line up */ - do - { - fy2 = ONE_PIXEL; - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * fx1 * 2; - fy1 = 0; - ey1++; - gray_set_cell( RAS_VAR_ ex1, ey1 ); - } while ( ey1 != ey2 ); - else /* vertical line down */ - do - { - fy2 = 0; - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * fx1 * 2; - fy1 = ONE_PIXEL; - ey1--; - gray_set_cell( RAS_VAR_ ex1, ey1 ); - } while ( ey1 != ey2 ); - } - else /* any other line */ - { - TArea prod = dx * fy1 - dy * fx1; - PVG_FT_UDIVPREP( dx ); - PVG_FT_UDIVPREP( dy ); - - - /* The fundamental value `prod' determines which side and the */ - /* exact coordinate where the line exits current cell. It is */ - /* also easily updated when moving from one cell to the next. */ - do - { - if ( prod <= 0 && - prod - dx * ONE_PIXEL > 0 ) /* left */ - { - fx2 = 0; - fy2 = (TPos)PVG_FT_UDIV( -prod, -dx ); - prod -= dy * ONE_PIXEL; - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - fx1 = ONE_PIXEL; - fy1 = fy2; - ex1--; - } - else if ( prod - dx * ONE_PIXEL <= 0 && - prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */ - { - prod -= dx * ONE_PIXEL; - fx2 = (TPos)PVG_FT_UDIV( -prod, dy ); - fy2 = ONE_PIXEL; - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - fx1 = fx2; - fy1 = 0; - ey1++; - } - else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 && - prod + dy * ONE_PIXEL >= 0 ) /* right */ - { - prod += dy * ONE_PIXEL; - fx2 = ONE_PIXEL; - fy2 = (TPos)PVG_FT_UDIV( prod, dx ); - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - fx1 = 0; - fy1 = fy2; - ex1++; - } - else /* ( prod + dy * ONE_PIXEL < 0 && - prod > 0 ) down */ - { - fx2 = (TPos)PVG_FT_UDIV( prod, -dy ); - fy2 = 0; - prod += dx * ONE_PIXEL; - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - fx1 = fx2; - fy1 = ONE_PIXEL; - ey1--; - } - - gray_set_cell( RAS_VAR_ ex1, ey1 ); - } while ( ex1 != ex2 || ey1 != ey2 ); - } - - fx2 = FRACT( to_x ); - fy2 = FRACT( to_y ); - - ras.cover += ( fy2 - fy1 ); - ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - } - -#endif - - static int - gray_clip_flags( RAS_ARG_ TPos x, TPos y ) - { - return ((x > ras.clip_box.xMax) << 0) | ((y > ras.clip_box.yMax) << 1) | - ((x < ras.clip_box.xMin) << 2) | ((y < ras.clip_box.yMin) << 3); - } - - static int - gray_clip_vflags( RAS_ARG_ TPos y ) - { - return ((y > ras.clip_box.yMax) << 1) | ((y < ras.clip_box.yMin) << 3); - } - - static void - gray_vline( RAS_ARG_ TPos x1, TPos y1, TPos x2, TPos y2, int f1, int f2 ) - { - f1 &= 10; - f2 &= 10; - if((f1 | f2) == 0) /* Fully visible */ - { - gray_render_line( RAS_VAR_ x1, y1, x2, y2 ); - } - else if(f1 == f2) /* Invisible by Y */ - { - return; - } - else - { - TPos tx1, ty1, tx2, ty2; - TPos clip_y1, clip_y2; - - tx1 = x1; - ty1 = y1; - tx2 = x2; - ty2 = y2; - - clip_y1 = ras.clip_box.yMin; - clip_y2 = ras.clip_box.yMax; - - if(f1 & 8) /* y1 < clip_y1 */ - { - tx1 = x1 + PVG_FT_MulDiv(clip_y1-y1, x2-x1, y2-y1); - ty1 = clip_y1; - } - - if(f1 & 2) /* y1 > clip_y2 */ - { - tx1 = x1 + PVG_FT_MulDiv(clip_y2-y1, x2-x1, y2-y1); - ty1 = clip_y2; - } - - if(f2 & 8) /* y2 < clip_y1 */ - { - tx2 = x1 + PVG_FT_MulDiv(clip_y1-y1, x2-x1, y2-y1); - ty2 = clip_y1; - } - - if(f2 & 2) /* y2 > clip_y2 */ - { - tx2 = x1 + PVG_FT_MulDiv(clip_y2-y1, x2-x1, y2-y1); - ty2 = clip_y2; - } - - gray_render_line( RAS_VAR_ tx1, ty1, tx2, ty2 ); - } - } - - static void - gray_line_to( RAS_ARG_ TPos x2, TPos y2 ) - { - if ( !ras.clipping ) - { - gray_render_line( RAS_VAR_ ras.x, ras.y, x2, y2 ); - } - else - { - TPos x1, y1, y3, y4; - TPos clip_x1, clip_x2; - int f1, f2, f3, f4; - - f1 = ras.clip_flags; - f2 = gray_clip_flags( RAS_VAR_ x2, y2 ); - - if((f1 & 10) == (f2 & 10) && (f1 & 10) != 0) /* Invisible by Y */ - { - ras.clip_flags = f2; - goto End; - } - - x1 = ras.x; - y1 = ras.y; - - clip_x1 = ras.clip_box.xMin; - clip_x2 = ras.clip_box.xMax; - - switch(((f1 & 5) << 1) | (f2 & 5)) - { - case 0: /* Visible by X */ - gray_vline( RAS_VAR_ x1, y1, x2, y2, f1, f2); - break; - - case 1: /* x2 > clip_x2 */ - y3 = y1 + PVG_FT_MulDiv(clip_x2-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - gray_vline( RAS_VAR_ x1, y1, clip_x2, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x2, y3, clip_x2, y2, f3, f2); - break; - - case 2: /* x1 > clip_x2 */ - y3 = y1 + PVG_FT_MulDiv(clip_x2-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - gray_vline( RAS_VAR_ clip_x2, y1, clip_x2, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x2, y3, x2, y2, f3, f2); - break; - - case 3: /* x1 > clip_x2 && x2 > clip_x2 */ - gray_vline( RAS_VAR_ clip_x2, y1, clip_x2, y2, f1, f2); - break; - - case 4: /* x2 < clip_x1 */ - y3 = y1 + PVG_FT_MulDiv(clip_x1-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - gray_vline( RAS_VAR_ x1, y1, clip_x1, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x1, y3, clip_x1, y2, f3, f2); - break; - - case 6: /* x1 > clip_x2 && x2 < clip_x1 */ - y3 = y1 + PVG_FT_MulDiv(clip_x2-x1, y2-y1, x2-x1); - y4 = y1 + PVG_FT_MulDiv(clip_x1-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - f4 = gray_clip_vflags( RAS_VAR_ y4 ); - gray_vline( RAS_VAR_ clip_x2, y1, clip_x2, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x2, y3, clip_x1, y4, f3, f4); - gray_vline( RAS_VAR_ clip_x1, y4, clip_x1, y2, f4, f2); - break; - - case 8: /* x1 < clip_x1 */ - y3 = y1 + PVG_FT_MulDiv(clip_x1-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - gray_vline( RAS_VAR_ clip_x1, y1, clip_x1, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x1, y3, x2, y2, f3, f2); - break; - - case 9: /* x1 < clip_x1 && x2 > clip_x2 */ - y3 = y1 + PVG_FT_MulDiv(clip_x1-x1, y2-y1, x2-x1); - y4 = y1 + PVG_FT_MulDiv(clip_x2-x1, y2-y1, x2-x1); - f3 = gray_clip_vflags( RAS_VAR_ y3 ); - f4 = gray_clip_vflags( RAS_VAR_ y4 ); - gray_vline( RAS_VAR_ clip_x1, y1, clip_x1, y3, f1, f3); - gray_vline( RAS_VAR_ clip_x1, y3, clip_x2, y4, f3, f4); - gray_vline( RAS_VAR_ clip_x2, y4, clip_x2, y2, f4, f2); - break; - - case 12: /* x1 < clip_x1 && x2 < clip_x1 */ - gray_vline( RAS_VAR_ clip_x1, y1, clip_x1, y2, f1, f2); - break; - } - - ras.clip_flags = f2; - } - - End: - ras.x = x2; - ras.y = y2; - } - - static void - gray_split_conic( PVG_FT_Vector* base ) - { - TPos a, b; - - - base[4].x = base[2].x; - b = base[1].x; - a = base[3].x = ( base[2].x + b ) / 2; - b = base[1].x = ( base[0].x + b ) / 2; - base[2].x = ( a + b ) / 2; - - base[4].y = base[2].y; - b = base[1].y; - a = base[3].y = ( base[2].y + b ) / 2; - b = base[1].y = ( base[0].y + b ) / 2; - base[2].y = ( a + b ) / 2; - } - - - static void - gray_render_conic( RAS_ARG_ const PVG_FT_Vector* control, - const PVG_FT_Vector* to ) - { - PVG_FT_Vector bez_stack[16 * 2 + 1]; /* enough to accommodate bisections */ - PVG_FT_Vector* arc = bez_stack; - TPos dx, dy; - int draw, split; - - - arc[0].x = UPSCALE( to->x ); - arc[0].y = UPSCALE( to->y ); - arc[1].x = UPSCALE( control->x ); - arc[1].y = UPSCALE( control->y ); - arc[2].x = ras.x; - arc[2].y = ras.y; - - /* short-cut the arc that crosses the current band */ - if ( ( TRUNC( arc[0].y ) >= ras.max_ey && - TRUNC( arc[1].y ) >= ras.max_ey && - TRUNC( arc[2].y ) >= ras.max_ey ) || - ( TRUNC( arc[0].y ) < ras.min_ey && - TRUNC( arc[1].y ) < ras.min_ey && - TRUNC( arc[2].y ) < ras.min_ey ) ) - { - if ( ras.clipping ) - ras.clip_flags = gray_clip_flags ( RAS_VAR_ arc[0].x, arc[0].y ); - ras.x = arc[0].x; - ras.y = arc[0].y; - return; - } - - dx = PVG_FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); - dy = PVG_FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); - if ( dx < dy ) - dx = dy; - - /* We can calculate the number of necessary bisections because */ - /* each bisection predictably reduces deviation exactly 4-fold. */ - /* Even 32-bit deviation would vanish after 16 bisections. */ - draw = 1; - while ( dx > ONE_PIXEL / 4 ) - { - dx >>= 2; - draw <<= 1; - } - - /* We use decrement counter to count the total number of segments */ - /* to draw starting from 2^level. Before each draw we split as */ - /* many times as there are trailing zeros in the counter. */ - do - { - split = 1; - while ( ( draw & split ) == 0 ) - { - gray_split_conic( arc ); - arc += 2; - split <<= 1; - } - - gray_line_to( RAS_VAR_ arc[0].x, arc[0].y ); - arc -= 2; - - } while ( --draw ); - } - - - static void - gray_split_cubic( PVG_FT_Vector* base ) - { - TPos a, b, c, d; - - - base[6].x = base[3].x; - c = base[1].x; - d = base[2].x; - base[1].x = a = ( base[0].x + c ) / 2; - base[5].x = b = ( base[3].x + d ) / 2; - c = ( c + d ) / 2; - base[2].x = a = ( a + c ) / 2; - base[4].x = b = ( b + c ) / 2; - base[3].x = ( a + b ) / 2; - - base[6].y = base[3].y; - c = base[1].y; - d = base[2].y; - base[1].y = a = ( base[0].y + c ) / 2; - base[5].y = b = ( base[3].y + d ) / 2; - c = ( c + d ) / 2; - base[2].y = a = ( a + c ) / 2; - base[4].y = b = ( b + c ) / 2; - base[3].y = ( a + b ) / 2; - } - - - static void - gray_render_cubic( RAS_ARG_ const PVG_FT_Vector* control1, - const PVG_FT_Vector* control2, - const PVG_FT_Vector* to ) - { - PVG_FT_Vector bez_stack[16 * 3 + 1]; /* enough to accommodate bisections */ - PVG_FT_Vector* arc = bez_stack; - PVG_FT_Vector* limit = bez_stack + 45; - TPos dx, dy, dx_, dy_; - TPos dx1, dy1, dx2, dy2; - TPos L, s, s_limit; - - - arc[0].x = UPSCALE( to->x ); - arc[0].y = UPSCALE( to->y ); - arc[1].x = UPSCALE( control2->x ); - arc[1].y = UPSCALE( control2->y ); - arc[2].x = UPSCALE( control1->x ); - arc[2].y = UPSCALE( control1->y ); - arc[3].x = ras.x; - arc[3].y = ras.y; - - /* short-cut the arc that crosses the current band */ - if ( ( TRUNC( arc[0].y ) >= ras.max_ey && - TRUNC( arc[1].y ) >= ras.max_ey && - TRUNC( arc[2].y ) >= ras.max_ey && - TRUNC( arc[3].y ) >= ras.max_ey ) || - ( TRUNC( arc[0].y ) < ras.min_ey && - TRUNC( arc[1].y ) < ras.min_ey && - TRUNC( arc[2].y ) < ras.min_ey && - TRUNC( arc[3].y ) < ras.min_ey ) ) - { - if ( ras.clipping ) - ras.clip_flags = gray_clip_flags ( RAS_VAR_ arc[0].x, arc[0].y ); - ras.x = arc[0].x; - ras.y = arc[0].y; - return; - } - - for (;;) - { - /* Decide whether to split or draw. See `Rapid Termination */ - /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ - /* F. Hain, at */ - /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ - - - /* dx and dy are x and y components of the P0-P3 chord vector. */ - dx = dx_ = arc[3].x - arc[0].x; - dy = dy_ = arc[3].y - arc[0].y; - - L = PVG_FT_HYPOT( dx_, dy_ ); - - /* Avoid possible arithmetic overflow below by splitting. */ - if ( L >= (1 << 23) ) - goto Split; - - /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ - s_limit = L * (TPos)( ONE_PIXEL / 6 ); - - /* s is L * the perpendicular distance from P1 to the line P0-P3. */ - dx1 = arc[1].x - arc[0].x; - dy1 = arc[1].y - arc[0].y; - s = PVG_FT_ABS( dy * dx1 - dx * dy1 ); - - if ( s > s_limit ) - goto Split; - - /* s is L * the perpendicular distance from P2 to the line P0-P3. */ - dx2 = arc[2].x - arc[0].x; - dy2 = arc[2].y - arc[0].y; - s = PVG_FT_ABS( dy * dx2 - dx * dy2 ); - - if ( s > s_limit ) - goto Split; - - /* Split super curvy segments where the off points are so far - from the chord that the angles P0-P1-P3 or P0-P2-P3 become - acute as detected by appropriate dot products. */ - if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 || - dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 ) - goto Split; - - gray_line_to( RAS_VAR_ arc[0].x, arc[0].y ); - - if ( arc == bez_stack ) - return; - - arc -= 3; - continue; - - Split: - if( arc == limit ) - return; - gray_split_cubic( arc ); - arc += 3; - } - } - - - - static int - gray_move_to( const PVG_FT_Vector* to, - PWorker worker ) - { - TPos x, y; - - - /* record current cell, if any */ - if ( !ras.invalid ) - gray_record_cell( worker ); - - /* start to a new position */ - x = UPSCALE( to->x ); - y = UPSCALE( to->y ); - - gray_start_cell( worker, TRUNC( x ), TRUNC( y ) ); - - if ( ras.clipping ) - ras.clip_flags = gray_clip_flags( worker, x, y ); - ras.x = x; - ras.y = y; - return 0; - } - - - static void - gray_hline( RAS_ARG_ TCoord x, - TCoord y, - TPos area, - int acount ) - { - int coverage; - - - /* compute the coverage line's coverage, depending on the */ - /* outline fill rule */ - /* */ - /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ - /* */ - coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) ); - /* use range 0..256 */ - if ( coverage < 0 ) - coverage = -coverage; - - if ( ras.outline.flags & PVG_FT_OUTLINE_EVEN_ODD_FILL ) - { - coverage &= 511; - - if ( coverage > 256 ) - coverage = 512 - coverage; - else if ( coverage == 256 ) - coverage = 255; - } - else - { - /* normal non-zero winding rule */ - if ( coverage >= 256 ) - coverage = 255; - } - - y += (TCoord)ras.min_ey; - x += (TCoord)ras.min_ex; - - /* PVG_FT_Span.x is an int, so limit our coordinates appropriately */ - if ( x >= (1 << 23) ) - x = (1 << 23) - 1; - - /* PVG_FT_Span.y is an int, so limit our coordinates appropriately */ - if ( y >= (1 << 23) ) - y = (1 << 23) - 1; - - if ( coverage ) - { - PVG_FT_Span* span; - int count; - int skip; - - /* see whether we can add this span to the current list */ - count = ras.num_gray_spans; - span = ras.gray_spans + count - 1; - if ( count > 0 && - span->y == y && - span->x + span->len == x && - span->coverage == coverage ) - { - span->len = span->len + acount; - return; - } - - if ( count >= PVG_FT_MAX_GRAY_SPANS ) - { - if ( ras.render_span && count > ras.skip_spans ) - { - skip = ras.skip_spans > 0 ? ras.skip_spans : 0; - ras.render_span( ras.num_gray_spans - skip, - ras.gray_spans + skip, - ras.render_span_data ); - } - - ras.skip_spans -= ras.num_gray_spans; - /* ras.render_span( span->y, ras.gray_spans, count ); */ - ras.num_gray_spans = 0; - - span = ras.gray_spans; - } - else - span++; - - /* add a gray span to the current list */ - span->x = x; - span->len = acount; - span->y = y; - span->coverage = (unsigned char)coverage; - - ras.num_gray_spans++; - } - } - - - - static void - gray_sweep( RAS_ARG) - { - int yindex; - - if ( ras.num_cells == 0 ) - return; - - for ( yindex = 0; yindex < ras.ycount; yindex++ ) - { - PCell cell = ras.ycells[yindex]; - TCoord cover = 0; - TCoord x = 0; - - - for ( ; cell != NULL; cell = cell->next ) - { - TArea area; - - - if ( cell->x > x && cover != 0 ) - gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), - cell->x - x ); - - cover += cell->cover; - area = cover * ( ONE_PIXEL * 2 ) - cell->area; - - if ( area != 0 && cell->x >= 0 ) - gray_hline( RAS_VAR_ cell->x, yindex, area, 1 ); - - x = cell->x + 1; - } - - if ( ras.count_ex > x && cover != 0 ) - gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), - ras.count_ex - x ); - } - } - -PVG_FT_Error PVG_FT_Outline_Check(PVG_FT_Outline* outline) -{ - if (outline) { - PVG_FT_Int n_points = outline->n_points; - PVG_FT_Int n_contours = outline->n_contours; - PVG_FT_Int end0, end; - PVG_FT_Int n; - - /* empty glyph? */ - if (n_points == 0 && n_contours == 0) return 0; - - /* check point and contour counts */ - if (n_points <= 0 || n_contours <= 0) goto Bad; - - end0 = end = -1; - for (n = 0; n < n_contours; n++) { - end = outline->contours[n]; - - /* note that we don't accept empty contours */ - if (end <= end0 || end >= n_points) goto Bad; - - end0 = end; - } - - if (end != n_points - 1) goto Bad; - - /* XXX: check the tags array */ - return 0; - } - -Bad: - return ErrRaster_Invalid_Outline; -} - -void PVG_FT_Outline_Get_CBox(const PVG_FT_Outline* outline, PVG_FT_BBox* acbox) -{ - PVG_FT_Pos xMin, yMin, xMax, yMax; - - if (outline && acbox) { - if (outline->n_points == 0) { - xMin = 0; - yMin = 0; - xMax = 0; - yMax = 0; - } else { - PVG_FT_Vector* vec = outline->points; - PVG_FT_Vector* limit = vec + outline->n_points; - - xMin = xMax = vec->x; - yMin = yMax = vec->y; - vec++; - - for (; vec < limit; vec++) { - PVG_FT_Pos x, y; - - x = vec->x; - if (x < xMin) xMin = x; - if (x > xMax) xMax = x; - - y = vec->y; - if (y < yMin) yMin = y; - if (y > yMax) yMax = y; - } - } - acbox->xMin = xMin; - acbox->xMax = xMax; - acbox->yMin = yMin; - acbox->yMax = yMax; - } -} - - /*************************************************************************/ - /* */ - /* The following function should only compile in stand_alone mode, */ - /* i.e., when building this component without the rest of FreeType. */ - /* */ - /*************************************************************************/ - - /*************************************************************************/ - /* */ - /* */ - /* PVG_FT_Outline_Decompose */ - /* */ - /* */ - /* Walks over an outline's structure to decompose it into individual */ - /* segments and Bezier arcs. This function is also able to emit */ - /* `move to' and `close to' operations to indicate the start and end */ - /* of new contours in the outline. */ - /* */ - /* */ - /* outline :: A pointer to the source target. */ - /* */ - /* user :: A typeless pointer which is passed to each */ - /* emitter during the decomposition. It can be */ - /* used to store the state during the */ - /* decomposition. */ - /* */ - /* */ - /* Error code. 0 means success. */ - /* */ - static - int PVG_FT_Outline_Decompose( const PVG_FT_Outline* outline, - void* user ) - { -#undef SCALED -#define SCALED( x ) (x) - - PVG_FT_Vector v_last; - PVG_FT_Vector v_control; - PVG_FT_Vector v_start; - - PVG_FT_Vector* point; - PVG_FT_Vector* limit; - char* tags; - - int n; /* index of contour in outline */ - int first; /* index of first point in contour */ - int error; - char tag; /* current point's state */ - - if ( !outline ) - return ErrRaster_Invalid_Outline; - - first = 0; - - for ( n = 0; n < outline->n_contours; n++ ) - { - int last; /* index of last point in contour */ - - - last = outline->contours[n]; - if ( last < 0 ) - goto Invalid_Outline; - limit = outline->points + last; - - v_start = outline->points[first]; - v_start.x = SCALED( v_start.x ); - v_start.y = SCALED( v_start.y ); - - v_last = outline->points[last]; - v_last.x = SCALED( v_last.x ); - v_last.y = SCALED( v_last.y ); - - v_control = v_start; - - point = outline->points + first; - tags = outline->tags + first; - tag = PVG_FT_CURVE_TAG( tags[0] ); - - /* A contour cannot start with a cubic control point! */ - if ( tag == PVG_FT_CURVE_TAG_CUBIC ) - goto Invalid_Outline; - - /* check first point to determine origin */ - if ( tag == PVG_FT_CURVE_TAG_CONIC ) - { - /* first point is conic control. Yes, this happens. */ - if ( PVG_FT_CURVE_TAG( outline->tags[last] ) == PVG_FT_CURVE_TAG_ON ) - { - /* start at last point if it is on the curve */ - v_start = v_last; - limit--; - } - else - { - /* if both first and last points are conic, */ - /* start at their middle and record its position */ - /* for closure */ - v_start.x = ( v_start.x + v_last.x ) / 2; - v_start.y = ( v_start.y + v_last.y ) / 2; - - v_last = v_start; - } - point--; - tags--; - } - - error = gray_move_to( &v_start, user ); - if ( error ) - goto Exit; - - while ( point < limit ) - { - point++; - tags++; - - tag = PVG_FT_CURVE_TAG( tags[0] ); - switch ( tag ) - { - case PVG_FT_CURVE_TAG_ON: /* emit a single line_to */ - { - PVG_FT_Vector vec; - - - vec.x = SCALED( point->x ); - vec.y = SCALED( point->y ); - - gray_line_to(user, UPSCALE(vec.x), UPSCALE(vec.y)); - continue; - } - - case PVG_FT_CURVE_TAG_CONIC: /* consume conic arcs */ - { - v_control.x = SCALED( point->x ); - v_control.y = SCALED( point->y ); - - Do_Conic: - if ( point < limit ) - { - PVG_FT_Vector vec; - PVG_FT_Vector v_middle; - - - point++; - tags++; - tag = PVG_FT_CURVE_TAG( tags[0] ); - - vec.x = SCALED( point->x ); - vec.y = SCALED( point->y ); - - if ( tag == PVG_FT_CURVE_TAG_ON ) - { - gray_render_conic(user, &v_control, &vec); - continue; - } - - if ( tag != PVG_FT_CURVE_TAG_CONIC ) - goto Invalid_Outline; - - v_middle.x = ( v_control.x + vec.x ) / 2; - v_middle.y = ( v_control.y + vec.y ) / 2; - - gray_render_conic(user, &v_control, &v_middle); - - v_control = vec; - goto Do_Conic; - } - - gray_render_conic(user, &v_control, &v_start); - goto Close; - } - - default: /* PVG_FT_CURVE_TAG_CUBIC */ - { - PVG_FT_Vector vec1, vec2; - - - if ( point + 1 > limit || - PVG_FT_CURVE_TAG( tags[1] ) != PVG_FT_CURVE_TAG_CUBIC ) - goto Invalid_Outline; - - point += 2; - tags += 2; - - vec1.x = SCALED( point[-2].x ); - vec1.y = SCALED( point[-2].y ); - - vec2.x = SCALED( point[-1].x ); - vec2.y = SCALED( point[-1].y ); - - if ( point <= limit ) - { - PVG_FT_Vector vec; - - - vec.x = SCALED( point->x ); - vec.y = SCALED( point->y ); - - gray_render_cubic(user, &vec1, &vec2, &vec); - continue; - } - - gray_render_cubic(user, &vec1, &vec2, &v_start); - goto Close; - } - } - } - - /* close the contour with a line segment */ - gray_line_to(user, UPSCALE(v_start.x), UPSCALE(v_start.y)); - - Close: - first = last + 1; - } - - return 0; - - Exit: - return error; - - Invalid_Outline: - return ErrRaster_Invalid_Outline; - } - - typedef struct TBand_ - { - TPos min, max; - - } TBand; - - static int - gray_convert_glyph_inner( RAS_ARG ) - { - volatile int error = 0; - - if ( pvg_ft_setjmp( ras.jump_buffer ) == 0 ) - { - error = PVG_FT_Outline_Decompose( &ras.outline, &ras ); - if ( !ras.invalid ) - gray_record_cell( RAS_VAR ); - } - else - { - error = ErrRaster_Memory_Overflow; - } - - return error; - } - - - static int - gray_convert_glyph( RAS_ARG ) - { - TBand bands[40]; - TBand* volatile band; - int volatile n, num_bands; - TPos volatile min, max, max_y; - PVG_FT_BBox* clip; - int skip; - - ras.num_gray_spans = 0; - - /* Set up state in the raster object */ - gray_compute_cbox( RAS_VAR ); - - /* clip to target bitmap, exit if nothing to do */ - clip = &ras.clip_box; - - if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax || - ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax ) - return 0; - - ras.clip_flags = ras.clipping = 0; - - if ( ras.min_ex < clip->xMin ) { - ras.min_ex = clip->xMin; - ras.clipping = 1; - } - - if ( ras.min_ey < clip->yMin ) { - ras.min_ey = clip->yMin; - ras.clipping = 1; - } - - if ( ras.max_ex > clip->xMax ) { - ras.max_ex = clip->xMax; - ras.clipping = 1; - } - - if ( ras.max_ey > clip->yMax ) { - ras.max_ey = clip->yMax; - ras.clipping = 1; - } - - clip->xMin = (ras.min_ex - 1) * ONE_PIXEL; - clip->yMin = (ras.min_ey - 1) * ONE_PIXEL; - clip->xMax = (ras.max_ex + 1) * ONE_PIXEL; - clip->yMax = (ras.max_ey + 1) * ONE_PIXEL; - - ras.count_ex = ras.max_ex - ras.min_ex; - ras.count_ey = ras.max_ey - ras.min_ey; - - /* set up vertical bands */ - num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size ); - if ( num_bands == 0 ) - num_bands = 1; - if ( num_bands >= 39 ) - num_bands = 39; - - ras.band_shoot = 0; - - min = ras.min_ey; - max_y = ras.max_ey; - - for ( n = 0; n < num_bands; n++, min = max ) - { - max = min + ras.band_size; - if ( n == num_bands - 1 || max > max_y ) - max = max_y; - - bands[0].min = min; - bands[0].max = max; - band = bands; - - while ( band >= bands ) - { - TPos bottom, top, middle; - int error; - - { - PCell cells_max; - int yindex; - int cell_start, cell_end, cell_mod; - - - ras.ycells = (PCell*)ras.buffer; - ras.ycount = band->max - band->min; - - cell_start = sizeof ( PCell ) * ras.ycount; - cell_mod = cell_start % sizeof ( TCell ); - if ( cell_mod > 0 ) - cell_start += sizeof ( TCell ) - cell_mod; - - cell_end = ras.buffer_size; - cell_end -= cell_end % sizeof( TCell ); - - cells_max = (PCell)( (char*)ras.buffer + cell_end ); - ras.cells = (PCell)( (char*)ras.buffer + cell_start ); - if ( ras.cells >= cells_max ) - goto ReduceBands; - - ras.max_cells = (int)(cells_max - ras.cells); - if ( ras.max_cells < 2 ) - goto ReduceBands; - - for ( yindex = 0; yindex < ras.ycount; yindex++ ) - ras.ycells[yindex] = NULL; - } - - ras.num_cells = 0; - ras.invalid = 1; - ras.min_ey = band->min; - ras.max_ey = band->max; - ras.count_ey = band->max - band->min; - - error = gray_convert_glyph_inner( RAS_VAR ); - - if ( !error ) - { - gray_sweep( RAS_VAR); - band--; - continue; - } - else if ( error != ErrRaster_Memory_Overflow ) - return 1; - - ReduceBands: - /* render pool overflow; we will reduce the render band by half */ - bottom = band->min; - top = band->max; - middle = bottom + ( ( top - bottom ) >> 1 ); - - /* This is too complex for a single scanline; there must */ - /* be some problems. */ - if ( middle == bottom ) - { - return ErrRaster_OutOfMemory; - } - - if ( bottom-top >= ras.band_size ) - ras.band_shoot++; - - band[1].min = bottom; - band[1].max = middle; - band[0].min = middle; - band[0].max = top; - band++; - } - } - - if ( ras.render_span && ras.num_gray_spans > ras.skip_spans ) - { - skip = ras.skip_spans > 0 ? ras.skip_spans : 0; - ras.render_span( ras.num_gray_spans - skip, - ras.gray_spans + skip, - ras.render_span_data ); - } - - ras.skip_spans -= ras.num_gray_spans; - - if ( ras.band_shoot > 8 && ras.band_size > 16 ) - ras.band_size = ras.band_size / 2; - - return 0; - } - - - static int - gray_raster_render( RAS_ARG_ void* buffer, long buffer_size, - const PVG_FT_Raster_Params* params ) - { - const PVG_FT_Outline* outline = (const PVG_FT_Outline*)params->source; - if ( outline == NULL ) - return ErrRaster_Invalid_Outline; - - /* return immediately if the outline is empty */ - if ( outline->n_points == 0 || outline->n_contours <= 0 ) - return 0; - - if ( !outline->contours || !outline->points ) - return ErrRaster_Invalid_Outline; - - if ( outline->n_points != - outline->contours[outline->n_contours - 1] + 1 ) - return ErrRaster_Invalid_Outline; - - /* this version does not support monochrome rendering */ - if ( !( params->flags & PVG_FT_RASTER_FLAG_AA ) ) - return ErrRaster_Invalid_Mode; - - if ( !( params->flags & PVG_FT_RASTER_FLAG_DIRECT ) ) - return ErrRaster_Invalid_Mode; - - /* compute clipping box */ - if ( params->flags & PVG_FT_RASTER_FLAG_CLIP ) - { - ras.clip_box = params->clip_box; - } - else - { - ras.clip_box.xMin = -(1 << 23); - ras.clip_box.yMin = -(1 << 23); - ras.clip_box.xMax = (1 << 23) - 1; - ras.clip_box.yMax = (1 << 23) - 1; - } - - gray_init_cells( RAS_VAR_ buffer, buffer_size ); - - ras.outline = *outline; - ras.num_cells = 0; - ras.invalid = 1; - ras.band_size = (int)(buffer_size / (long)(sizeof(TCell) * 8)); - - ras.render_span = (PVG_FT_Raster_Span_Func)params->gray_spans; - ras.render_span_data = params->user; - - return gray_convert_glyph( RAS_VAR ); - } - - void - PVG_FT_Raster_Render(const PVG_FT_Raster_Params *params) - { - char stack[PVG_FT_MINIMUM_POOL_SIZE]; - size_t length = PVG_FT_MINIMUM_POOL_SIZE; - - TWorker worker; - worker.skip_spans = 0; - int rendered_spans = 0; - int error = gray_raster_render(&worker, stack, length, params); - while(error == ErrRaster_OutOfMemory) { - if(worker.skip_spans < 0) - rendered_spans += -worker.skip_spans; - worker.skip_spans = rendered_spans; - length *= 2; - void* heap = malloc(length); - error = gray_raster_render(&worker, heap, length, params); - free(heap); - } - } - -/* END */ diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.h b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.h deleted file mode 100644 index e83ccc6..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.h +++ /dev/null @@ -1,420 +0,0 @@ -/***************************************************************************/ -/* */ -/* 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" - -/*************************************************************************/ -/* */ -/* */ -/* FT_BBox */ -/* */ -/* */ -/* A structure used to hold an outline's bounding box, i.e., the */ -/* coordinates of its extrema in the horizontal and vertical */ -/* directions. */ -/* */ -/* */ -/* xMin :: The horizontal minimum (left-most). */ -/* */ -/* yMin :: The vertical minimum (bottom-most). */ -/* */ -/* xMax :: The horizontal maximum (right-most). */ -/* */ -/* yMax :: The vertical maximum (top-most). */ -/* */ -/* */ -/* 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; - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Outline */ -/* */ -/* */ -/* This structure is used to describe an outline to the scan-line */ -/* converter. */ -/* */ -/* */ -/* 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; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_OUTLINE_FLAGS */ -/* */ -/* */ -/* A list of bit-field constants use for the flags in an outline's */ -/* `flags' field. */ -/* */ -/* */ -/* 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 - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Outline_Check */ -/* */ -/* */ -/* Check the contents of an outline descriptor. */ -/* */ -/* */ -/* outline :: A handle to a source outline. */ -/* */ -/* */ -/* FreeType error code. 0~means success. */ -/* */ -PVG_FT_Error -PVG_FT_Outline_Check( PVG_FT_Outline* outline ); - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Outline_Get_CBox */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* outline :: A pointer to the source outline descriptor. */ -/* */ -/* */ -/* acbox :: The outline's control box. */ -/* */ -/* */ -/* 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 ); - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Span */ -/* */ -/* */ -/* A structure used to model a single span of gray (or black) pixels */ -/* when rendering a monochrome or anti-aliased bitmap. */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* 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; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_SpanFunc */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* 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. */ -/* */ -/* */ -/* 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 - - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_RASTER_FLAG_XXX */ -/* */ -/* */ -/* A list of bit flag constants as used in the `flags' field of a */ -/* @PVG_FT_Raster_Params structure. */ -/* */ -/* */ -/* 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 - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Raster_Params */ -/* */ -/* */ -/* A structure to hold the arguments used by a raster's render */ -/* function. */ -/* */ -/* */ -/* 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). */ -/* */ -/* */ -/* 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 diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c b/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c deleted file mode 100644 index b20cfea..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c +++ /dev/null @@ -1,1873 +0,0 @@ - -/***************************************************************************/ -/* */ -/* ftstroke.c */ -/* */ -/* FreeType path stroker (body). */ -/* */ -/* Copyright 2002-2006, 2008-2011, 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-stroker.h" -#include "plutovg-ft-math.h" - -#include -#include -#include - -/*************************************************************************/ -/*************************************************************************/ -/***** *****/ -/***** BEZIER COMPUTATIONS *****/ -/***** *****/ -/*************************************************************************/ -/*************************************************************************/ - -#define PVG_FT_SMALL_CONIC_THRESHOLD (PVG_FT_ANGLE_PI / 6) -#define PVG_FT_SMALL_CUBIC_THRESHOLD (PVG_FT_ANGLE_PI / 8) - -#define PVG_FT_EPSILON 2 - -#define PVG_FT_IS_SMALL(x) ((x) > -PVG_FT_EPSILON && (x) < PVG_FT_EPSILON) - -static PVG_FT_Pos ft_pos_abs(PVG_FT_Pos x) -{ - return x >= 0 ? x : -x; -} - -static void ft_conic_split(PVG_FT_Vector* base) -{ - PVG_FT_Pos a, b; - - base[4].x = base[2].x; - a = base[0].x + base[1].x; - b = base[1].x + base[2].x; - base[3].x = b >> 1; - base[2].x = ( a + b ) >> 2; - base[1].x = a >> 1; - - base[4].y = base[2].y; - a = base[0].y + base[1].y; - b = base[1].y + base[2].y; - base[3].y = b >> 1; - base[2].y = ( a + b ) >> 2; - base[1].y = a >> 1; -} - -static PVG_FT_Bool ft_conic_is_small_enough(PVG_FT_Vector* base, - PVG_FT_Angle* angle_in, - PVG_FT_Angle* angle_out) -{ - PVG_FT_Vector d1, d2; - PVG_FT_Angle theta; - PVG_FT_Int close1, close2; - - d1.x = base[1].x - base[2].x; - d1.y = base[1].y - base[2].y; - d2.x = base[0].x - base[1].x; - d2.y = base[0].y - base[1].y; - - close1 = PVG_FT_IS_SMALL(d1.x) && PVG_FT_IS_SMALL(d1.y); - close2 = PVG_FT_IS_SMALL(d2.x) && PVG_FT_IS_SMALL(d2.y); - - if (close1) { - if (close2) { - /* basically a point; */ - /* do nothing to retain original direction */ - } else { - *angle_in = *angle_out = PVG_FT_Atan2(d2.x, d2.y); - } - } else /* !close1 */ - { - if (close2) { - *angle_in = *angle_out = PVG_FT_Atan2(d1.x, d1.y); - } else { - *angle_in = PVG_FT_Atan2(d1.x, d1.y); - *angle_out = PVG_FT_Atan2(d2.x, d2.y); - } - } - - theta = ft_pos_abs(PVG_FT_Angle_Diff(*angle_in, *angle_out)); - - return PVG_FT_BOOL(theta < PVG_FT_SMALL_CONIC_THRESHOLD); -} - -static void ft_cubic_split(PVG_FT_Vector* base) -{ - PVG_FT_Pos a, b, c; - - base[6].x = base[3].x; - a = base[0].x + base[1].x; - b = base[1].x + base[2].x; - c = base[2].x + base[3].x; - base[5].x = c >> 1; - c += b; - base[4].x = c >> 2; - base[1].x = a >> 1; - a += b; - base[2].x = a >> 2; - base[3].x = ( a + c ) >> 3; - - base[6].y = base[3].y; - a = base[0].y + base[1].y; - b = base[1].y + base[2].y; - c = base[2].y + base[3].y; - base[5].y = c >> 1; - c += b; - base[4].y = c >> 2; - base[1].y = a >> 1; - a += b; - base[2].y = a >> 2; - base[3].y = ( a + c ) >> 3; -} - -/* Return the average of `angle1' and `angle2'. */ -/* This gives correct result even if `angle1' and `angle2' */ -/* have opposite signs. */ -static PVG_FT_Angle ft_angle_mean(PVG_FT_Angle angle1, PVG_FT_Angle angle2) -{ - return angle1 + PVG_FT_Angle_Diff(angle1, angle2) / 2; -} - -static PVG_FT_Bool ft_cubic_is_small_enough(PVG_FT_Vector* base, - PVG_FT_Angle* angle_in, - PVG_FT_Angle* angle_mid, - PVG_FT_Angle* angle_out) -{ - PVG_FT_Vector d1, d2, d3; - PVG_FT_Angle theta1, theta2; - PVG_FT_Int close1, close2, close3; - - d1.x = base[2].x - base[3].x; - d1.y = base[2].y - base[3].y; - d2.x = base[1].x - base[2].x; - d2.y = base[1].y - base[2].y; - d3.x = base[0].x - base[1].x; - d3.y = base[0].y - base[1].y; - - close1 = PVG_FT_IS_SMALL(d1.x) && PVG_FT_IS_SMALL(d1.y); - close2 = PVG_FT_IS_SMALL(d2.x) && PVG_FT_IS_SMALL(d2.y); - close3 = PVG_FT_IS_SMALL(d3.x) && PVG_FT_IS_SMALL(d3.y); - - if (close1) { - if (close2) { - if (close3) { - /* basically a point; */ - /* do nothing to retain original direction */ - } else /* !close3 */ - { - *angle_in = *angle_mid = *angle_out = PVG_FT_Atan2(d3.x, d3.y); - } - } else /* !close2 */ - { - if (close3) { - *angle_in = *angle_mid = *angle_out = PVG_FT_Atan2(d2.x, d2.y); - } else /* !close3 */ - { - *angle_in = *angle_mid = PVG_FT_Atan2(d2.x, d2.y); - *angle_out = PVG_FT_Atan2(d3.x, d3.y); - } - } - } else /* !close1 */ - { - if (close2) { - if (close3) { - *angle_in = *angle_mid = *angle_out = PVG_FT_Atan2(d1.x, d1.y); - } else /* !close3 */ - { - *angle_in = PVG_FT_Atan2(d1.x, d1.y); - *angle_out = PVG_FT_Atan2(d3.x, d3.y); - *angle_mid = ft_angle_mean(*angle_in, *angle_out); - } - } else /* !close2 */ - { - if (close3) { - *angle_in = PVG_FT_Atan2(d1.x, d1.y); - *angle_mid = *angle_out = PVG_FT_Atan2(d2.x, d2.y); - } else /* !close3 */ - { - *angle_in = PVG_FT_Atan2(d1.x, d1.y); - *angle_mid = PVG_FT_Atan2(d2.x, d2.y); - *angle_out = PVG_FT_Atan2(d3.x, d3.y); - } - } - } - - theta1 = ft_pos_abs(PVG_FT_Angle_Diff(*angle_in, *angle_mid)); - theta2 = ft_pos_abs(PVG_FT_Angle_Diff(*angle_mid, *angle_out)); - - return PVG_FT_BOOL(theta1 < PVG_FT_SMALL_CUBIC_THRESHOLD && - theta2 < PVG_FT_SMALL_CUBIC_THRESHOLD); -} - -/*************************************************************************/ -/*************************************************************************/ -/***** *****/ -/***** STROKE BORDERS *****/ -/***** *****/ -/*************************************************************************/ -/*************************************************************************/ - -typedef enum PVG_FT_StrokeTags_ { - PVG_FT_STROKE_TAG_ON = 1, /* on-curve point */ - PVG_FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ - PVG_FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ - PVG_FT_STROKE_TAG_END = 8 /* sub-path end */ - -} PVG_FT_StrokeTags; - -#define PVG_FT_STROKE_TAG_BEGIN_END \ - (PVG_FT_STROKE_TAG_BEGIN | PVG_FT_STROKE_TAG_END) - -typedef struct PVG_FT_StrokeBorderRec_ { - PVG_FT_UInt num_points; - PVG_FT_UInt max_points; - PVG_FT_Vector* points; - PVG_FT_Byte* tags; - PVG_FT_Bool movable; /* TRUE for ends of lineto borders */ - PVG_FT_Int start; /* index of current sub-path start point */ - PVG_FT_Bool valid; - -} PVG_FT_StrokeBorderRec, *PVG_FT_StrokeBorder; - -static PVG_FT_Error ft_stroke_border_grow(PVG_FT_StrokeBorder border, - PVG_FT_UInt new_points) -{ - PVG_FT_UInt old_max = border->max_points; - PVG_FT_UInt new_max = border->num_points + new_points; - PVG_FT_Error error = 0; - - if (new_max > old_max) { - PVG_FT_UInt cur_max = old_max; - - while (cur_max < new_max) cur_max += (cur_max >> 1) + 16; - - border->points = (PVG_FT_Vector*)realloc(border->points, - cur_max * sizeof(PVG_FT_Vector)); - border->tags = - (PVG_FT_Byte*)realloc(border->tags, cur_max * sizeof(PVG_FT_Byte)); - - if (!border->points || !border->tags) goto Exit; - - border->max_points = cur_max; - } - -Exit: - return error; -} - -static void ft_stroke_border_close(PVG_FT_StrokeBorder border, - PVG_FT_Bool reverse) -{ - PVG_FT_UInt start = border->start; - PVG_FT_UInt count = border->num_points; - - assert(border->start >= 0); - - /* don't record empty paths! */ - if (count <= start + 1U) - border->num_points = start; - else { - /* copy the last point to the start of this sub-path, since */ - /* it contains the `adjusted' starting coordinates */ - border->num_points = --count; - border->points[start] = border->points[count]; - border->tags[start] = border->tags[count]; - - if (reverse) { - /* reverse the points */ - { - PVG_FT_Vector* vec1 = border->points + start + 1; - PVG_FT_Vector* vec2 = border->points + count - 1; - - for (; vec1 < vec2; vec1++, vec2--) { - PVG_FT_Vector tmp; - - tmp = *vec1; - *vec1 = *vec2; - *vec2 = tmp; - } - } - - /* then the tags */ - { - PVG_FT_Byte* tag1 = border->tags + start + 1; - PVG_FT_Byte* tag2 = border->tags + count - 1; - - for (; tag1 < tag2; tag1++, tag2--) { - PVG_FT_Byte tmp; - - tmp = *tag1; - *tag1 = *tag2; - *tag2 = tmp; - } - } - } - - border->tags[start] |= PVG_FT_STROKE_TAG_BEGIN; - border->tags[count - 1] |= PVG_FT_STROKE_TAG_END; - } - - border->start = -1; - border->movable = FALSE; -} - -static PVG_FT_Error ft_stroke_border_lineto(PVG_FT_StrokeBorder border, - PVG_FT_Vector* to, PVG_FT_Bool movable) -{ - PVG_FT_Error error = 0; - - assert(border->start >= 0); - - if (border->movable) { - /* move last point */ - border->points[border->num_points - 1] = *to; - } else { - /* don't add zero-length lineto, but always add moveto */ - if (border->num_points > border->start && - PVG_FT_IS_SMALL(border->points[border->num_points - 1].x - to->x) && - PVG_FT_IS_SMALL(border->points[border->num_points - 1].y - to->y)) - return error; - - /* add one point */ - error = ft_stroke_border_grow(border, 1); - if (!error) { - PVG_FT_Vector* vec = border->points + border->num_points; - PVG_FT_Byte* tag = border->tags + border->num_points; - - vec[0] = *to; - tag[0] = PVG_FT_STROKE_TAG_ON; - - border->num_points += 1; - } - } - border->movable = movable; - return error; -} - -static PVG_FT_Error ft_stroke_border_conicto(PVG_FT_StrokeBorder border, - PVG_FT_Vector* control, - PVG_FT_Vector* to) -{ - PVG_FT_Error error; - - assert(border->start >= 0); - - error = ft_stroke_border_grow(border, 2); - if (!error) { - PVG_FT_Vector* vec = border->points + border->num_points; - PVG_FT_Byte* tag = border->tags + border->num_points; - - vec[0] = *control; - vec[1] = *to; - - tag[0] = 0; - tag[1] = PVG_FT_STROKE_TAG_ON; - - border->num_points += 2; - } - - border->movable = FALSE; - - return error; -} - -static PVG_FT_Error ft_stroke_border_cubicto(PVG_FT_StrokeBorder border, - PVG_FT_Vector* control1, - PVG_FT_Vector* control2, - PVG_FT_Vector* to) -{ - PVG_FT_Error error; - - assert(border->start >= 0); - - error = ft_stroke_border_grow(border, 3); - if (!error) { - PVG_FT_Vector* vec = border->points + border->num_points; - PVG_FT_Byte* tag = border->tags + border->num_points; - - vec[0] = *control1; - vec[1] = *control2; - vec[2] = *to; - - tag[0] = PVG_FT_STROKE_TAG_CUBIC; - tag[1] = PVG_FT_STROKE_TAG_CUBIC; - tag[2] = PVG_FT_STROKE_TAG_ON; - - border->num_points += 3; - } - - border->movable = FALSE; - - return error; -} - -#define PVG_FT_ARC_CUBIC_ANGLE (PVG_FT_ANGLE_PI / 2) - - -static PVG_FT_Error -ft_stroke_border_arcto( PVG_FT_StrokeBorder border, - PVG_FT_Vector* center, - PVG_FT_Fixed radius, - PVG_FT_Angle angle_start, - PVG_FT_Angle angle_diff ) -{ - PVG_FT_Fixed coef; - PVG_FT_Vector a0, a1, a2, a3; - PVG_FT_Int i, arcs = 1; - PVG_FT_Error error = 0; - - - /* number of cubic arcs to draw */ - while ( angle_diff > PVG_FT_ARC_CUBIC_ANGLE * arcs || - -angle_diff > PVG_FT_ARC_CUBIC_ANGLE * arcs ) - arcs++; - - /* control tangents */ - coef = PVG_FT_Tan( angle_diff / ( 4 * arcs ) ); - coef += coef / 3; - - /* compute start and first control point */ - PVG_FT_Vector_From_Polar( &a0, radius, angle_start ); - a1.x = PVG_FT_MulFix( -a0.y, coef ); - a1.y = PVG_FT_MulFix( a0.x, coef ); - - a0.x += center->x; - a0.y += center->y; - a1.x += a0.x; - a1.y += a0.y; - - for ( i = 1; i <= arcs; i++ ) - { - /* compute end and second control point */ - PVG_FT_Vector_From_Polar( &a3, radius, - angle_start + i * angle_diff / arcs ); - a2.x = PVG_FT_MulFix( a3.y, coef ); - a2.y = PVG_FT_MulFix( -a3.x, coef ); - - a3.x += center->x; - a3.y += center->y; - a2.x += a3.x; - a2.y += a3.y; - - /* add cubic arc */ - error = ft_stroke_border_cubicto( border, &a1, &a2, &a3 ); - if ( error ) - break; - - /* a0 = a3; */ - a1.x = a3.x - a2.x + a3.x; - a1.y = a3.y - a2.y + a3.y; - } - - return error; -} - -static PVG_FT_Error ft_stroke_border_moveto(PVG_FT_StrokeBorder border, - PVG_FT_Vector* to) -{ - /* close current open path if any ? */ - if (border->start >= 0) ft_stroke_border_close(border, FALSE); - - border->start = border->num_points; - border->movable = FALSE; - - return ft_stroke_border_lineto(border, to, FALSE); -} - -static void ft_stroke_border_init(PVG_FT_StrokeBorder border) -{ - border->points = NULL; - border->tags = NULL; - - border->num_points = 0; - border->max_points = 0; - border->start = -1; - border->valid = FALSE; -} - -static void ft_stroke_border_reset(PVG_FT_StrokeBorder border) -{ - border->num_points = 0; - border->start = -1; - border->valid = FALSE; -} - -static void ft_stroke_border_done(PVG_FT_StrokeBorder border) -{ - free(border->points); - free(border->tags); - - border->num_points = 0; - border->max_points = 0; - border->start = -1; - border->valid = FALSE; -} - -static PVG_FT_Error ft_stroke_border_get_counts(PVG_FT_StrokeBorder border, - PVG_FT_UInt* anum_points, - PVG_FT_UInt* anum_contours) -{ - PVG_FT_Error error = 0; - PVG_FT_UInt num_points = 0; - PVG_FT_UInt num_contours = 0; - - PVG_FT_UInt count = border->num_points; - PVG_FT_Vector* point = border->points; - PVG_FT_Byte* tags = border->tags; - PVG_FT_Int in_contour = 0; - - for (; count > 0; count--, num_points++, point++, tags++) { - if (tags[0] & PVG_FT_STROKE_TAG_BEGIN) { - if (in_contour != 0) goto Fail; - - in_contour = 1; - } else if (in_contour == 0) - goto Fail; - - if (tags[0] & PVG_FT_STROKE_TAG_END) { - in_contour = 0; - num_contours++; - } - } - - if (in_contour != 0) goto Fail; - - border->valid = TRUE; - -Exit: - *anum_points = num_points; - *anum_contours = num_contours; - return error; - -Fail: - num_points = 0; - num_contours = 0; - goto Exit; -} - -static void ft_stroke_border_export(PVG_FT_StrokeBorder border, - PVG_FT_Outline* outline) -{ - /* copy point locations */ - memcpy(outline->points + outline->n_points, border->points, - border->num_points * sizeof(PVG_FT_Vector)); - - /* copy tags */ - { - PVG_FT_UInt count = border->num_points; - PVG_FT_Byte* read = border->tags; - PVG_FT_Byte* write = (PVG_FT_Byte*)outline->tags + outline->n_points; - - for (; count > 0; count--, read++, write++) { - if (*read & PVG_FT_STROKE_TAG_ON) - *write = PVG_FT_CURVE_TAG_ON; - else if (*read & PVG_FT_STROKE_TAG_CUBIC) - *write = PVG_FT_CURVE_TAG_CUBIC; - else - *write = PVG_FT_CURVE_TAG_CONIC; - } - } - - /* copy contours */ - { - PVG_FT_UInt count = border->num_points; - PVG_FT_Byte* tags = border->tags; - PVG_FT_Int* write = outline->contours + outline->n_contours; - PVG_FT_Int idx = (PVG_FT_Int)outline->n_points; - - for (; count > 0; count--, tags++, idx++) { - if (*tags & PVG_FT_STROKE_TAG_END) { - *write++ = idx; - outline->n_contours++; - } - } - } - - outline->n_points = (int)(outline->n_points + border->num_points); - - assert(PVG_FT_Outline_Check(outline) == 0); -} - -/*************************************************************************/ -/*************************************************************************/ -/***** *****/ -/***** STROKER *****/ -/***** *****/ -/*************************************************************************/ -/*************************************************************************/ - -#define PVG_FT_SIDE_TO_ROTATE(s) (PVG_FT_ANGLE_PI2 - (s)*PVG_FT_ANGLE_PI) - -typedef struct PVG_FT_StrokerRec_ { - PVG_FT_Angle angle_in; /* direction into curr join */ - PVG_FT_Angle angle_out; /* direction out of join */ - PVG_FT_Vector center; /* current position */ - PVG_FT_Fixed line_length; /* length of last lineto */ - PVG_FT_Bool first_point; /* is this the start? */ - PVG_FT_Bool subpath_open; /* is the subpath open? */ - PVG_FT_Angle subpath_angle; /* subpath start direction */ - PVG_FT_Vector subpath_start; /* subpath start position */ - PVG_FT_Fixed subpath_line_length; /* subpath start lineto len */ - PVG_FT_Bool handle_wide_strokes; /* use wide strokes logic? */ - - PVG_FT_Stroker_LineCap line_cap; - PVG_FT_Stroker_LineJoin line_join; - PVG_FT_Stroker_LineJoin line_join_saved; - PVG_FT_Fixed miter_limit; - PVG_FT_Fixed radius; - - PVG_FT_StrokeBorderRec borders[2]; -} PVG_FT_StrokerRec; - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_New(PVG_FT_Stroker* astroker) -{ - PVG_FT_Error error = 0; /* assigned in PVG_FT_NEW */ - PVG_FT_Stroker stroker = NULL; - - stroker = (PVG_FT_StrokerRec*)calloc(1, sizeof(PVG_FT_StrokerRec)); - if (stroker) { - ft_stroke_border_init(&stroker->borders[0]); - ft_stroke_border_init(&stroker->borders[1]); - } - - *astroker = stroker; - - return error; -} - -void PVG_FT_Stroker_Rewind(PVG_FT_Stroker stroker) -{ - if (stroker) { - ft_stroke_border_reset(&stroker->borders[0]); - ft_stroke_border_reset(&stroker->borders[1]); - } -} - -/* documentation is in ftstroke.h */ - -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) -{ - stroker->radius = radius; - stroker->line_cap = line_cap; - stroker->line_join = line_join; - stroker->miter_limit = miter_limit; - - /* ensure miter limit has sensible value */ - if (stroker->miter_limit < 0x10000) stroker->miter_limit = 0x10000; - - /* save line join style: */ - /* line join style can be temporarily changed when stroking curves */ - stroker->line_join_saved = line_join; - - PVG_FT_Stroker_Rewind(stroker); -} - -/* documentation is in ftstroke.h */ - -void PVG_FT_Stroker_Done(PVG_FT_Stroker stroker) -{ - if (stroker) { - ft_stroke_border_done(&stroker->borders[0]); - ft_stroke_border_done(&stroker->borders[1]); - - free(stroker); - } -} - -/* create a circular arc at a corner or cap */ -static PVG_FT_Error ft_stroker_arcto(PVG_FT_Stroker stroker, PVG_FT_Int side) -{ - PVG_FT_Angle total, rotate; - PVG_FT_Fixed radius = stroker->radius; - PVG_FT_Error error = 0; - PVG_FT_StrokeBorder border = stroker->borders + side; - - rotate = PVG_FT_SIDE_TO_ROTATE(side); - - total = PVG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); - if (total == PVG_FT_ANGLE_PI) total = -rotate * 2; - - error = ft_stroke_border_arcto(border, &stroker->center, radius, - stroker->angle_in + rotate, total); - border->movable = FALSE; - return error; -} - -/* add a cap at the end of an opened path */ -static PVG_FT_Error -ft_stroker_cap(PVG_FT_Stroker stroker, - PVG_FT_Angle angle, - PVG_FT_Int side) -{ - PVG_FT_Error error = 0; - - if (stroker->line_cap == PVG_FT_STROKER_LINECAP_ROUND) - { - /* add a round cap */ - stroker->angle_in = angle; - stroker->angle_out = angle + PVG_FT_ANGLE_PI; - - error = ft_stroker_arcto(stroker, side); - } - else - { - /* add a square or butt cap */ - PVG_FT_Vector middle, delta; - PVG_FT_Fixed radius = stroker->radius; - PVG_FT_StrokeBorder border = stroker->borders + side; - - /* compute middle point and first angle point */ - PVG_FT_Vector_From_Polar( &middle, radius, angle ); - delta.x = side ? middle.y : -middle.y; - delta.y = side ? -middle.x : middle.x; - - if ( stroker->line_cap == PVG_FT_STROKER_LINECAP_SQUARE ) - { - middle.x += stroker->center.x; - middle.y += stroker->center.y; - } - else /* PVG_FT_STROKER_LINECAP_BUTT */ - { - middle.x = stroker->center.x; - middle.y = stroker->center.y; - } - - delta.x += middle.x; - delta.y += middle.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - if ( error ) - goto Exit; - - /* compute second angle point */ - delta.x = middle.x - delta.x + middle.x; - delta.y = middle.y - delta.y + middle.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - } - -Exit: - return error; -} - -/* process an inside corner, i.e. compute intersection */ -static PVG_FT_Error ft_stroker_inside(PVG_FT_Stroker stroker, PVG_FT_Int side, - PVG_FT_Fixed line_length) -{ - PVG_FT_StrokeBorder border = stroker->borders + side; - PVG_FT_Angle phi, theta, rotate; - PVG_FT_Fixed length; - PVG_FT_Vector sigma = {0, 0}; - PVG_FT_Vector delta; - PVG_FT_Error error = 0; - PVG_FT_Bool intersect; /* use intersection of lines? */ - - rotate = PVG_FT_SIDE_TO_ROTATE(side); - - theta = PVG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out) / 2; - - /* Only intersect borders if between two lineto's and both */ - /* lines are long enough (line_length is zero for curves). */ - if (!border->movable || line_length == 0 || - theta > 0x59C000 || theta < -0x59C000 ) - intersect = FALSE; - else { - /* compute minimum required length of lines */ - PVG_FT_Fixed min_length; - - - PVG_FT_Vector_Unit( &sigma, theta ); - min_length = - ft_pos_abs( PVG_FT_MulDiv( stroker->radius, sigma.y, sigma.x ) ); - - intersect = PVG_FT_BOOL( min_length && - stroker->line_length >= min_length && - line_length >= min_length ); - } - - if (!intersect) { - PVG_FT_Vector_From_Polar(&delta, stroker->radius, - stroker->angle_out + rotate); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - border->movable = FALSE; - } else { - /* compute median angle */ - phi = stroker->angle_in + theta + rotate; - - length = PVG_FT_DivFix( stroker->radius, sigma.x ); - - PVG_FT_Vector_From_Polar( &delta, length, phi ); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - } - - error = ft_stroke_border_lineto(border, &delta, FALSE); - - return error; -} - - /* process an outside corner, i.e. compute bevel/miter/round */ -static PVG_FT_Error -ft_stroker_outside( PVG_FT_Stroker stroker, - PVG_FT_Int side, - PVG_FT_Fixed line_length ) -{ - PVG_FT_StrokeBorder border = stroker->borders + side; - PVG_FT_Error error; - PVG_FT_Angle rotate; - - - if ( stroker->line_join == PVG_FT_STROKER_LINEJOIN_ROUND ) - error = ft_stroker_arcto( stroker, side ); - else - { - /* this is a mitered (pointed) or beveled (truncated) corner */ - PVG_FT_Fixed radius = stroker->radius; - PVG_FT_Vector sigma = {0, 0}; - PVG_FT_Angle theta = 0, phi = 0; - PVG_FT_Bool bevel, fixed_bevel; - - - rotate = PVG_FT_SIDE_TO_ROTATE( side ); - - bevel = - PVG_FT_BOOL( stroker->line_join == PVG_FT_STROKER_LINEJOIN_BEVEL ); - - fixed_bevel = - PVG_FT_BOOL( stroker->line_join != PVG_FT_STROKER_LINEJOIN_MITER_VARIABLE ); - - /* check miter limit first */ - if ( !bevel ) - { - theta = PVG_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; - - if ( theta == PVG_FT_ANGLE_PI2 ) - theta = -rotate; - - phi = stroker->angle_in + theta + rotate; - - PVG_FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta ); - - /* is miter limit exceeded? */ - if ( sigma.x < 0x10000L ) - { - /* don't create variable bevels for very small deviations; */ - /* FT_Sin(x) = 0 for x <= 57 */ - if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) - bevel = TRUE; - } - } - - if ( bevel ) /* this is a bevel (broken angle) */ - { - if ( fixed_bevel ) - { - /* the outer corners are simply joined together */ - PVG_FT_Vector delta; - - - /* add bevel */ - PVG_FT_Vector_From_Polar( &delta, - radius, - stroker->angle_out + rotate ); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - border->movable = FALSE; - error = ft_stroke_border_lineto( border, &delta, FALSE ); - } - else /* variable bevel or clipped miter */ - { - /* the miter is truncated */ - PVG_FT_Vector middle, delta; - PVG_FT_Fixed coef; - - - /* compute middle point and first angle point */ - PVG_FT_Vector_From_Polar( &middle, - PVG_FT_MulFix( radius, stroker->miter_limit ), - phi ); - - coef = PVG_FT_DivFix( 0x10000L - sigma.x, sigma.y ); - delta.x = PVG_FT_MulFix( middle.y, coef ); - delta.y = PVG_FT_MulFix( -middle.x, coef ); - - middle.x += stroker->center.x; - middle.y += stroker->center.y; - delta.x += middle.x; - delta.y += middle.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - if ( error ) - goto Exit; - - /* compute second angle point */ - delta.x = middle.x - delta.x + middle.x; - delta.y = middle.y - delta.y + middle.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - if ( error ) - goto Exit; - - /* finally, add an end point; only needed if not lineto */ - /* (line_length is zero for curves) */ - if ( line_length == 0 ) - { - PVG_FT_Vector_From_Polar( &delta, - radius, - stroker->angle_out + rotate ); - - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - } - } - } - else /* this is a miter (intersection) */ - { - PVG_FT_Fixed length; - PVG_FT_Vector delta; - - - length = PVG_FT_MulDiv( stroker->radius, stroker->miter_limit, sigma.x ); - - PVG_FT_Vector_From_Polar( &delta, length, phi ); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - if ( error ) - goto Exit; - - /* now add an end point; only needed if not lineto */ - /* (line_length is zero for curves) */ - if ( line_length == 0 ) - { - PVG_FT_Vector_From_Polar( &delta, - stroker->radius, - stroker->angle_out + rotate ); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - error = ft_stroke_border_lineto( border, &delta, FALSE ); - } - } - } - - Exit: - return error; -} - -static PVG_FT_Error ft_stroker_process_corner(PVG_FT_Stroker stroker, - PVG_FT_Fixed line_length) -{ - PVG_FT_Error error = 0; - PVG_FT_Angle turn; - PVG_FT_Int inside_side; - - turn = PVG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); - - /* no specific corner processing is required if the turn is 0 */ - if (turn == 0) goto Exit; - - /* when we turn to the right, the inside side is 0 */ - inside_side = 0; - - /* otherwise, the inside side is 1 */ - if (turn < 0) inside_side = 1; - - /* process the inside side */ - error = ft_stroker_inside(stroker, inside_side, line_length); - if (error) goto Exit; - - /* process the outside side */ - error = ft_stroker_outside(stroker, 1 - inside_side, line_length); - -Exit: - return error; -} - -/* add two points to the left and right borders corresponding to the */ -/* start of the subpath */ -static PVG_FT_Error ft_stroker_subpath_start(PVG_FT_Stroker stroker, - PVG_FT_Angle start_angle, - PVG_FT_Fixed line_length) -{ - PVG_FT_Vector delta; - PVG_FT_Vector point; - PVG_FT_Error error; - PVG_FT_StrokeBorder border; - - PVG_FT_Vector_From_Polar(&delta, stroker->radius, - start_angle + PVG_FT_ANGLE_PI2); - - point.x = stroker->center.x + delta.x; - point.y = stroker->center.y + delta.y; - - border = stroker->borders; - error = ft_stroke_border_moveto(border, &point); - if (error) goto Exit; - - point.x = stroker->center.x - delta.x; - point.y = stroker->center.y - delta.y; - - border++; - error = ft_stroke_border_moveto(border, &point); - - /* save angle, position, and line length for last join */ - /* (line_length is zero for curves) */ - stroker->subpath_angle = start_angle; - stroker->first_point = FALSE; - stroker->subpath_line_length = line_length; - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_LineTo(PVG_FT_Stroker stroker, PVG_FT_Vector* to) -{ - PVG_FT_Error error = 0; - PVG_FT_StrokeBorder border; - PVG_FT_Vector delta; - PVG_FT_Angle angle; - PVG_FT_Int side; - PVG_FT_Fixed line_length; - - delta.x = to->x - stroker->center.x; - delta.y = to->y - stroker->center.y; - - /* a zero-length lineto is a no-op; avoid creating a spurious corner */ - if (delta.x == 0 && delta.y == 0) goto Exit; - - /* compute length of line */ - line_length = PVG_FT_Vector_Length(&delta); - - angle = PVG_FT_Atan2(delta.x, delta.y); - PVG_FT_Vector_From_Polar(&delta, stroker->radius, angle + PVG_FT_ANGLE_PI2); - - /* process corner if necessary */ - if (stroker->first_point) { - /* This is the first segment of a subpath. We need to */ - /* add a point to each border at their respective starting */ - /* point locations. */ - error = ft_stroker_subpath_start(stroker, angle, line_length); - if (error) goto Exit; - } else { - /* process the current corner */ - stroker->angle_out = angle; - error = ft_stroker_process_corner(stroker, line_length); - if (error) goto Exit; - } - - /* now add a line segment to both the `inside' and `outside' paths */ - for (border = stroker->borders, side = 1; side >= 0; side--, border++) { - PVG_FT_Vector point; - - point.x = to->x + delta.x; - point.y = to->y + delta.y; - - /* the ends of lineto borders are movable */ - error = ft_stroke_border_lineto(border, &point, TRUE); - if (error) goto Exit; - - delta.x = -delta.x; - delta.y = -delta.y; - } - - stroker->angle_in = angle; - stroker->center = *to; - stroker->line_length = line_length; - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_ConicTo(PVG_FT_Stroker stroker, PVG_FT_Vector* control, - PVG_FT_Vector* to) -{ - PVG_FT_Error error = 0; - PVG_FT_Vector bez_stack[34]; - PVG_FT_Vector* arc; - PVG_FT_Vector* limit = bez_stack + 30; - PVG_FT_Bool first_arc = TRUE; - - /* if all control points are coincident, this is a no-op; */ - /* avoid creating a spurious corner */ - if (PVG_FT_IS_SMALL(stroker->center.x - control->x) && - PVG_FT_IS_SMALL(stroker->center.y - control->y) && - PVG_FT_IS_SMALL(control->x - to->x) && - PVG_FT_IS_SMALL(control->y - to->y)) { - stroker->center = *to; - goto Exit; - } - - arc = bez_stack; - arc[0] = *to; - arc[1] = *control; - arc[2] = stroker->center; - - while (arc >= bez_stack) { - PVG_FT_Angle angle_in, angle_out; - - /* initialize with current direction */ - angle_in = angle_out = stroker->angle_in; - - if (arc < limit && - !ft_conic_is_small_enough(arc, &angle_in, &angle_out)) { - if (stroker->first_point) stroker->angle_in = angle_in; - - ft_conic_split(arc); - arc += 2; - continue; - } - - if (first_arc) { - first_arc = FALSE; - - /* process corner if necessary */ - if (stroker->first_point) - error = ft_stroker_subpath_start(stroker, angle_in, 0); - else { - stroker->angle_out = angle_in; - error = ft_stroker_process_corner(stroker, 0); - } - } else if (ft_pos_abs(PVG_FT_Angle_Diff(stroker->angle_in, angle_in)) > - PVG_FT_SMALL_CONIC_THRESHOLD / 4) { - /* if the deviation from one arc to the next is too great, */ - /* add a round corner */ - stroker->center = arc[2]; - stroker->angle_out = angle_in; - stroker->line_join = PVG_FT_STROKER_LINEJOIN_ROUND; - - error = ft_stroker_process_corner(stroker, 0); - - /* reinstate line join style */ - stroker->line_join = stroker->line_join_saved; - } - - if (error) goto Exit; - - /* the arc's angle is small enough; we can add it directly to each */ - /* border */ - { - PVG_FT_Vector ctrl, end; - PVG_FT_Angle theta, phi, rotate, alpha0 = 0; - PVG_FT_Fixed length; - PVG_FT_StrokeBorder border; - PVG_FT_Int side; - - theta = PVG_FT_Angle_Diff(angle_in, angle_out) / 2; - phi = angle_in + theta; - length = PVG_FT_DivFix(stroker->radius, PVG_FT_Cos(theta)); - - /* compute direction of original arc */ - if (stroker->handle_wide_strokes) - alpha0 = PVG_FT_Atan2(arc[0].x - arc[2].x, arc[0].y - arc[2].y); - - for (border = stroker->borders, side = 0; side <= 1; - side++, border++) { - rotate = PVG_FT_SIDE_TO_ROTATE(side); - - /* compute control point */ - PVG_FT_Vector_From_Polar(&ctrl, length, phi + rotate); - ctrl.x += arc[1].x; - ctrl.y += arc[1].y; - - /* compute end point */ - PVG_FT_Vector_From_Polar(&end, stroker->radius, - angle_out + rotate); - end.x += arc[0].x; - end.y += arc[0].y; - - if (stroker->handle_wide_strokes) { - PVG_FT_Vector start; - PVG_FT_Angle alpha1; - - /* determine whether the border radius is greater than the - */ - /* radius of curvature of the original arc */ - start = border->points[border->num_points - 1]; - - alpha1 = PVG_FT_Atan2(end.x - start.x, end.y - start.y); - - /* is the direction of the border arc opposite to */ - /* that of the original arc? */ - if (ft_pos_abs(PVG_FT_Angle_Diff(alpha0, alpha1)) > - PVG_FT_ANGLE_PI / 2) { - PVG_FT_Angle beta, gamma; - PVG_FT_Vector bvec, delta; - PVG_FT_Fixed blen, sinA, sinB, alen; - - /* use the sine rule to find the intersection point */ - beta = - PVG_FT_Atan2(arc[2].x - start.x, arc[2].y - start.y); - gamma = PVG_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y); - - bvec.x = end.x - start.x; - bvec.y = end.y - start.y; - - blen = PVG_FT_Vector_Length(&bvec); - - sinA = ft_pos_abs(PVG_FT_Sin(alpha1 - gamma)); - sinB = ft_pos_abs(PVG_FT_Sin(beta - gamma)); - - alen = PVG_FT_MulDiv(blen, sinA, sinB); - - PVG_FT_Vector_From_Polar(&delta, alen, beta); - delta.x += start.x; - delta.y += start.y; - - /* circumnavigate the negative sector backwards */ - border->movable = FALSE; - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; - error = ft_stroke_border_lineto(border, &end, FALSE); - if (error) goto Exit; - error = ft_stroke_border_conicto(border, &ctrl, &start); - if (error) goto Exit; - /* and then move to the endpoint */ - error = ft_stroke_border_lineto(border, &end, FALSE); - if (error) goto Exit; - - continue; - } - - /* else fall through */ - } - - /* simply add an arc */ - error = ft_stroke_border_conicto(border, &ctrl, &end); - if (error) goto Exit; - } - } - - arc -= 2; - - stroker->angle_in = angle_out; - } - - stroker->center = *to; - stroker->line_length = 0; - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_CubicTo(PVG_FT_Stroker stroker, PVG_FT_Vector* control1, - PVG_FT_Vector* control2, PVG_FT_Vector* to) -{ - PVG_FT_Error error = 0; - PVG_FT_Vector bez_stack[37]; - PVG_FT_Vector* arc; - PVG_FT_Vector* limit = bez_stack + 32; - PVG_FT_Bool first_arc = TRUE; - - /* if all control points are coincident, this is a no-op; */ - /* avoid creating a spurious corner */ - if (PVG_FT_IS_SMALL(stroker->center.x - control1->x) && - PVG_FT_IS_SMALL(stroker->center.y - control1->y) && - PVG_FT_IS_SMALL(control1->x - control2->x) && - PVG_FT_IS_SMALL(control1->y - control2->y) && - PVG_FT_IS_SMALL(control2->x - to->x) && - PVG_FT_IS_SMALL(control2->y - to->y)) { - stroker->center = *to; - goto Exit; - } - - arc = bez_stack; - arc[0] = *to; - arc[1] = *control2; - arc[2] = *control1; - arc[3] = stroker->center; - - while (arc >= bez_stack) { - PVG_FT_Angle angle_in, angle_mid, angle_out; - - /* initialize with current direction */ - angle_in = angle_out = angle_mid = stroker->angle_in; - - if (arc < limit && - !ft_cubic_is_small_enough(arc, &angle_in, &angle_mid, &angle_out)) { - if (stroker->first_point) stroker->angle_in = angle_in; - - ft_cubic_split(arc); - arc += 3; - continue; - } - - if (first_arc) { - first_arc = FALSE; - - /* process corner if necessary */ - if (stroker->first_point) - error = ft_stroker_subpath_start(stroker, angle_in, 0); - else { - stroker->angle_out = angle_in; - error = ft_stroker_process_corner(stroker, 0); - } - } else if (ft_pos_abs(PVG_FT_Angle_Diff(stroker->angle_in, angle_in)) > - PVG_FT_SMALL_CUBIC_THRESHOLD / 4) { - /* if the deviation from one arc to the next is too great, */ - /* add a round corner */ - stroker->center = arc[3]; - stroker->angle_out = angle_in; - stroker->line_join = PVG_FT_STROKER_LINEJOIN_ROUND; - - error = ft_stroker_process_corner(stroker, 0); - - /* reinstate line join style */ - stroker->line_join = stroker->line_join_saved; - } - - if (error) goto Exit; - - /* the arc's angle is small enough; we can add it directly to each */ - /* border */ - { - PVG_FT_Vector ctrl1, ctrl2, end; - PVG_FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; - PVG_FT_Fixed length1, length2; - PVG_FT_StrokeBorder border; - PVG_FT_Int side; - - theta1 = PVG_FT_Angle_Diff(angle_in, angle_mid) / 2; - theta2 = PVG_FT_Angle_Diff(angle_mid, angle_out) / 2; - phi1 = ft_angle_mean(angle_in, angle_mid); - phi2 = ft_angle_mean(angle_mid, angle_out); - length1 = PVG_FT_DivFix(stroker->radius, PVG_FT_Cos(theta1)); - length2 = PVG_FT_DivFix(stroker->radius, PVG_FT_Cos(theta2)); - - /* compute direction of original arc */ - if (stroker->handle_wide_strokes) - alpha0 = PVG_FT_Atan2(arc[0].x - arc[3].x, arc[0].y - arc[3].y); - - for (border = stroker->borders, side = 0; side <= 1; - side++, border++) { - rotate = PVG_FT_SIDE_TO_ROTATE(side); - - /* compute control points */ - PVG_FT_Vector_From_Polar(&ctrl1, length1, phi1 + rotate); - ctrl1.x += arc[2].x; - ctrl1.y += arc[2].y; - - PVG_FT_Vector_From_Polar(&ctrl2, length2, phi2 + rotate); - ctrl2.x += arc[1].x; - ctrl2.y += arc[1].y; - - /* compute end point */ - PVG_FT_Vector_From_Polar(&end, stroker->radius, - angle_out + rotate); - end.x += arc[0].x; - end.y += arc[0].y; - - if (stroker->handle_wide_strokes) { - PVG_FT_Vector start; - PVG_FT_Angle alpha1; - - /* determine whether the border radius is greater than the - */ - /* radius of curvature of the original arc */ - start = border->points[border->num_points - 1]; - - alpha1 = PVG_FT_Atan2(end.x - start.x, end.y - start.y); - - /* is the direction of the border arc opposite to */ - /* that of the original arc? */ - if (ft_pos_abs(PVG_FT_Angle_Diff(alpha0, alpha1)) > - PVG_FT_ANGLE_PI / 2) { - PVG_FT_Angle beta, gamma; - PVG_FT_Vector bvec, delta; - PVG_FT_Fixed blen, sinA, sinB, alen; - - /* use the sine rule to find the intersection point */ - beta = - PVG_FT_Atan2(arc[3].x - start.x, arc[3].y - start.y); - gamma = PVG_FT_Atan2(arc[0].x - end.x, arc[0].y - end.y); - - bvec.x = end.x - start.x; - bvec.y = end.y - start.y; - - blen = PVG_FT_Vector_Length(&bvec); - - sinA = ft_pos_abs(PVG_FT_Sin(alpha1 - gamma)); - sinB = ft_pos_abs(PVG_FT_Sin(beta - gamma)); - - alen = PVG_FT_MulDiv(blen, sinA, sinB); - - PVG_FT_Vector_From_Polar(&delta, alen, beta); - delta.x += start.x; - delta.y += start.y; - - /* circumnavigate the negative sector backwards */ - border->movable = FALSE; - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; - error = ft_stroke_border_lineto(border, &end, FALSE); - if (error) goto Exit; - error = ft_stroke_border_cubicto(border, &ctrl2, &ctrl1, - &start); - if (error) goto Exit; - /* and then move to the endpoint */ - error = ft_stroke_border_lineto(border, &end, FALSE); - if (error) goto Exit; - - continue; - } - - /* else fall through */ - } - - /* simply add an arc */ - error = ft_stroke_border_cubicto(border, &ctrl1, &ctrl2, &end); - if (error) goto Exit; - } - } - - arc -= 3; - - stroker->angle_in = angle_out; - } - - stroker->center = *to; - stroker->line_length = 0; - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_BeginSubPath(PVG_FT_Stroker stroker, PVG_FT_Vector* to, - PVG_FT_Bool open) -{ - /* We cannot process the first point, because there is not enough */ - /* information regarding its corner/cap. The latter will be processed */ - /* in the `PVG_FT_Stroker_EndSubPath' routine. */ - /* */ - stroker->first_point = TRUE; - stroker->center = *to; - stroker->subpath_open = open; - - /* Determine if we need to check whether the border radius is greater */ - /* than the radius of curvature of a curve, to handle this case */ - /* specially. This is only required if bevel joins or butt caps may */ - /* be created, because round & miter joins and round & square caps */ - /* cover the negative sector created with wide strokes. */ - stroker->handle_wide_strokes = - PVG_FT_BOOL(stroker->line_join != PVG_FT_STROKER_LINEJOIN_ROUND || - (stroker->subpath_open && - stroker->line_cap == PVG_FT_STROKER_LINECAP_BUTT)); - - /* record the subpath start point for each border */ - stroker->subpath_start = *to; - - stroker->angle_in = 0; - - return 0; -} - -static PVG_FT_Error ft_stroker_add_reverse_left(PVG_FT_Stroker stroker, - PVG_FT_Bool open) -{ - PVG_FT_StrokeBorder right = stroker->borders + 0; - PVG_FT_StrokeBorder left = stroker->borders + 1; - PVG_FT_Int new_points; - PVG_FT_Error error = 0; - - assert(left->start >= 0); - - new_points = left->num_points - left->start; - if (new_points > 0) { - error = ft_stroke_border_grow(right, (PVG_FT_UInt)new_points); - if (error) goto Exit; - - { - PVG_FT_Vector* dst_point = right->points + right->num_points; - PVG_FT_Byte* dst_tag = right->tags + right->num_points; - PVG_FT_Vector* src_point = left->points + left->num_points - 1; - PVG_FT_Byte* src_tag = left->tags + left->num_points - 1; - - while (src_point >= left->points + left->start) { - *dst_point = *src_point; - *dst_tag = *src_tag; - - if (open) - dst_tag[0] &= ~PVG_FT_STROKE_TAG_BEGIN_END; - else { - PVG_FT_Byte ttag = - (PVG_FT_Byte)(dst_tag[0] & PVG_FT_STROKE_TAG_BEGIN_END); - - /* switch begin/end tags if necessary */ - if (ttag == PVG_FT_STROKE_TAG_BEGIN || - ttag == PVG_FT_STROKE_TAG_END) - dst_tag[0] ^= PVG_FT_STROKE_TAG_BEGIN_END; - } - - src_point--; - src_tag--; - dst_point++; - dst_tag++; - } - } - - left->num_points = left->start; - right->num_points += new_points; - - right->movable = FALSE; - left->movable = FALSE; - } - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -/* there's a lot of magic in this function! */ -PVG_FT_Error PVG_FT_Stroker_EndSubPath(PVG_FT_Stroker stroker) -{ - PVG_FT_Error error = 0; - - if (stroker->subpath_open) { - PVG_FT_StrokeBorder right = stroker->borders; - - /* All right, this is an opened path, we need to add a cap between */ - /* right & left, add the reverse of left, then add a final cap */ - /* between left & right. */ - error = ft_stroker_cap(stroker, stroker->angle_in, 0); - if (error) goto Exit; - - /* add reversed points from `left' to `right' */ - error = ft_stroker_add_reverse_left(stroker, TRUE); - if (error) goto Exit; - - /* now add the final cap */ - stroker->center = stroker->subpath_start; - error = - ft_stroker_cap(stroker, stroker->subpath_angle + PVG_FT_ANGLE_PI, 0); - if (error) goto Exit; - - /* Now end the right subpath accordingly. The left one is */ - /* rewind and doesn't need further processing. */ - ft_stroke_border_close(right, FALSE); - } else { - PVG_FT_Angle turn; - PVG_FT_Int inside_side; - - /* close the path if needed */ - if (stroker->center.x != stroker->subpath_start.x || - stroker->center.y != stroker->subpath_start.y) { - error = PVG_FT_Stroker_LineTo(stroker, &stroker->subpath_start); - if (error) goto Exit; - } - - /* process the corner */ - stroker->angle_out = stroker->subpath_angle; - turn = PVG_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); - - /* no specific corner processing is required if the turn is 0 */ - if (turn != 0) { - /* when we turn to the right, the inside side is 0 */ - inside_side = 0; - - /* otherwise, the inside side is 1 */ - if (turn < 0) inside_side = 1; - - error = ft_stroker_inside(stroker, inside_side, - stroker->subpath_line_length); - if (error) goto Exit; - - /* process the outside side */ - error = ft_stroker_outside(stroker, 1 - inside_side, - stroker->subpath_line_length); - if (error) goto Exit; - } - - /* then end our two subpaths */ - ft_stroke_border_close(stroker->borders + 0, FALSE); - ft_stroke_border_close(stroker->borders + 1, TRUE); - } - -Exit: - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_GetBorderCounts(PVG_FT_Stroker stroker, - PVG_FT_StrokerBorder border, - PVG_FT_UInt* anum_points, - PVG_FT_UInt* anum_contours) -{ - PVG_FT_UInt num_points = 0, num_contours = 0; - PVG_FT_Error error; - - if (!stroker || border > 1) { - error = -1; // PVG_FT_THROW( Invalid_Argument ); - goto Exit; - } - - error = ft_stroke_border_get_counts(stroker->borders + border, &num_points, - &num_contours); -Exit: - if (anum_points) *anum_points = num_points; - - if (anum_contours) *anum_contours = num_contours; - - return error; -} - -/* documentation is in ftstroke.h */ - -PVG_FT_Error PVG_FT_Stroker_GetCounts(PVG_FT_Stroker stroker, - PVG_FT_UInt* anum_points, - PVG_FT_UInt* anum_contours) -{ - PVG_FT_UInt count1, count2, num_points = 0; - PVG_FT_UInt count3, count4, num_contours = 0; - PVG_FT_Error error; - - error = ft_stroke_border_get_counts(stroker->borders + 0, &count1, &count2); - if (error) goto Exit; - - error = ft_stroke_border_get_counts(stroker->borders + 1, &count3, &count4); - if (error) goto Exit; - - num_points = count1 + count3; - num_contours = count2 + count4; - -Exit: - *anum_points = num_points; - *anum_contours = num_contours; - return error; -} - -/* documentation is in ftstroke.h */ - -void PVG_FT_Stroker_ExportBorder(PVG_FT_Stroker stroker, - PVG_FT_StrokerBorder border, - PVG_FT_Outline* outline) -{ - if (border == PVG_FT_STROKER_BORDER_LEFT || - border == PVG_FT_STROKER_BORDER_RIGHT) { - PVG_FT_StrokeBorder sborder = &stroker->borders[border]; - - if (sborder->valid) ft_stroke_border_export(sborder, outline); - } -} - -/* documentation is in ftstroke.h */ - -void PVG_FT_Stroker_Export(PVG_FT_Stroker stroker, PVG_FT_Outline* outline) -{ - PVG_FT_Stroker_ExportBorder(stroker, PVG_FT_STROKER_BORDER_LEFT, outline); - PVG_FT_Stroker_ExportBorder(stroker, PVG_FT_STROKER_BORDER_RIGHT, outline); -} - -/* documentation is in ftstroke.h */ - -/* - * The following is very similar to PVG_FT_Outline_Decompose, except - * that we do support opened paths, and do not scale the outline. - */ -PVG_FT_Error PVG_FT_Stroker_ParseOutline(PVG_FT_Stroker stroker, - const PVG_FT_Outline* outline) -{ - PVG_FT_Vector v_last; - PVG_FT_Vector v_control; - PVG_FT_Vector v_start; - - PVG_FT_Vector* point; - PVG_FT_Vector* limit; - char* tags; - - PVG_FT_Error error; - - PVG_FT_Int n; /* index of contour in outline */ - PVG_FT_UInt first; /* index of first point in contour */ - PVG_FT_Int tag; /* current point's state */ - - if (!outline || !stroker) return -1; // PVG_FT_THROW( Invalid_Argument ); - - PVG_FT_Stroker_Rewind(stroker); - - first = 0; - - for (n = 0; n < outline->n_contours; n++) { - PVG_FT_UInt last; /* index of last point in contour */ - - last = outline->contours[n]; - limit = outline->points + last; - - /* skip empty points; we don't stroke these */ - if (last <= first) { - first = last + 1; - continue; - } - - v_start = outline->points[first]; - v_last = outline->points[last]; - - v_control = v_start; - - point = outline->points + first; - tags = outline->tags + first; - tag = PVG_FT_CURVE_TAG(tags[0]); - - /* A contour cannot start with a cubic control point! */ - if (tag == PVG_FT_CURVE_TAG_CUBIC) goto Invalid_Outline; - - /* check first point to determine origin */ - if (tag == PVG_FT_CURVE_TAG_CONIC) { - /* First point is conic control. Yes, this happens. */ - if (PVG_FT_CURVE_TAG(outline->tags[last]) == PVG_FT_CURVE_TAG_ON) { - /* start at last point if it is on the curve */ - v_start = v_last; - limit--; - } else { - /* if both first and last points are conic, */ - /* start at their middle */ - v_start.x = (v_start.x + v_last.x) / 2; - v_start.y = (v_start.y + v_last.y) / 2; - } - point--; - tags--; - } - - error = PVG_FT_Stroker_BeginSubPath(stroker, &v_start, outline->contours_flag[n]); - if (error) goto Exit; - - while (point < limit) { - point++; - tags++; - - tag = PVG_FT_CURVE_TAG(tags[0]); - switch (tag) { - case PVG_FT_CURVE_TAG_ON: /* emit a single line_to */ - { - PVG_FT_Vector vec; - - vec.x = point->x; - vec.y = point->y; - - error = PVG_FT_Stroker_LineTo(stroker, &vec); - if (error) goto Exit; - continue; - } - - case PVG_FT_CURVE_TAG_CONIC: /* consume conic arcs */ - v_control.x = point->x; - v_control.y = point->y; - - Do_Conic: - if (point < limit) { - PVG_FT_Vector vec; - PVG_FT_Vector v_middle; - - point++; - tags++; - tag = PVG_FT_CURVE_TAG(tags[0]); - - vec = point[0]; - - if (tag == PVG_FT_CURVE_TAG_ON) { - error = - PVG_FT_Stroker_ConicTo(stroker, &v_control, &vec); - if (error) goto Exit; - continue; - } - - if (tag != PVG_FT_CURVE_TAG_CONIC) goto Invalid_Outline; - - v_middle.x = (v_control.x + vec.x) / 2; - v_middle.y = (v_control.y + vec.y) / 2; - - error = - PVG_FT_Stroker_ConicTo(stroker, &v_control, &v_middle); - if (error) goto Exit; - - v_control = vec; - goto Do_Conic; - } - - error = PVG_FT_Stroker_ConicTo(stroker, &v_control, &v_start); - goto Close; - - default: /* PVG_FT_CURVE_TAG_CUBIC */ - { - PVG_FT_Vector vec1, vec2; - - if (point + 1 > limit || - PVG_FT_CURVE_TAG(tags[1]) != PVG_FT_CURVE_TAG_CUBIC) - goto Invalid_Outline; - - point += 2; - tags += 2; - - vec1 = point[-2]; - vec2 = point[-1]; - - if (point <= limit) { - PVG_FT_Vector vec; - - vec = point[0]; - - error = PVG_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &vec); - if (error) goto Exit; - continue; - } - - error = PVG_FT_Stroker_CubicTo(stroker, &vec1, &vec2, &v_start); - goto Close; - } - } - } - - Close: - if (error) goto Exit; - - if (stroker->first_point) { - stroker->subpath_open = TRUE; - error = ft_stroker_subpath_start(stroker, 0, 0); - if (error) goto Exit; - } - - error = PVG_FT_Stroker_EndSubPath(stroker); - if (error) goto Exit; - - first = last + 1; - } - - return 0; - -Exit: - return error; - -Invalid_Outline: - return -2; // PVG_FT_THROW( Invalid_Outline ); -} - -/* END */ diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h b/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h deleted file mode 100644 index 530a6f2..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h +++ /dev/null @@ -1,320 +0,0 @@ -/***************************************************************************/ -/* */ -/* 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 diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-types.h b/vendor/lunasvg/plutovg/source/plutovg-ft-types.h deleted file mode 100644 index 4b3db90..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-types.h +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** - * - * 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 - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Fixed */ -/* */ -/* */ -/* This type is used to store 16.16 fixed-point values, like scaling */ -/* values or matrix coefficients. */ -/* */ -typedef signed long PVG_FT_Fixed; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Int */ -/* */ -/* */ -/* A typedef for the int type. */ -/* */ -typedef signed int PVG_FT_Int; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_UInt */ -/* */ -/* */ -/* A typedef for the unsigned int type. */ -/* */ -typedef unsigned int PVG_FT_UInt; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Long */ -/* */ -/* */ -/* A typedef for signed long. */ -/* */ -typedef signed long PVG_FT_Long; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_ULong */ -/* */ -/* */ -/* A typedef for unsigned long. */ -/* */ -typedef unsigned long PVG_FT_ULong; - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Short */ -/* */ -/* */ -/* A typedef for signed short. */ -/* */ -typedef signed short PVG_FT_Short; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Byte */ -/* */ -/* */ -/* A simple typedef for the _unsigned_ char type. */ -/* */ -typedef unsigned char PVG_FT_Byte; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Bool */ -/* */ -/* */ -/* 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; - - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Error */ -/* */ -/* */ -/* The FreeType error code type. A value of~0 is always interpreted */ -/* as a successful operation. */ -/* */ -typedef int PVG_FT_Error; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Pos */ -/* */ -/* */ -/* 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; - - -/*************************************************************************/ -/* */ -/* */ -/* PVG_FT_Vector */ -/* */ -/* */ -/* A simple structure used to store a 2D vector; coordinates are of */ -/* the PVG_FT_Pos type. */ -/* */ -/* */ -/* 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 diff --git a/vendor/lunasvg/plutovg/source/plutovg-matrix.c b/vendor/lunasvg/plutovg/source/plutovg-matrix.c deleted file mode 100644 index d784d7a..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-matrix.c +++ /dev/null @@ -1,229 +0,0 @@ -#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; -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-paint.c b/vendor/lunasvg/plutovg/source/plutovg-paint.c deleted file mode 100644 index 43672ff..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-paint.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "plutovg-private.h" -#include "plutovg-utils.h" - -#include - -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); -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-path.c b/vendor/lunasvg/plutovg/source/plutovg-path.c deleted file mode 100644 index 2efaa63..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-path.c +++ /dev/null @@ -1,918 +0,0 @@ -#include "plutovg-private.h" -#include "plutovg-utils.h" - -#include - -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; -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-private.h b/vendor/lunasvg/plutovg/source/plutovg-private.h deleted file mode 100644 index 838cd43..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-private.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef PLUTOVG_PRIVATE_H -#define PLUTOVG_PRIVATE_H - -#include "plutovg.h" - -#if defined(_WIN32) - -#include - -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 - -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 diff --git a/vendor/lunasvg/plutovg/source/plutovg-rasterize.c b/vendor/lunasvg/plutovg/source/plutovg-rasterize.c deleted file mode 100644 index 559f0ee..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-rasterize.c +++ /dev/null @@ -1,394 +0,0 @@ -#include "plutovg-private.h" -#include "plutovg-utils.h" - -#include "plutovg-ft-raster.h" -#include "plutovg-ft-stroker.h" - -#include - -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); -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h b/vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h deleted file mode 100644 index dba9089..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h +++ /dev/null @@ -1,1724 +0,0 @@ -/* stb_image_write - v1.16 - public domain - http://nothings.org/stb - writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 - no warranty implied; use at your own risk - - Before #including, - - #define STB_IMAGE_WRITE_IMPLEMENTATION - - in the file that you want to have the implementation. - - Will probably not work correctly with strict-aliasing optimizations. - -ABOUT: - - This header file is a library for writing images to C stdio or a callback. - - The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation; though providing a custom - zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. - This library is designed for source code compactness and simplicity, - not optimal image file size or run-time performance. - -BUILDING: - - You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. - You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace - malloc,realloc,free. - You can #define STBIW_MEMMOVE() to replace memmove() - You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function - for PNG compression (instead of the builtin one), it must have the following signature: - unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); - The returned data will be freed with STBIW_FREE() (free() by default), - so it must be heap allocated with STBIW_MALLOC() (malloc() by default), - -UNICODE: - - If compiling for Windows and you wish to use Unicode filenames, compile - with - #define STBIW_WINDOWS_UTF8 - and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert - Windows wchar_t filenames to utf8. - -USAGE: - - There are five functions, one for each image file format: - - int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); - int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); - - void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically - - There are also five equivalent functions that use an arbitrary write function. You are - expected to open/close your file-equivalent before and after calling these: - - int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); - int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); - int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); - - where the callback is: - void stbi_write_func(void *context, void *data, int size); - - You can configure it with these global variables: - int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE - int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression - int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode - - - You can define STBI_WRITE_NO_STDIO to disable the file variant of these - functions, so the library will not use stdio.h at all. However, this will - also disable HDR writing, because it requires stdio for formatted output. - - Each function returns 0 on failure and non-0 on success. - - The functions create an image file defined by the parameters. The image - is a rectangle of pixels stored from left-to-right, top-to-bottom. - Each pixel contains 'comp' channels of data stored interleaved with 8-bits - per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is - monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. - The *data pointer points to the first byte of the top-left-most pixel. - For PNG, "stride_in_bytes" is the distance in bytes from the first byte of - a row of pixels to the first byte of the next row of pixels. - - PNG creates output files with the same number of components as the input. - The BMP format expands Y to RGB in the file format and does not - output alpha. - - PNG supports writing rectangles of data even when the bytes storing rows of - data are not consecutive in memory (e.g. sub-rectangles of a larger image), - by supplying the stride between the beginning of adjacent rows. The other - formats do not. (Thus you cannot write a native-format BMP through the BMP - writer, both because it is in BGR order and because it may have padding - at the end of the line.) - - PNG allows you to set the deflate compression level by setting the global - variable 'stbi_write_png_compression_level' (it defaults to 8). - - HDR expects linear float data. Since the format is always 32-bit rgb(e) - data, alpha (if provided) is discarded, and for monochrome data it is - replicated across all three channels. - - TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed - data, set the global variable 'stbi_write_tga_with_rle' to 0. - - JPEG does ignore alpha channels in input data; quality is between 1 and 100. - Higher quality looks better but results in a bigger image. - JPEG baseline (no JPEG progressive). - -CREDITS: - - - Sean Barrett - PNG/BMP/TGA - Baldur Karlsson - HDR - Jean-Sebastien Guay - TGA monochrome - Tim Kelsey - misc enhancements - Alan Hickman - TGA RLE - Emmanuel Julien - initial file IO callback implementation - Jon Olick - original jo_jpeg.cpp code - Daniel Gibson - integrate JPEG, allow external zlib - Aarni Koskela - allow choosing PNG filter - - bugfixes: - github:Chribba - Guillaume Chereau - github:jry2 - github:romigrou - Sergio Gonzalez - Jonas Karlsson - Filip Wasil - Thatcher Ulrich - github:poppolopoppo - Patrick Boettcher - github:xeekworx - Cap Petschulat - Simon Rodriguez - Ivan Tikhonov - github:ignotion - Adam Schackart - Andrew Kensler - -LICENSE - - See end of file for license information. - -*/ - -#ifndef PLUTOVG_STB_IMAGE_WRITE_H -#define PLUTOVG_STB_IMAGE_WRITE_H - -#include - -// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' -#ifndef STBIWDEF -#ifdef STB_IMAGE_WRITE_STATIC -#define STBIWDEF static -#else -#ifdef __cplusplus -#define STBIWDEF extern "C" -#else -#define STBIWDEF extern -#endif -#endif -#endif - -#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations -STBIWDEF int stbi_write_tga_with_rle; -STBIWDEF int stbi_write_png_compression_level; -STBIWDEF int stbi_write_force_png_filter; -#endif - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); -STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); - -#ifdef STBIW_WINDOWS_UTF8 -STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif -#endif - -typedef void stbi_write_func(void *context, void *data, int size); - -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); -STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); -STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); -STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); - -STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); - -#endif//PLUTOVG_STB_IMAGE_WRITE_H - -#ifdef STB_IMAGE_WRITE_IMPLEMENTATION - -#ifdef _WIN32 - #ifndef _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #ifndef _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE - #endif -#endif - -#ifndef STBI_WRITE_NO_STDIO -#include -#endif // STBI_WRITE_NO_STDIO - -#include -#include -#include -#include - -#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) -// ok -#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." -#endif - -#ifndef STBIW_MALLOC -#define STBIW_MALLOC(sz) malloc(sz) -#define STBIW_REALLOC(p,newsz) realloc(p,newsz) -#define STBIW_FREE(p) free(p) -#endif - -#ifndef STBIW_REALLOC_SIZED -#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) -#endif - - -#ifndef STBIW_MEMMOVE -#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) -#endif - - -#ifndef STBIW_ASSERT -#include -#define STBIW_ASSERT(x) assert(x) -#endif - -#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) - -#ifdef STB_IMAGE_WRITE_STATIC -static int stbi_write_png_compression_level = 8; -static int stbi_write_tga_with_rle = 1; -static int stbi_write_force_png_filter = -1; -#else -int stbi_write_png_compression_level = 8; -int stbi_write_tga_with_rle = 1; -int stbi_write_force_png_filter = -1; -#endif - -static int stbi__flip_vertically_on_write = 0; - -STBIWDEF void stbi_flip_vertically_on_write(int flag) -{ - stbi__flip_vertically_on_write = flag; -} - -typedef struct -{ - stbi_write_func *func; - void *context; - unsigned char buffer[64]; - int buf_used; -} stbi__write_context; - -// initialize a callback-based context -static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) -{ - s->func = c; - s->context = context; -} - -#ifndef STBI_WRITE_NO_STDIO - -static void stbi__stdio_write(void *context, void *data, int size) -{ - fwrite(data,1,size,(FILE*) context); -} - -#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) -#ifdef __cplusplus -#define STBIW_EXTERN extern "C" -#else -#define STBIW_EXTERN extern -#endif -STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); - -STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbiw__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - -static int stbi__start_write_file(stbi__write_context *s, const char *filename) -{ - FILE *f = stbiw__fopen(filename, "wb"); - stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); - return f != NULL; -} - -static void stbi__end_write_file(stbi__write_context *s) -{ - fclose((FILE *)s->context); -} - -#endif // !STBI_WRITE_NO_STDIO - -typedef unsigned int stbiw_uint32; -typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; - -static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) -{ - while (*fmt) { - switch (*fmt++) { - case ' ': break; - case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); - s->func(s->context,&x,1); - break; } - case '2': { int x = va_arg(v,int); - unsigned char b[2]; - b[0] = STBIW_UCHAR(x); - b[1] = STBIW_UCHAR(x>>8); - s->func(s->context,b,2); - break; } - case '4': { stbiw_uint32 x = va_arg(v,int); - unsigned char b[4]; - b[0]=STBIW_UCHAR(x); - b[1]=STBIW_UCHAR(x>>8); - b[2]=STBIW_UCHAR(x>>16); - b[3]=STBIW_UCHAR(x>>24); - s->func(s->context,b,4); - break; } - default: - STBIW_ASSERT(0); - return; - } - } -} - -static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) -{ - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); -} - -static void stbiw__write_flush(stbi__write_context *s) -{ - if (s->buf_used) { - s->func(s->context, &s->buffer, s->buf_used); - s->buf_used = 0; - } -} - -static void stbiw__putc(stbi__write_context *s, unsigned char c) -{ - s->func(s->context, &c, 1); -} - -static void stbiw__write1(stbi__write_context *s, unsigned char a) -{ - if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) - stbiw__write_flush(s); - s->buffer[s->buf_used++] = a; -} - -static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) -{ - int n; - if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) - stbiw__write_flush(s); - n = s->buf_used; - s->buf_used = n+3; - s->buffer[n+0] = a; - s->buffer[n+1] = b; - s->buffer[n+2] = c; -} - -static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) -{ - unsigned char bg[3] = { 255, 0, 255}, px[3]; - int k; - - if (write_alpha < 0) - stbiw__write1(s, d[comp - 1]); - - switch (comp) { - case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case - case 1: - if (expand_mono) - stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp - else - stbiw__write1(s, d[0]); // monochrome TGA - break; - case 4: - if (!write_alpha) { - // composite against pink background - for (k = 0; k < 3; ++k) - px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; - stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); - break; - } - /* FALLTHROUGH */ - case 3: - stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); - break; - } - if (write_alpha > 0) - stbiw__write1(s, d[comp - 1]); -} - -static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) -{ - stbiw_uint32 zero = 0; - int i,j, j_end; - - if (y <= 0) - return; - - if (stbi__flip_vertically_on_write) - vdir *= -1; - - if (vdir < 0) { - j_end = -1; j = y-1; - } else { - j_end = y; j = 0; - } - - for (; j != j_end; j += vdir) { - for (i=0; i < x; ++i) { - unsigned char *d = (unsigned char *) data + (j*x+i)*comp; - stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); - } - stbiw__write_flush(s); - s->func(s->context, &zero, scanline_pad); - } -} - -static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) -{ - if (y < 0 || x < 0) { - return 0; - } else { - va_list v; - va_start(v, fmt); - stbiw__writefv(s, fmt, v); - va_end(v); - stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); - return 1; - } -} - -static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) -{ - if (comp != 4) { - // write RGB bitmap - int pad = (-x*3) & 3; - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, - "11 4 22 4" "4 44 22 444444", - 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header - 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header - } else { - // RGBA bitmaps need a v4 header - // use BI_BITFIELDS mode with 32bpp and alpha mask - // (straight BI_RGB with alpha mask doesn't work in most readers) - return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, - "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", - 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header - 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header - } -} - -STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) -{ - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_bmp_core(&s, x, y, comp, data); -} - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) -{ - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_bmp_core(&s, x, y, comp, data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif //!STBI_WRITE_NO_STDIO - -static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) -{ - int has_alpha = (comp == 2 || comp == 4); - int colorbytes = has_alpha ? comp-1 : comp; - int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 - - if (y < 0 || x < 0) - return 0; - - if (!stbi_write_tga_with_rle) { - return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, - "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); - } else { - int i,j,k; - int jend, jdir; - - stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); - - if (stbi__flip_vertically_on_write) { - j = 0; - jend = y; - jdir = 1; - } else { - j = y-1; - jend = -1; - jdir = -1; - } - for (; j != jend; j += jdir) { - unsigned char *row = (unsigned char *) data + j * x * comp; - int len; - - for (i = 0; i < x; i += len) { - unsigned char *begin = row + i * comp; - int diff = 1; - len = 1; - - if (i < x - 1) { - ++len; - diff = memcmp(begin, row + (i + 1) * comp, comp); - if (diff) { - const unsigned char *prev = begin; - for (k = i + 2; k < x && len < 128; ++k) { - if (memcmp(prev, row + k * comp, comp)) { - prev += comp; - ++len; - } else { - --len; - break; - } - } - } else { - for (k = i + 2; k < x && len < 128; ++k) { - if (!memcmp(begin, row + k * comp, comp)) { - ++len; - } else { - break; - } - } - } - } - - if (diff) { - unsigned char header = STBIW_UCHAR(len - 1); - stbiw__write1(s, header); - for (k = 0; k < len; ++k) { - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); - } - } else { - unsigned char header = STBIW_UCHAR(len - 129); - stbiw__write1(s, header); - stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); - } - } - } - stbiw__write_flush(s); - } - return 1; -} - -STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) -{ - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_tga_core(&s, x, y, comp, (void *) data); -} - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) -{ - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR writer -// by Baldur Karlsson - -#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) - -#ifndef STBI_WRITE_NO_STDIO - -static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) -{ - int exponent; - float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); - - if (maxcomp < 1e-32f) { - rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; - } else { - float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; - - rgbe[0] = (unsigned char)(linear[0] * normalize); - rgbe[1] = (unsigned char)(linear[1] * normalize); - rgbe[2] = (unsigned char)(linear[2] * normalize); - rgbe[3] = (unsigned char)(exponent + 128); - } -} - -static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) -{ - unsigned char lengthbyte = STBIW_UCHAR(length+128); - STBIW_ASSERT(length+128 <= 255); - s->func(s->context, &lengthbyte, 1); - s->func(s->context, &databyte, 1); -} - -static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) -{ - unsigned char lengthbyte = STBIW_UCHAR(length); - STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code - s->func(s->context, &lengthbyte, 1); - s->func(s->context, data, length); -} - -static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) -{ - unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; - unsigned char rgbe[4]; - float linear[3]; - int x; - - scanlineheader[2] = (width&0xff00)>>8; - scanlineheader[3] = (width&0x00ff); - - /* skip RLE for images too small or large */ - if (width < 8 || width >= 32768) { - for (x=0; x < width; x++) { - switch (ncomp) { - case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; - default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - s->func(s->context, rgbe, 4); - } - } else { - int c,r; - /* encode into scratch buffer */ - for (x=0; x < width; x++) { - switch(ncomp) { - case 4: /* fallthrough */ - case 3: linear[2] = scanline[x*ncomp + 2]; - linear[1] = scanline[x*ncomp + 1]; - linear[0] = scanline[x*ncomp + 0]; - break; - default: - linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; - break; - } - stbiw__linear_to_rgbe(rgbe, linear); - scratch[x + width*0] = rgbe[0]; - scratch[x + width*1] = rgbe[1]; - scratch[x + width*2] = rgbe[2]; - scratch[x + width*3] = rgbe[3]; - } - - s->func(s->context, scanlineheader, 4); - - /* RLE each component separately */ - for (c=0; c < 4; c++) { - unsigned char *comp = &scratch[width*c]; - - x = 0; - while (x < width) { - // find first run - r = x; - while (r+2 < width) { - if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) - break; - ++r; - } - if (r+2 >= width) - r = width; - // dump up to first run - while (x < r) { - int len = r-x; - if (len > 128) len = 128; - stbiw__write_dump_data(s, len, &comp[x]); - x += len; - } - // if there's a run, output it - if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd - // find next byte after run - while (r < width && comp[r] == comp[x]) - ++r; - // output run up to r - while (x < r) { - int len = r-x; - if (len > 127) len = 127; - stbiw__write_run_data(s, len, comp[x]); - x += len; - } - } - } - } - } -} - -static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) -{ - if (y <= 0 || x <= 0 || data == NULL) - return 0; - else { - // Each component is stored separately. Allocate scratch space for full output scanline. - unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); - int i, len; - char buffer[128]; - char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; - s->func(s->context, header, sizeof(header)-1); - -#ifdef __STDC_LIB_EXT1__ - len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); -#else - len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); -#endif - s->func(s->context, buffer, len); - - for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); - STBIW_FREE(scratch); - return 1; - } -} - -STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) -{ - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_hdr_core(&s, x, y, comp, (float *) data); -} - -STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) -{ - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif // STBI_WRITE_NO_STDIO - - -////////////////////////////////////////////////////////////////////////////// -// -// PNG writer -// - -#ifndef STBIW_ZLIB_COMPRESS -// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() -#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) -#define stbiw__sbm(a) stbiw__sbraw(a)[0] -#define stbiw__sbn(a) stbiw__sbraw(a)[1] - -#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) -#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) -#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) - -#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) -#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) -#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) - -static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) -{ - int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; - void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); - STBIW_ASSERT(p); - if (p) { - if (!*arr) ((int *) p)[1] = 0; - *arr = (void *) ((int *) p + 2); - stbiw__sbm(*arr) = m; - } - return *arr; -} - -static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) -{ - while (*bitcount >= 8) { - stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); - *bitbuffer >>= 8; - *bitcount -= 8; - } - return data; -} - -static int stbiw__zlib_bitrev(int code, int codebits) -{ - int res=0; - while (codebits--) { - res = (res << 1) | (code & 1); - code >>= 1; - } - return res; -} - -static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) -{ - int i; - for (i=0; i < limit && i < 258; ++i) - if (a[i] != b[i]) break; - return i; -} - -static unsigned int stbiw__zhash(unsigned char *data) -{ - stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - return hash; -} - -#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) -#define stbiw__zlib_add(code,codebits) \ - (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) -#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) -// default huffman tables -#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) -#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) -#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) -#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) -#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) -#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) - -#define stbiw__ZHASH 16384 - -#endif // STBIW_ZLIB_COMPRESS - -STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) -{ -#ifdef STBIW_ZLIB_COMPRESS - // user provided a zlib compress implementation, use that - return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); -#else // use builtin - static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; - static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; - static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; - static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; - unsigned int bitbuf=0; - int i,j, bitcount=0; - unsigned char *out = NULL; - unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); - if (hash_table == NULL) - return NULL; - if (quality < 5) quality = 5; - - stbiw__sbpush(out, 0x78); // DEFLATE 32K window - stbiw__sbpush(out, 0x5e); // FLEVEL = 1 - stbiw__zlib_add(1,1); // BFINAL = 1 - stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman - - for (i=0; i < stbiw__ZHASH; ++i) - hash_table[i] = NULL; - - i=0; - while (i < data_len-3) { - // hash next 3 bytes of data to be compressed - int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; - unsigned char *bestloc = 0; - unsigned char **hlist = hash_table[h]; - int n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32768) { // if entry lies within window - int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); - if (d >= best) { best=d; bestloc=hlist[j]; } - } - } - // when hash table entry is too long, delete half the entries - if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { - STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); - stbiw__sbn(hash_table[h]) = quality; - } - stbiw__sbpush(hash_table[h],data+i); - - if (bestloc) { - // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal - h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); - hlist = hash_table[h]; - n = stbiw__sbcount(hlist); - for (j=0; j < n; ++j) { - if (hlist[j]-data > i-32767) { - int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); - if (e > best) { // if next match is better, bail on current match - bestloc = NULL; - break; - } - } - } - } - - if (bestloc) { - int d = (int) (data+i - bestloc); // distance back - STBIW_ASSERT(d <= 32767 && best <= 258); - for (j=0; best > lengthc[j+1]-1; ++j); - stbiw__zlib_huff(j+257); - if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); - for (j=0; d > distc[j+1]-1; ++j); - stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); - if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); - i += best; - } else { - stbiw__zlib_huffb(data[i]); - ++i; - } - } - // write out final bytes - for (;i < data_len; ++i) - stbiw__zlib_huffb(data[i]); - stbiw__zlib_huff(256); // end of block - // pad with 0 bits to byte boundary - while (bitcount) - stbiw__zlib_add(0,1); - - for (i=0; i < stbiw__ZHASH; ++i) - (void) stbiw__sbfree(hash_table[i]); - STBIW_FREE(hash_table); - - // store uncompressed instead if compression was worse - if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { - stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 - for (j = 0; j < data_len;) { - int blocklen = data_len - j; - if (blocklen > 32767) blocklen = 32767; - stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression - stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN - stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN - stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); - memcpy(out+stbiw__sbn(out), data+j, blocklen); - stbiw__sbn(out) += blocklen; - j += blocklen; - } - } - - { - // compute adler32 on input - unsigned int s1=1, s2=0; - int blocklen = (int) (data_len % 5552); - j=0; - while (j < data_len) { - for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } - s1 %= 65521; s2 %= 65521; - j += blocklen; - blocklen = 5552; - } - stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s2)); - stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); - stbiw__sbpush(out, STBIW_UCHAR(s1)); - } - *out_len = stbiw__sbn(out); - // make returned pointer freeable - STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); - return (unsigned char *) stbiw__sbraw(out); -#endif // STBIW_ZLIB_COMPRESS -} - -static unsigned int stbiw__crc32(unsigned char *buffer, int len) -{ -#ifdef STBIW_CRC32 - return STBIW_CRC32(buffer, len); -#else - static unsigned int crc_table[256] = - { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D - }; - - unsigned int crc = ~0u; - int i; - for (i=0; i < len; ++i) - crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; - return ~crc; -#endif -} - -#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) -#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); -#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) - -static void stbiw__wpcrc(unsigned char **data, int len) -{ - unsigned int crc = stbiw__crc32(*data - len - 4, len+4); - stbiw__wp32(*data, crc); -} - -static unsigned char stbiw__paeth(int a, int b, int c) -{ - int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); - if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); - if (pb <= pc) return STBIW_UCHAR(b); - return STBIW_UCHAR(c); -} - -// @OPTIMIZE: provide an option that always forces left-predict or paeth predict -static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) -{ - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = (y != 0) ? mapping : firstmap; - int i; - int type = mymap[filter_type]; - unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); - int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; - - if (type==0) { - memcpy(line_buffer, z, width*n); - return; - } - - // first loop isn't optimized since it's just one pixel - for (i = 0; i < n; ++i) { - switch (type) { - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; - case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - } - switch (type) { - case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; - case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; - case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; - case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; - case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } -} - -STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) -{ - int force_filter = stbi_write_force_png_filter; - int ctype[5] = { -1, 0, 4, 2, 6 }; - unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; - unsigned char *out,*o, *filt, *zlib; - signed char *line_buffer; - int j,zlen; - - if (stride_bytes == 0) - stride_bytes = x * n; - - if (force_filter >= 5) { - force_filter = -1; - } - - filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; - line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } - for (j=0; j < y; ++j) { - int filter_type; - if (force_filter > -1) { - filter_type = force_filter; - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); - } else { // Estimate the best filter by running through all of them: - int best_filter = 0, best_filter_val = 0x7fffffff, est, i; - for (filter_type = 0; filter_type < 5; filter_type++) { - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); - - // Estimate the entropy of the line using this filter; the less, the better. - est = 0; - for (i = 0; i < x*n; ++i) { - est += abs((signed char) line_buffer[i]); - } - if (est < best_filter_val) { - best_filter_val = est; - best_filter = filter_type; - } - } - if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it - stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); - filter_type = best_filter; - } - } - // when we get here, filter_type contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) filter_type; - STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); - } - STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); - STBIW_FREE(filt); - if (!zlib) return 0; - - // each tag requires 12 bytes of overhead - out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); - if (!out) return 0; - *out_len = 8 + 12+13 + 12+zlen + 12; - - o=out; - STBIW_MEMMOVE(o,sig,8); o+= 8; - stbiw__wp32(o, 13); // header length - stbiw__wptag(o, "IHDR"); - stbiw__wp32(o, x); - stbiw__wp32(o, y); - *o++ = 8; - *o++ = STBIW_UCHAR(ctype[n]); - *o++ = 0; - *o++ = 0; - *o++ = 0; - stbiw__wpcrc(&o,13); - - stbiw__wp32(o, zlen); - stbiw__wptag(o, "IDAT"); - STBIW_MEMMOVE(o, zlib, zlen); - o += zlen; - STBIW_FREE(zlib); - stbiw__wpcrc(&o, zlen); - - stbiw__wp32(o,0); - stbiw__wptag(o, "IEND"); - stbiw__wpcrc(&o,0); - - STBIW_ASSERT(o == out + *out_len); - - return out; -} - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) -{ - FILE *f; - int len; - unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - - f = stbiw__fopen(filename, "wb"); - if (!f) { STBIW_FREE(png); return 0; } - fwrite(png, 1, len, f); - fclose(f); - STBIW_FREE(png); - return 1; -} -#endif - -STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) -{ - int len; - unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); - if (png == NULL) return 0; - func(context, png, len); - STBIW_FREE(png); - return 1; -} - - -/* *************************************************************************** - * - * JPEG writer - * - * This is based on Jon Olick's jo_jpeg.cpp: - * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html - */ - -static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, - 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; - -static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { - int bitBuf = *bitBufP, bitCnt = *bitCntP; - bitCnt += bs[1]; - bitBuf |= bs[0] << (24 - bitCnt); - while(bitCnt >= 8) { - unsigned char c = (bitBuf >> 16) & 255; - stbiw__putc(s, c); - if(c == 255) { - stbiw__putc(s, 0); - } - bitBuf <<= 8; - bitCnt -= 8; - } - *bitBufP = bitBuf; - *bitCntP = bitCnt; -} - -static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { - float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; - float z1, z2, z3, z4, z5, z11, z13; - - float tmp0 = d0 + d7; - float tmp7 = d0 - d7; - float tmp1 = d1 + d6; - float tmp6 = d1 - d6; - float tmp2 = d2 + d5; - float tmp5 = d2 - d5; - float tmp3 = d3 + d4; - float tmp4 = d3 - d4; - - // Even part - float tmp10 = tmp0 + tmp3; // phase 2 - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; - - d0 = tmp10 + tmp11; // phase 3 - d4 = tmp10 - tmp11; - - z1 = (tmp12 + tmp13) * 0.707106781f; // c4 - d2 = tmp13 + z1; // phase 5 - d6 = tmp13 - z1; - - // Odd part - tmp10 = tmp4 + tmp5; // phase 2 - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - // The rotator is modified from fig 4-8 to avoid extra negations. - z5 = (tmp10 - tmp12) * 0.382683433f; // c6 - z2 = tmp10 * 0.541196100f + z5; // c2-c6 - z4 = tmp12 * 1.306562965f + z5; // c2+c6 - z3 = tmp11 * 0.707106781f; // c4 - - z11 = tmp7 + z3; // phase 5 - z13 = tmp7 - z3; - - *d5p = z13 + z2; // phase 6 - *d3p = z13 - z2; - *d1p = z11 + z4; - *d7p = z11 - z4; - - *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; -} - -static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { - int tmp1 = val < 0 ? -val : val; - val = val < 0 ? val-1 : val; - bits[1] = 1; - while(tmp1 >>= 1) { - ++bits[1]; - } - bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { - } - // end0pos = first element in reverse order !=0 - if(end0pos == 0) { - stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); - return DU[0]; - } - for(i = 1; i <= end0pos; ++i) { - int startpos = i; - int nrzeroes; - unsigned short bits[2]; - for (; DU[i]==0 && i<=end0pos; ++i) { - } - nrzeroes = i-startpos; - if ( nrzeroes >= 16 ) { - int lng = nrzeroes>>4; - int nrmarker; - for (nrmarker=1; nrmarker <= lng; ++nrmarker) - stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); - nrzeroes &= 15; - } - stbiw__jpg_calcBits(DU[i], bits); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); - stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); - } - if(end0pos != 63) { - stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); - } - return DU[0]; -} - -static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { - // Constants that don't pollute global namespace - static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; - static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; - static const unsigned char std_ac_luminance_values[] = { - 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, - 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, - 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, - 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, - 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, - 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, - 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa - }; - static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; - static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; - static const unsigned char std_ac_chrominance_values[] = { - 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, - 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, - 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, - 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, - 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, - 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, - 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa - }; - // Huffman tables - static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; - static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; - static const unsigned short YAC_HT[256][2] = { - {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} - }; - static const unsigned short UVAC_HT[256][2] = { - {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, - {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, - {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} - }; - static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, - 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; - static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, - 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; - static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, - 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; - - int row, col, i, k, subsample; - float fdtbl_Y[64], fdtbl_UV[64]; - unsigned char YTable[64], UVTable[64]; - - if(!data || !width || !height || comp > 4 || comp < 1) { - return 0; - } - - quality = quality ? quality : 90; - subsample = quality <= 90 ? 1 : 0; - quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; - quality = quality < 50 ? 5000 / quality : 200 - quality * 2; - - for(i = 0; i < 64; ++i) { - int uvti, yti = (YQT[i]*quality+50)/100; - YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); - uvti = (UVQT[i]*quality+50)/100; - UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); - } - - for(row = 0, k = 0; row < 8; ++row) { - for(col = 0; col < 8; ++col, ++k) { - fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); - fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); - } - } - - // Write Headers - { - static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; - static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; - const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), - 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; - s->func(s->context, (void*)head0, sizeof(head0)); - s->func(s->context, (void*)YTable, sizeof(YTable)); - stbiw__putc(s, 1); - s->func(s->context, UVTable, sizeof(UVTable)); - s->func(s->context, (void*)head1, sizeof(head1)); - s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); - s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); - stbiw__putc(s, 0x10); // HTYACinfo - s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); - s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); - stbiw__putc(s, 1); // HTUDCinfo - s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); - s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); - stbiw__putc(s, 0x11); // HTUACinfo - s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); - s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); - s->func(s->context, (void*)head2, sizeof(head2)); - } - - // Encode 8x8 macroblocks - { - static const unsigned short fillBits[] = {0x7F, 7}; - int DCY=0, DCU=0, DCV=0; - int bitBuf=0, bitCnt=0; - // comp == 2 is grey+alpha (alpha is ignored) - int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; - const unsigned char *dataR = (const unsigned char *)data; - const unsigned char *dataG = dataR + ofsG; - const unsigned char *dataB = dataR + ofsB; - int x, y, pos; - if(subsample) { - for(y = 0; y < height; y += 16) { - for(x = 0; x < width; x += 16) { - float Y[256], U[256], V[256]; - for(row = y, pos = 0; row < y+16; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; - for(col = x; col < x+16; ++col, ++pos) { - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width-1))*comp; - float r = dataR[p], g = dataG[p], b = dataB[p]; - Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; - U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; - V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; - } - } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); - - // subsample U,V - { - float subU[64], subV[64]; - int yy, xx; - for(yy = 0, pos = 0; yy < 8; ++yy) { - for(xx = 0; xx < 8; ++xx, ++pos) { - int j = yy*32+xx*2; - subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; - subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; - } - } - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); - } - } - } - } else { - for(y = 0; y < height; y += 8) { - for(x = 0; x < width; x += 8) { - float Y[64], U[64], V[64]; - for(row = y, pos = 0; row < y+8; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; - for(col = x; col < x+8; ++col, ++pos) { - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width-1))*comp; - float r = dataR[p], g = dataG[p], b = dataB[p]; - Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; - U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; - V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; - } - } - - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); - } - } - } - - // Do the bit alignment of the EOI marker - stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); - } - - // EOI - stbiw__putc(s, 0xFF); - stbiw__putc(s, 0xD9); - - return 1; -} - -STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) -{ - stbi__write_context s = { 0 }; - stbi__start_write_callbacks(&s, func, context); - return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); -} - - -#ifndef STBI_WRITE_NO_STDIO -STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) -{ - stbi__write_context s = { 0 }; - if (stbi__start_write_file(&s,filename)) { - int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); - stbi__end_write_file(&s); - return r; - } else - return 0; -} -#endif - -#endif // STB_IMAGE_WRITE_IMPLEMENTATION - -/* Revision history - 1.16 (2021-07-11) - make Deflate code emit uncompressed blocks when it would otherwise expand - support writing BMPs with alpha channel - 1.15 (2020-07-13) unknown - 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels - 1.13 - 1.12 - 1.11 (2019-08-11) - - 1.10 (2019-02-07) - support utf8 filenames in Windows; fix warnings and platform ifdefs - 1.09 (2018-02-11) - fix typo in zlib quality API, improve STB_I_W_STATIC in C++ - 1.08 (2018-01-29) - add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter - 1.07 (2017-07-24) - doc fix - 1.06 (2017-07-23) - writing JPEG (using Jon Olick's code) - 1.05 ??? - 1.04 (2017-03-03) - monochrome BMP expansion - 1.03 ??? - 1.02 (2016-04-02) - avoid allocating large structures on the stack - 1.01 (2016-01-16) - STBIW_REALLOC_SIZED: support allocators with no realloc support - avoid race-condition in crc initialization - minor compile issues - 1.00 (2015-09-14) - installable file IO function - 0.99 (2015-09-13) - warning fixes; TGA rle support - 0.98 (2015-04-08) - added STBIW_MALLOC, STBIW_ASSERT etc - 0.97 (2015-01-18) - fixed HDR asserts, rewrote HDR rle logic - 0.96 (2015-01-17) - add HDR output - fix monochrome BMP - 0.95 (2014-08-17) - add monochrome TGA output - 0.94 (2014-05-31) - rename private functions to avoid conflicts with stb_image.h - 0.93 (2014-05-27) - warning fixes - 0.92 (2010-08-01) - casts to unsigned char to fix warnings - 0.91 (2010-07-17) - first public release - 0.90 first internal release -*/ - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -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 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. ------------------------------------------------------------------------------- -*/ diff --git a/vendor/lunasvg/plutovg/source/plutovg-stb-image.h b/vendor/lunasvg/plutovg/source/plutovg-stb-image.h deleted file mode 100644 index 3938baf..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-stb-image.h +++ /dev/null @@ -1,7988 +0,0 @@ -/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.30 (2024-05-31) avoid erroneous gcc warning - 2.29 (2023-05-xx) optimizations - 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff - 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine Simon Breuss (16-bit PNM) - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Eugene Golushkov Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko github:mosra - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - Jacko Dirks - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef PLUTOVG_STB_IMAGE_H -#define PLUTOVG_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data); -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// To query the width, height and component count of an image without having to -// decode the full file, you can use the stbi_info family of functions: -// -// int x,y,n,ok; -// ok = stbi_info(filename, &x, &y, &n); -// // returns ok=1 and sets x, y, n if image is a supported format, -// // 0 otherwise. -// -// Note that stb_image pervasively uses ints in its public API for sizes, -// including sizes of memory buffers. This is now part of the API and thus -// hard to change without causing breakage. As a result, the various image -// loaders all have certain limits on image size; these differ somewhat -// by format but generally boil down to either just under 2GB or just under -// 1GB. When the decoded image would be larger than this, stb_image decoding -// will fail. -// -// Additionally, stb_image will reject image files that have any of their -// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, -// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, -// the only way to have an image with such dimensions load correctly -// is for it to have a rather extreme aspect ratio. Either way, the -// assumption here is that such larger images are likely to be malformed -// or malicious. If you do need to load an image with individual dimensions -// larger than that, and it still fits in the overall size limit, you can -// #define STBI_MAX_DIMENSIONS on your own to be something larger. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// We optionally support converting iPhone-formatted PNGs (which store -// premultiplied BGRA) back to RGB, even though they're internally encoded -// differently. To enable this conversion, call -// stbi_convert_iphone_png_to_rgb(1). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // PLUTOVG_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#if defined(_MSC_VER) || defined(__SYMBIAN32__) -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -#ifdef _MSC_VER -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -#else -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__pnm_is16(stbi__context *s); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. -static int stbi__addints_valid(int a, int b) -{ - if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow - if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. - return a <= INT_MAX - b; -} - -// returns 1 if the product of two ints fits in a signed short, 0 on overflow. -static int stbi__mul2shorts_valid(int a, int b) -{ - if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow - if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid - if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN - return a >= SHRT_MIN / b; -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - // test the formats with a very explicit header first (at least a FOURCC - // or distinctive magic number first) - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - - // then the formats that can end up attempting to load with just 1 or 2 - // bytes matching expectations; these are prone to false positives, so - // try them later - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - z += (stbi__uint32)stbi__get16le(s) << 16; - return z; -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) { - for (j=0; j < count[i]; ++j) { - h->size[k++] = (stbi_uc) (i+1); - if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); - } - } - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - if(c < 0 || c >= 256) // symbol id out of bounds! - return -1; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - - sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & (sgn - 1)); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short) (dc * (1 << j->succ_low)); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * (1 << shift)); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios - // and I've never seen a non-corrupted JPEG file actually use them - for (i=0; i < s->img_n; ++i) { - if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) -{ - // some JPEGs have junk at end, skip over it but if we find what looks - // like a valid marker, resume there - while (!stbi__at_eof(j->s)) { - stbi_uc x = stbi__get8(j->s); - while (x == 0xff) { // might be a marker - if (stbi__at_eof(j->s)) return STBI__MARKER_none; - x = stbi__get8(j->s); - if (x != 0x00 && x != 0xff) { - // not a stuffed zero or lead-in to another marker, looks - // like an actual marker, return it - return x; - } - // stuffed zero has x=0 now which ends the loop, meaning we go - // back to regular scan loop. - // repeated 0xff keeps trying to read the next byte of the marker. - } - } - return STBI__MARKER_none; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - j->marker = stbi__skip_jpeg_junk_at_end(j); - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - m = stbi__get_marker(j); - if (STBI__RESTART(m)) - m = stbi__get_marker(j); - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - m = stbi__get_marker(j); - } else { - if (!stbi__process_marker(j, m)) return 1; - m = stbi__get_marker(j); - } - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // nothing to do if no components requested; check this now to avoid - // accessing uninitialized coutput[0] later - if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__errpuc("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - if (!j) return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) -#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[STBI__ZNSYMS]; - stbi__uint16 value[STBI__ZNSYMS]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - int hit_zeof_once; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - if (!a->hit_zeof_once) { - // This is the first time we hit eof, insert 16 extra padding btis - // to allow us to keep going; if we actually consume any of them - // though, that is invalid data. This is caught later. - a->hit_zeof_once = 1; - a->num_bits += 16; // add 16 implicit zero bits - } else { - // We already inserted our extra 16 padding bits and are again - // out, this stream is actually prematurely terminated. - return -1; - } - } else { - stbi__fill_bits(a); - } - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - if (a->hit_zeof_once && a->num_bits < 16) { - // The first time we hit zeof, we inserted 16 extra zero bits into our bit - // buffer so the decoder can just do its speculative decoding. But if we - // actually consumed any of those bits (which is the case when num_bits < 16), - // the stream actually read past the end so it is malformed. - return stbi__err("unexpected end","Corrupt PNG"); - } - return 1; - } - if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (len > a->zout_end - zout) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - a->hit_zeof_once = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filter used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub -}; - -static int stbi__paeth(int a, int b, int c) -{ - // This formulation looks very different from the reference in the PNG spec, but is - // actually equivalent and has favorable data dependencies and admits straightforward - // generation of branch-free code, which helps performance significantly. - int thresh = c*3 - (a + b); - int lo = a < b ? a : b; - int hi = a < b ? b : a; - int t0 = (hi <= thresh) ? lo : c; - int t1 = (thresh <= lo) ? hi : t0; - return t1; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// adds an extra all-255 alpha channel -// dest == src is legal -// img_n must be 1 or 3 -static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) -{ - int i; - // must process data backwards since we allow dest==src - if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - dest[i*2+1] = 255; - dest[i*2+0] = src[i]; - } - } else { - STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - dest[i*4+3] = 255; - dest[i*4+2] = src[i*3+2]; - dest[i*4+1] = src[i*3+1]; - dest[i*4+0] = src[i*3+0]; - } - } -} - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16 ? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - stbi_uc *filter_buf; - int all_ok = 1; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - // note: error exits here don't need to clean up a->out individually, - // stbi__do_png always does on error. - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - // Allocate two scan lines worth of filter workspace buffer. - filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); - if (!filter_buf) return stbi__err("outofmem", "Out of memory"); - - // Filtering for low-bit-depth images - if (depth < 8) { - filter_bytes = 1; - width = img_width_bytes; - } - - for (j=0; j < y; ++j) { - // cur/prior filter buffers alternate - stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; - stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; - stbi_uc *dest = a->out + stride*j; - int nk = width * filter_bytes; - int filter = *raw++; - - // check filter type - if (filter > 4) { - all_ok = stbi__err("invalid filter","Corrupt PNG"); - break; - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // perform actual filtering - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - case STBI__F_sub: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); - break; - case STBI__F_up: - for (k = 0; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - break; - case STBI__F_avg: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); - break; - case STBI__F_paeth: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); - break; - case STBI__F_avg_first: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); - break; - } - - raw += nk; - - // expand decoded bits in cur to dest, also adding an extra alpha channel if desired - if (depth < 8) { - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - stbi_uc *in = cur; - stbi_uc *out = dest; - stbi_uc inb = 0; - stbi__uint32 nsmp = x*img_n; - - // expand bits to bytes first - if (depth == 4) { - for (i=0; i < nsmp; ++i) { - if ((i & 1) == 0) inb = *in++; - *out++ = scale * (inb >> 4); - inb <<= 4; - } - } else if (depth == 2) { - for (i=0; i < nsmp; ++i) { - if ((i & 3) == 0) inb = *in++; - *out++ = scale * (inb >> 6); - inb <<= 2; - } - } else { - STBI_ASSERT(depth == 1); - for (i=0; i < nsmp; ++i) { - if ((i & 7) == 0) inb = *in++; - *out++ = scale * (inb >> 7); - inb <<= 1; - } - } - - // insert alpha=255 values if desired - if (img_n != out_n) - stbi__create_png_alpha_expand8(dest, dest, x, img_n); - } else if (depth == 8) { - if (img_n == out_n) - memcpy(dest, cur, x*img_n); - else - stbi__create_png_alpha_expand8(dest, cur, x, img_n); - } else if (depth == 16) { - // convert the image data from big-endian to platform-native - stbi__uint16 *dest16 = (stbi__uint16*)dest; - stbi__uint32 nsmp = x*img_n; - - if (img_n == out_n) { - for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) - *dest16 = (cur[0] << 8) | cur[1]; - } else { - STBI_ASSERT(img_n+1 == out_n); - if (img_n == 1) { - for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = 0xffff; - } - } else { - STBI_ASSERT(img_n == 3); - for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = (cur[2] << 8) | cur[3]; - dest16[2] = (cur[4] << 8) | cur[5]; - dest16[3] = 0xffff; - } - } - } - } - } - - STBI_FREE(filter_buf); - if (!all_ok) return 0; - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - if (!final) return stbi__err("outofmem", "Out of memory"); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load_global = 0; -static int stbi__de_iphone_flag_global = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_global = flag_true_if_should_convert; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global -#define stbi__de_iphone_flag stbi__de_iphone_flag_global -#else -static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; -static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; - -STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; - stbi__unpremultiply_on_load_set = 1; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_local = flag_true_if_should_convert; - stbi__de_iphone_flag_set = 1; -} - -#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ - ? stbi__unpremultiply_on_load_local \ - : stbi__unpremultiply_on_load_global) -#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ - ? stbi__de_iphone_flag_local \ - : stbi__de_iphone_flag_global) -#endif // STBI_THREAD_LOCAL - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - } - // even with SCAN_header, have to scan to see if we have a tRNS - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. - if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } - if (z->depth == 16) { - for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning - tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n && k < 3; ++k) - tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { - // header scan definitely stops at first IDAT - if (pal_img_n) - s->img_n = pal_img_n; - return 1; - } - if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) -{ - // BI_BITFIELDS specifies masks explicitly, don't override - if (compress == 3) - return 1; - - if (compress == 0) { - if (info->bpp == 16) { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } else if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; - } - return 0; // error -} - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - stbi__bmp_set_mask_defaults(info, compress); - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - // accept some number of extra bytes after the header, but if the offset points either to before - // the header ends or implies a large amount of extra data, reject the file as malformed - int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); - int header_limit = 1024; // max we actually read is below 256 bytes currently. - int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. - if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { - return stbi__errpuc("bad header", "Corrupt BMP"); - } - // we established that bytes_read_so_far is positive and sensible. - // the first half of this test rejects offsets that are either too small positives, or - // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn - // ensures the number computed in the second half of the test can't overflow. - if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } else { - stbi__skip(s, info.offset - bytes_read_so_far); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - if (!result) return stbi__errpuc("outofmem", "Out of memory"); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!g) return stbi__err("outofmem", "Out of memory"); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) -{ - STBI_FREE(g->out); - STBI_FREE(g->history); - STBI_FREE(g->background); - - if (out) STBI_FREE(out); - if (delays && *delays) STBI_FREE(*delays); - return stbi__errpuc("outofmem", "Out of memory"); -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; - - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); - - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } - - if (delays) { - int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - if (p == NULL) { - stbi__rewind( s ); - return 0; - } - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - STBI_NOTUSED(stbi__get32be(s)); - STBI_NOTUSED(stbi__get32be(s)); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); - if (ri->bits_per_channel == 0) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { - STBI_FREE(out); - return stbi__errpuc("bad PNM", "PNM file truncated"); - } - - if (req_comp && req_comp != s->img_n) { - if (ri->bits_per_channel == 16) { - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); - } else { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - } - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - if((value > 214748364) || (value == 214748364 && *c > '7')) - return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - if(*x == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - if (*y == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); - else if (maxv > 255) - return 16; - else - return 8; -} - -static int stbi__pnm_is16(stbi__context *s) -{ - if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) - return 1; - return 0; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_is16(s)) return 1; - #endif - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -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 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. ------------------------------------------------------------------------------- -*/ diff --git a/vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h b/vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h deleted file mode 100644 index a66c5b4..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h +++ /dev/null @@ -1,5077 +0,0 @@ -// stb_truetype.h - v1.26 - public domain -// authored from 2009-2021 by Sean Barrett / RAD Game Tools -// -// ======================================================================= -// -// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES -// -// This library does no range checking of the offsets found in the file, -// meaning an attacker can use it to read arbitrary memory. -// -// ======================================================================= -// -// This library processes TrueType files: -// parse files -// extract glyph metrics -// extract glyph shapes -// render glyphs to one-channel bitmaps with antialiasing (box filter) -// render glyphs to one-channel SDF bitmaps (signed-distance field/function) -// -// Todo: -// non-MS cmaps -// crashproof on bad data -// hinting? (no longer patented) -// cleartype-style AA? -// optimize: use simple memory allocator for intermediates -// optimize: build edge-list directly from curves -// optimize: rasterize directly from curves? -// -// ADDITIONAL CONTRIBUTORS -// -// Mikko Mononen: compound shape support, more cmap formats -// Tor Andersson: kerning, subpixel rendering -// Dougall Johnson: OpenType / Type 2 font handling -// Daniel Ribeiro Maciel: basic GPOS-based kerning -// -// Misc other: -// Ryan Gordon -// Simon Glass -// github:IntellectualKitty -// Imanol Celaya -// Daniel Ribeiro Maciel -// -// Bug/warning reports/fixes: -// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe -// Cass Everitt Martins Mozeiko github:aloucks -// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam -// Brian Hook Omar Cornut github:vassvik -// Walter van Niftrik Ryan Griege -// David Gow Peter LaValle -// David Given Sergey Popov -// Ivan-Assen Ivanov Giumo X. Clanjor -// Anthony Pesch Higor Euripedes -// Johan Duparc Thomas Fields -// Hou Qiming Derek Vinyard -// Rob Loach Cort Stratton -// Kenney Phillis Jr. Brian Costabile -// Ken Voskuil (kaesve) -// -// VERSION HISTORY -// -// 1.26 (2021-08-28) fix broken rasterizer -// 1.25 (2021-07-11) many fixes -// 1.24 (2020-02-05) fix warning -// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) -// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined -// 1.21 (2019-02-25) fix warning -// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() -// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// variant PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// -// Full history can be found at the end of this file. -// -// LICENSE -// -// See end of file for license information. -// -// USAGE -// -// Include this file in whatever places need to refer to it. In ONE C/C++ -// file, write: -// #define STB_TRUETYPE_IMPLEMENTATION -// before the #include of this file. This expands out the actual -// implementation into that C/C++ file. -// -// To make the implementation private to the file that generates the implementation, -// #define STBTT_STATIC -// -// Simple 3D API (don't ship this, but it's fine for tools and quick start) -// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture -// stbtt_GetBakedQuad() -- compute quad to draw for a given char -// -// Improved 3D API (more shippable): -// #include "stb_rect_pack.h" -- optional, but you really want it -// stbtt_PackBegin() -// stbtt_PackSetOversampling() -- for improved quality on small fonts -// stbtt_PackFontRanges() -- pack and renders -// stbtt_PackEnd() -// stbtt_GetPackedQuad() -// -// "Load" a font file from a memory buffer (you have to keep the buffer loaded) -// stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections -// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections -// -// Render a unicode codepoint to a bitmap -// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap -// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide -// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be -// -// Character advance/positioning -// stbtt_GetCodepointHMetrics() -// stbtt_GetFontVMetrics() -// stbtt_GetFontVMetricsOS2() -// stbtt_GetCodepointKernAdvance() -// -// Starting with version 1.06, the rasterizer was replaced with a new, -// faster and generally-more-precise rasterizer. The new rasterizer more -// accurately measures pixel coverage for anti-aliasing, except in the case -// where multiple shapes overlap, in which case it overestimates the AA pixel -// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If -// this turns out to be a problem, you can re-enable the old rasterizer with -// #define STBTT_RASTERIZER_VERSION 1 -// which will incur about a 15% speed hit. -// -// ADDITIONAL DOCUMENTATION -// -// Immediately after this block comment are a series of sample programs. -// -// After the sample programs is the "header file" section. This section -// includes documentation for each API function. -// -// Some important concepts to understand to use this library: -// -// Codepoint -// Characters are defined by unicode codepoints, e.g. 65 is -// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is -// the hiragana for "ma". -// -// Glyph -// A visual character shape (every codepoint is rendered as -// some glyph) -// -// Glyph index -// A font-specific integer ID representing a glyph -// -// Baseline -// Glyph shapes are defined relative to a baseline, which is the -// bottom of uppercase characters. Characters extend both above -// and below the baseline. -// -// Current Point -// As you draw text to the screen, you keep track of a "current point" -// which is the origin of each character. The current point's vertical -// position is the baseline. Even "baked fonts" use this model. -// -// Vertical Font Metrics -// The vertical qualities of the font, used to vertically position -// and space the characters. See docs for stbtt_GetFontVMetrics. -// -// Font Size in Pixels or Points -// The preferred interface for specifying font sizes in stb_truetype -// is to specify how tall the font's vertical extent should be in pixels. -// If that sounds good enough, skip the next paragraph. -// -// Most font APIs instead use "points", which are a common typographic -// measurement for describing font size, defined as 72 points per inch. -// stb_truetype provides a point API for compatibility. However, true -// "per inch" conventions don't make much sense on computer displays -// since different monitors have different number of pixels per -// inch. For example, Windows traditionally uses a convention that -// there are 96 pixels per inch, thus making 'inch' measurements have -// nothing to do with inches, and thus effectively defining a point to -// be 1.333 pixels. Additionally, the TrueType font data provides -// an explicit scale factor to scale a given font's glyphs to points, -// but the author has observed that this scale factor is often wrong -// for non-commercial fonts, thus making fonts scaled in points -// according to the TrueType spec incoherently sized in practice. -// -// DETAILED USAGE: -// -// Scale: -// Select how high you want the font to be, in points or pixels. -// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute -// a scale factor SF that will be used by all other functions. -// -// Baseline: -// You need to select a y-coordinate that is the baseline of where -// your text will appear. Call GetFontBoundingBox to get the baseline-relative -// bounding box for all characters. SF*-y0 will be the distance in pixels -// that the worst-case character could extend above the baseline, so if -// you want the top edge of characters to appear at the top of the -// screen where y=0, then you would set the baseline to SF*-y0. -// -// Current point: -// Set the current point where the first character will appear. The -// first character could extend left of the current point; this is font -// dependent. You can either choose a current point that is the leftmost -// point and hope, or add some padding, or check the bounding box or -// left-side-bearing of the first character to be displayed and set -// the current point based on that. -// -// Displaying a character: -// Compute the bounding box of the character. It will contain signed values -// relative to . I.e. if it returns x0,y0,x1,y1, -// then the character should be displayed in the rectangle from -// to = 32 && *text < 128) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 - glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); - glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); - glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); - glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); - } - ++text; - } - glEnd(); -} -#endif -// -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program (this compiles): get a single bitmap, print as ASCII art -// -#if 0 -#include -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "stb_truetype.h" - -char ttf_buffer[1<<25]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); - - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif -// -// Output: -// -// .ii. -// @@@@@@. -// V@Mio@@o -// :i. V@V -// :oM@@M -// :@@@MM@M -// @@o o@M -// :@@. M@M -// @@@o@@@@ -// :M@@V:@@. -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program: print "Hello World!" banner, with bugs -// -#if 0 -char buffer[24<<20]; -unsigned char screen[20][79]; - -int main(int arg, char **argv) -{ - stbtt_fontinfo font; - int i,j,ascent,baseline,ch=0; - float scale, xpos=2; // leave a little padding in case the character extends left - char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness - - fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); - stbtt_InitFont(&font, buffer, 0); - - scale = stbtt_ScaleForPixelHeight(&font, 15); - stbtt_GetFontVMetrics(&font, &ascent,0,0); - baseline = (int) (ascent*scale); - - while (text[ch]) { - int advance,lsb,x0,y0,x1,y1; - float x_shift = xpos - (float) floor(xpos); - stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); - stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); - stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); - // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong - // because this API is really for baking character bitmaps into textures. if you want to render - // a sequence of characters, you really need to render each bitmap to a temp buffer, then - // "alpha blend" that into the working buffer - xpos += (advance * scale); - if (text[ch+1]) - xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); - ++ch; - } - - for (j=0; j < 20; ++j) { - for (i=0; i < 78; ++i) - putchar(" .:ioVM@"[screen[j][i]>>5]); - putchar('\n'); - } - - return 0; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//// -//// INTEGRATION WITH YOUR CODEBASE -//// -//// The following sections allow you to supply alternate definitions -//// of C library functions used by stb_truetype, e.g. if you don't -//// link with the C runtime library. - -#ifdef STB_TRUETYPE_IMPLEMENTATION - // #define your own (u)stbtt_int8/16/32 before including to override this - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif - - typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; - typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - - // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h - #ifndef STBTT_ifloor - #include - #define STBTT_ifloor(x) ((int) floor(x)) - #define STBTT_iceil(x) ((int) ceil(x)) - #endif - - #ifndef STBTT_sqrt - #include - #define STBTT_sqrt(x) sqrt(x) - #define STBTT_pow(x,y) pow(x,y) - #endif - - #ifndef STBTT_fmod - #include - #define STBTT_fmod(x,y) fmod(x,y) - #endif - - #ifndef STBTT_cos - #include - #define STBTT_cos(x) cos(x) - #define STBTT_acos(x) acos(x) - #endif - - #ifndef STBTT_fabs - #include - #define STBTT_fabs(x) fabs(x) - #endif - - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h - #ifndef STBTT_malloc - #include - #define STBTT_malloc(x,u) ((void)(u),malloc(x)) - #define STBTT_free(x,u) ((void)(u),free(x)) - #endif - - #ifndef STBTT_assert - #include - #define STBTT_assert(x) assert(x) - #endif - - #ifndef STBTT_strlen - #include - #define STBTT_strlen(x) strlen(x) - #endif - - #ifndef STBTT_memcpy - #include - #define STBTT_memcpy memcpy - #define STBTT_memset memset - #endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// INTERFACE -//// -//// - -#ifndef PLUTOVG_STB_TRUETYPE_H -#define PLUTOVG_STB_TRUETYPE_H - -#ifdef STBTT_STATIC -#define STBTT_DEF static -#else -#define STBTT_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// private structure -typedef struct -{ - unsigned char *data; - int cursor; - int size; -} stbtt__buf; - -////////////////////////////////////////////////////////////////////////////// -// -// TEXTURE BAKING API -// -// If you use this API, you only have to call two functions ever. -// - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; -} stbtt_bakedchar; - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata); // you allocate this, it's num_chars long -// if return is positive, the first unused row of the bitmap -// if return is negative, returns the negative of the number of characters that fit -// if return is 0, no characters fit and no rows were used -// This uses a very crappy packing. - -typedef struct -{ - float x0,y0,s0,t0; // top-left - float x1,y1,s1,t1; // bottom-right -} stbtt_aligned_quad; - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier -// Call GetBakedQuad with char_index = 'character - first_char', and it -// creates the quad you need to draw and advances the current position. -// -// The coordinate system used assumes y increases downwards. -// -// Characters will extend both above and below the current position; -// see discussion of "BASELINE" above. -// -// It's inefficient; you might want to c&p it and optimize it. - -STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); -// Query the font vertical metrics without having to create a font first. - - -////////////////////////////////////////////////////////////////////////////// -// -// NEW TEXTURE BAKING API -// -// This provides options for packing multiple fonts into one atlas, not -// perfectly but better than nothing. - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; - float xoff2,yoff2; -} stbtt_packedchar; - -typedef struct stbtt_pack_context stbtt_pack_context; -typedef struct stbtt_fontinfo stbtt_fontinfo; -#ifndef STB_RECT_PACK_VERSION -typedef struct stbrp_rect stbrp_rect; -#endif - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); -// Initializes a packing context stored in the passed-in stbtt_pack_context. -// Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is width * height. stride_in_bytes is -// the distance from one row to the next (or 0 to mean they are packed tightly -// together). "padding" is the amount of padding to leave between each -// character (normally you want '1' for bitmaps you'll use as textures with -// bilinear filtering). -// -// Returns 0 on failure, 1 on success. - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); -// Cleans up the packing context and frees all memory. - -#define STBTT_POINT_SIZE(x) (-(x)) - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); -// Creates character bitmaps from the font_index'th font found in fontdata (use -// font_index=0 if you don't know what that is). It creates num_chars_in_range -// bitmaps for characters with unicode values starting at first_unicode_char_in_range -// and increasing. Data for how to render them is stored in chardata_for_range; -// pass these to stbtt_GetPackedQuad to get back renderable quads. -// -// font_size is the full height of the character from ascender to descender, -// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed -// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() -// and pass that result as 'font_size': -// ..., 20 , ... // font max minus min y is 20 pixels tall -// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall - -typedef struct -{ - float font_size; - int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint - int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints - int num_chars; - stbtt_packedchar *chardata_for_range; // output - unsigned char h_oversample, v_oversample; // don't set these, they're used internally -} stbtt_pack_range; - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); -// Creates character bitmaps from multiple ranges of characters stored in -// ranges. This will usually create a better-packed bitmap than multiple -// calls to stbtt_PackFontRange. Note that you can call this multiple -// times within a single PackBegin/PackEnd. - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); -// Oversampling a font increases the quality by allowing higher-quality subpixel -// positioning, and is especially valuable at smaller text sizes. -// -// This function sets the amount of oversampling for all following calls to -// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given -// pack context. The default (no oversampling) is achieved by h_oversample=1 -// and v_oversample=1. The total number of pixels required is -// h_oversample*v_oversample larger than the default; for example, 2x2 -// oversampling requires 4x the storage of 1x1. For best results, render -// oversampled textures with bilinear filtering. Look at the readme in -// stb/tests/oversample for information about oversampled fonts -// -// To use with PackFontRangesGather etc., you must set it before calls -// call to PackFontRangesGatherRects. - -STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); -// If skip != 0, this tells stb_truetype to skip any codepoints for which -// there is no corresponding glyph. If skip=0, which is the default, then -// codepoints without a glyph recived the font's "missing character" glyph, -// typically an empty box by convention. - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int align_to_integer); - -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -// Calling these functions in sequence is roughly equivalent to calling -// stbtt_PackFontRanges(). If you more control over the packing of multiple -// fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version -// using these functions, e.g. call GatherRects multiple times, -// building up a single array of rects, then call PackRects once, -// then call RenderIntoRects repeatedly. This may result in a -// better packing than calling PackFontRanges multiple times -// (or it may not). - -// this is an opaque structure that you shouldn't mess with which holds -// all the context needed from PackBegin to PackEnd. -struct stbtt_pack_context { - void *user_allocator_context; - void *pack_info; - int width; - int height; - int stride_in_bytes; - int padding; - int skip_missing; - unsigned int h_oversample, v_oversample; - unsigned char *pixels; - void *nodes; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// FONT LOADING -// -// - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); -// This function will determine the number of fonts in a font file. TrueType -// collection (.ttc) files may contain multiple fonts, while TrueType font -// (.ttf) files only contain one font. The number of fonts can be used for -// indexing with the previous function where the index is between zero and one -// less than the total fonts. If an error occurs, -1 is returned. - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -// Each .ttf/.ttc file may have more than one font. Each font has a sequential -// index number starting from 0. Call this function to get the font offset for -// a given index; it returns -1 if the index is out of range. A regular .ttf -// file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. - -// The following structure is defined publicly so you can declare one on -// the stack or as a global or etc, but you should treat it as opaque. -struct stbtt_fontinfo -{ - void * userdata; - unsigned char * data; // pointer to .ttf file - int fontstart; // offset of start of font - - int numGlyphs; // number of glyphs, needed for range checking - - int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf - int index_map; // a cmap mapping for our chosen character encoding - int indexToLocFormat; // format needed to map from glyph index to glyph - - stbtt__buf cff; // cff font data - stbtt__buf charstrings; // the charstring index - stbtt__buf gsubrs; // global charstring subroutines index - stbtt__buf subrs; // private charstring subroutines index - stbtt__buf fontdicts; // array of font dicts - stbtt__buf fdselect; // map from glyph to fontdict -}; - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -// Given an offset into the file that defines a font, this function builds -// the necessary cached info for the rest of the system. You must allocate -// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't -// need to do anything special to free it, because the contents are pure -// value data with no additional data structures. Returns 0 on failure. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER TO GLYPH-INDEX CONVERSIOn - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -// If you're going to perform multiple operations on the same character -// and you want a speed-up, call this function with the character you're -// going to process, then use glyph-based functions instead of the -// codepoint-based functions. -// Returns 0 if the character codepoint is not defined in the font. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER PROPERTIES -// - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose "height" is 'pixels' tall. -// Height is measured as the distance from the highest ascender to the lowest -// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics -// and computing: -// scale = pixels / (ascent - descent) -// so if you prefer to measure height by the ascent only, use a similar calculation. - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose EM size is mapped to -// 'pixels' tall. This is probably what traditional APIs compute, but -// I'm not positive. - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -// ascent is the coordinate above the baseline the font extends; descent -// is the coordinate below the baseline the font extends (i.e. it is typically negative) -// lineGap is the spacing between one row's descent and the next row's ascent... -// so you should advance the vertical position by "*ascent - *descent + *lineGap" -// these are expressed in unscaled coordinates, so you must multiply by -// the scale factor for a given size - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); -// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 -// table (specific to MS/Windows TTF files). -// -// Returns 1 on success (table present), 0 on failure. - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); -// the bounding box around all possible characters - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -// leftSideBearing is the offset from the current horizontal position to the left edge of the character -// advanceWidth is the offset from the current horizontal position to the next horizontal position -// these are expressed in unscaled coordinates - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -// an additional amount to add to the 'advance' value between ch1 and ch2 - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -// Gets the bounding box of the visible part of the glyph, in unscaled coordinates - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -// as above, but takes one or more glyph indices for greater efficiency - -typedef struct stbtt_kerningentry -{ - int glyph1; // use stbtt_FindGlyphIndex - int glyph2; - int advance; -} stbtt_kerningentry; - -STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); -STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); -// Retrieves a complete list of all of the kerning pairs provided by the font -// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. -// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) - -////////////////////////////////////////////////////////////////////////////// -// -// GLYPH SHAPES (you probably don't need these, but they have to go before -// the bitmaps for C declaration-order reasons) -// - -#ifndef STBTT_vmove // you can predefine these to use different values (but why?) - enum { - STBTT_vmove=1, - STBTT_vline, - STBTT_vcurve, - STBTT_vcubic - }; -#endif - -#ifndef stbtt_vertex // you can predefine this to use different values - // (we share this with other code at RAD) - #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file - typedef struct - { - stbtt_vertex_type x,y,cx,cy,cx1,cy1; - unsigned char type,padding; - } stbtt_vertex; -#endif - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); -// returns non-zero if nothing is drawn for this glyph - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -// returns # of vertices and fills *vertices with the pointer to them -// these are expressed in "unscaled" coordinates -// -// The shape is a series of contours. Each one starts with -// a STBTT_moveto, then consists of a series of mixed -// STBTT_lineto and STBTT_curveto segments. A lineto -// draws a line from previous endpoint to its x,y; a curveto -// draws a quadratic bezier from previous endpoint to -// its x,y, using cx,cy as the bezier control point. - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -// frees the data allocated above - -STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); -STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); -STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); -// fills svg with the character's SVG data. -// returns data size or 0 if SVG not found. - -////////////////////////////////////////////////////////////////////////////// -// -// BITMAP RENDERING -// - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -// frees the bitmap allocated below - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// allocates a large-enough single-channel 8bpp bitmap and renders the -// specified character/glyph at the specified scale into it, with -// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). -// *width & *height are filled out with the width & height of the bitmap, -// which is stored left-to-right, top-to-bottom. -// -// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap -// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap -// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the -// width and height and positioning info for it first. - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); -// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); -// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering -// is performed (see stbtt_PackSetOversampling) - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -// get the bbox of the bitmap centered around the glyph origin; so the -// bitmap width is ix1-ix0, height is iy1-iy0, and location to place -// the bitmap top left is (leftSideBearing*scale,iy0). -// (Note that the bitmap uses y-increases-down, but the shape uses -// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); -// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel -// shift for the character - -// the following functions are equivalent to the above functions, but operate -// on glyph indices instead of Unicode codepoints (for efficiency) -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); - - -// @TODO: don't expose this structure -typedef struct -{ - int w,h,stride; - unsigned char *pixels; -} stbtt__bitmap; - -// rasterize a shape with quadratic beziers into a bitmap -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into - float flatness_in_pixels, // allowable error of curve in pixels - stbtt_vertex *vertices, // array of vertices defining shape - int num_verts, // number of vertices in above array - float scale_x, float scale_y, // scale applied to input vertices - float shift_x, float shift_y, // translation applied to input vertices - int x_off, int y_off, // another translation applied to input - int invert, // if non-zero, vertically flip shape - void *userdata); // context for to STBTT_MALLOC - -////////////////////////////////////////////////////////////////////////////// -// -// Signed Distance Function (or Field) rendering - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); -// frees the SDF bitmap allocated below - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -// These functions compute a discretized SDF field for a single character, suitable for storing -// in a single-channel texture, sampling with bilinear filtering, and testing against -// larger than some threshold to produce scalable fonts. -// info -- the font -// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap -// glyph/codepoint -- the character to generate the SDF for -// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), -// which allows effects like bit outlines -// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) -// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) -// if positive, > onedge_value is inside; if negative, < onedge_value is inside -// width,height -- output height & width of the SDF bitmap (including padding) -// xoff,yoff -- output origin of the character -// return value -- a 2D array of bytes 0..255, width*height in size -// -// pixel_dist_scale & onedge_value are a scale & bias that allows you to make -// optimal use of the limited 0..255 for your application, trading off precision -// and special effects. SDF values outside the range 0..255 are clamped to 0..255. -// -// Example: -// scale = stbtt_ScaleForPixelHeight(22) -// padding = 5 -// onedge_value = 180 -// pixel_dist_scale = 180/5.0 = 36.0 -// -// This will create an SDF bitmap in which the character is about 22 pixels -// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled -// shape, sample the SDF at each pixel and fill the pixel if the SDF value -// is greater than or equal to 180/255. (You'll actually want to antialias, -// which is beyond the scope of this example.) Additionally, you can compute -// offset outlines (e.g. to stroke the character border inside & outside, -// or only outside). For example, to fill outside the character up to 3 SDF -// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above -// choice of variables maps a range from 5 pixels outside the shape to -// 2 pixels inside the shape to 0..255; this is intended primarily for apply -// outside effects only (the interior range is needed to allow proper -// antialiasing of the font at *smaller* sizes) -// -// The function computes the SDF analytically at each SDF pixel, not by e.g. -// building a higher-res bitmap and approximating it. In theory the quality -// should be as high as possible for an SDF of this size & representation, but -// unclear if this is true in practice (perhaps building a higher-res bitmap -// and computing from that can allow drop-out prevention). -// -// The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. - - - -////////////////////////////////////////////////////////////////////////////// -// -// Finding the right font... -// -// You should really just solve this offline, keep your own tables -// of what font is what, and don't try to get it out of the .ttf file. -// That's because getting it out of the .ttf file is really hard, because -// the names in the file can appear in many possible encodings, in many -// possible languages, and e.g. if you need a case-insensitive comparison, -// the details of that depend on the encoding & language in a complex way -// (actually underspecified in truetype, but also gigantic). -// -// But you can use the provided functions in two possible ways: -// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on -// unicode-encoded names to try to find the font you want; -// you can run this before calling stbtt_InitFont() -// -// stbtt_GetFontNameString() lets you get any of the various strings -// from the file yourself and do your own comparisons on them. -// You have to have called stbtt_InitFont() first. - - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -// returns the offset (not index) of the font that matches, or -1 if none -// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". -// if you use any other flag, use a font name like "Arial"; this checks -// the 'macStyle' header field; i don't know if fonts set this consistently -#define STBTT_MACSTYLE_DONTCARE 0 -#define STBTT_MACSTYLE_BOLD 1 -#define STBTT_MACSTYLE_ITALIC 2 -#define STBTT_MACSTYLE_UNDERSCORE 4 -#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -// returns 1/0 whether the first string interpreted as utf8 is identical to -// the second string interpreted as big-endian utf16... useful for strings from next func - -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -// returns the string (which may be big-endian double byte, e.g. for unicode) -// and puts the length in bytes in *length. -// -// some of the values for the IDs are below; for more see the truetype spec: -// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html -// http://www.microsoft.com/typography/otspec/name.htm - -enum { // platformID - STBTT_PLATFORM_ID_UNICODE =0, - STBTT_PLATFORM_ID_MAC =1, - STBTT_PLATFORM_ID_ISO =2, - STBTT_PLATFORM_ID_MICROSOFT =3 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_UNICODE - STBTT_UNICODE_EID_UNICODE_1_0 =0, - STBTT_UNICODE_EID_UNICODE_1_1 =1, - STBTT_UNICODE_EID_ISO_10646 =2, - STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, - STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT - STBTT_MS_EID_SYMBOL =0, - STBTT_MS_EID_UNICODE_BMP =1, - STBTT_MS_EID_SHIFTJIS =2, - STBTT_MS_EID_UNICODE_FULL =10 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes - STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, - STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, - STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, - STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 -}; - -enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... - // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs - STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, - STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, - STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, - STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, - STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, - STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D -}; - -enum { // languageID for STBTT_PLATFORM_ID_MAC - STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, - STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, - STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, - STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , - STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , - STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, - STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 -}; - -#ifdef __cplusplus -} -#endif - -#endif // PLUTOVG_STB_TRUETYPE_H - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// IMPLEMENTATION -//// -//// - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -#ifndef STBTT_MAX_OVERSAMPLE -#define STBTT_MAX_OVERSAMPLE 8 -#endif - -#if STBTT_MAX_OVERSAMPLE > 255 -#error "STBTT_MAX_OVERSAMPLE cannot be > 255" -#endif - -typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; - -#ifndef STBTT_RASTERIZER_VERSION -#define STBTT_RASTERIZER_VERSION 2 -#endif - -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif - -////////////////////////////////////////////////////////////////////////// -// -// stbtt__buf helpers to parse data from file -// - -static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor++]; -} - -static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor]; -} - -static void stbtt__buf_seek(stbtt__buf *b, int o) -{ - STBTT_assert(!(o > b->size || o < 0)); - b->cursor = (o > b->size || o < 0) ? b->size : o; -} - -static void stbtt__buf_skip(stbtt__buf *b, int o) -{ - stbtt__buf_seek(b, b->cursor + o); -} - -static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) -{ - stbtt_uint32 v = 0; - int i; - STBTT_assert(n >= 1 && n <= 4); - for (i = 0; i < n; i++) - v = (v << 8) | stbtt__buf_get8(b); - return v; -} - -static stbtt__buf stbtt__new_buf(const void *p, size_t size) -{ - stbtt__buf r; - STBTT_assert(size < 0x40000000); - r.data = (stbtt_uint8*) p; - r.size = (int) size; - r.cursor = 0; - return r; -} - -#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) -#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) - -static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) -{ - stbtt__buf r = stbtt__new_buf(NULL, 0); - if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; - r.data = b->data + o; - r.size = s; - return r; -} - -static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) -{ - int count, start, offsize; - start = b->cursor; - count = stbtt__buf_get16(b); - if (count) { - offsize = stbtt__buf_get8(b); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(b, offsize * count); - stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); - } - return stbtt__buf_range(b, start, b->cursor - start); -} - -static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) -{ - int b0 = stbtt__buf_get8(b); - if (b0 >= 32 && b0 <= 246) return b0 - 139; - else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; - else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; - else if (b0 == 28) return stbtt__buf_get16(b); - else if (b0 == 29) return stbtt__buf_get32(b); - STBTT_assert(0); - return 0; -} - -static void stbtt__cff_skip_operand(stbtt__buf *b) { - int v, b0 = stbtt__buf_peek8(b); - STBTT_assert(b0 >= 28); - if (b0 == 30) { - stbtt__buf_skip(b, 1); - while (b->cursor < b->size) { - v = stbtt__buf_get8(b); - if ((v & 0xF) == 0xF || (v >> 4) == 0xF) - break; - } - } else { - stbtt__cff_int(b); - } -} - -static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) -{ - stbtt__buf_seek(b, 0); - while (b->cursor < b->size) { - int start = b->cursor, end, op; - while (stbtt__buf_peek8(b) >= 28) - stbtt__cff_skip_operand(b); - end = b->cursor; - op = stbtt__buf_get8(b); - if (op == 12) op = stbtt__buf_get8(b) | 0x100; - if (op == key) return stbtt__buf_range(b, start, end-start); - } - return stbtt__buf_range(b, 0, 0); -} - -static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) -{ - int i; - stbtt__buf operands = stbtt__dict_get(b, key); - for (i = 0; i < outcount && operands.cursor < operands.size; i++) - out[i] = stbtt__cff_int(&operands); -} - -static int stbtt__cff_index_count(stbtt__buf *b) -{ - stbtt__buf_seek(b, 0); - return stbtt__buf_get16(b); -} - -static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) -{ - int count, offsize, start, end; - stbtt__buf_seek(&b, 0); - count = stbtt__buf_get16(&b); - offsize = stbtt__buf_get8(&b); - STBTT_assert(i >= 0 && i < count); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(&b, i*offsize); - start = stbtt__buf_get(&b, offsize); - end = stbtt__buf_get(&b, offsize); - return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); -} - -////////////////////////////////////////////////////////////////////////// -// -// accessors to parse data from file -// - -// on platforms that don't allow misaligned reads, if we want to allow -// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE - -#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -#define ttCHAR(p) (* (stbtt_int8 *) (p)) -#define ttFixed(p) ttLONG(p) - -static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) - -static int stbtt__isfont(stbtt_uint8 *font) -{ - // check the version number - if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 - if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! - if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF - if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 - if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts - return 0; -} - -// @OPTIMIZE: binary search -static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -{ - stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); - stbtt_uint32 tabledir = fontstart + 12; - stbtt_int32 i; - for (i=0; i < num_tables; ++i) { - stbtt_uint32 loc = tabledir + 16*i; - if (stbtt_tag(data+loc+0, tag)) - return ttULONG(data+loc+8); - } - return 0; -} - -static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) -{ - // if it's just a font, there's only one valid index - if (stbtt__isfont(font_collection)) - return index == 0 ? 0 : -1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - stbtt_int32 n = ttLONG(font_collection+8); - if (index >= n) - return -1; - return ttULONG(font_collection+12+index*4); - } - } - return -1; -} - -static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) -{ - // if it's just a font, there's only one valid font - if (stbtt__isfont(font_collection)) - return 1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - return ttLONG(font_collection+8); - } - } - return 0; -} - -static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) -{ - stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; - stbtt__buf pdict; - stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); - if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); - pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); - stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); - if (!subrsoff) return stbtt__new_buf(NULL, 0); - stbtt__buf_seek(&cff, private_loc[1]+subrsoff); - return stbtt__cff_get_index(&cff); -} - -// since most people won't use this, find this table the first time it's needed -static int stbtt__get_svg(stbtt_fontinfo *info) -{ - stbtt_uint32 t; - if (info->svg < 0) { - t = stbtt__find_table(info->data, info->fontstart, "SVG "); - if (t) { - stbtt_uint32 offset = ttULONG(info->data + t + 2); - info->svg = t + offset; - } else { - info->svg = 0; - } - } - return info->svg; -} - -static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) -{ - stbtt_uint32 cmap, t; - stbtt_int32 i,numTables; - - info->data = data; - info->fontstart = fontstart; - info->cff = stbtt__new_buf(NULL, 0); - - cmap = stbtt__find_table(data, fontstart, "cmap"); // required - info->loca = stbtt__find_table(data, fontstart, "loca"); // required - info->head = stbtt__find_table(data, fontstart, "head"); // required - info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required - info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required - info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required - info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required - - if (!cmap || !info->head || !info->hhea || !info->hmtx) - return 0; - if (info->glyf) { - // required for truetype - if (!info->loca) return 0; - } else { - // initialization for CFF / Type2 fonts (OTF) - stbtt__buf b, topdict, topdictidx; - stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; - stbtt_uint32 cff; - - cff = stbtt__find_table(data, fontstart, "CFF "); - if (!cff) return 0; - - info->fontdicts = stbtt__new_buf(NULL, 0); - info->fdselect = stbtt__new_buf(NULL, 0); - - // @TODO this should use size from table (not 512MB) - info->cff = stbtt__new_buf(data+cff, 512*1024*1024); - b = info->cff; - - // read the header - stbtt__buf_skip(&b, 2); - stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize - - // @TODO the name INDEX could list multiple fonts, - // but we just use the first one. - stbtt__cff_get_index(&b); // name INDEX - topdictidx = stbtt__cff_get_index(&b); - topdict = stbtt__cff_index_get(topdictidx, 0); - stbtt__cff_get_index(&b); // string INDEX - info->gsubrs = stbtt__cff_get_index(&b); - - stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); - stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); - stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); - stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); - info->subrs = stbtt__get_subrs(b, topdict); - - // we only support Type 2 charstrings - if (cstype != 2) return 0; - if (charstrings == 0) return 0; - - if (fdarrayoff) { - // looks like a CID font - if (!fdselectoff) return 0; - stbtt__buf_seek(&b, fdarrayoff); - info->fontdicts = stbtt__cff_get_index(&b); - info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); - } - - stbtt__buf_seek(&b, charstrings); - info->charstrings = stbtt__cff_get_index(&b); - } - - t = stbtt__find_table(data, fontstart, "maxp"); - if (t) - info->numGlyphs = ttUSHORT(data+t+4); - else - info->numGlyphs = 0xffff; - - info->svg = -1; - - // find a cmap encoding table we understand *now* to avoid searching - // later. (todo: could make this installable) - // the same regardless of glyph. - numTables = ttUSHORT(data + cmap + 2); - info->index_map = 0; - for (i=0; i < numTables; ++i) { - stbtt_uint32 encoding_record = cmap + 4 + 8 * i; - // find an encoding we understand: - switch(ttUSHORT(data+encoding_record)) { - case STBTT_PLATFORM_ID_MICROSOFT: - switch (ttUSHORT(data+encoding_record+2)) { - case STBTT_MS_EID_UNICODE_BMP: - case STBTT_MS_EID_UNICODE_FULL: - // MS/Unicode - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - break; - case STBTT_PLATFORM_ID_UNICODE: - // Mac/iOS has these - // all the encodingIDs are unicode, so we don't bother to check it - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - } - if (info->index_map == 0) - return 0; - - info->indexToLocFormat = ttUSHORT(data+info->head + 50); - return 1; -} - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -{ - stbtt_uint8 *data = info->data; - stbtt_uint32 index_map = info->index_map; - - stbtt_uint16 format = ttUSHORT(data + index_map + 0); - if (format == 0) { // apple byte encoding - stbtt_int32 bytes = ttUSHORT(data + index_map + 2); - if (unicode_codepoint < bytes-6) - return ttBYTE(data + index_map + 6 + unicode_codepoint); - return 0; - } else if (format == 6) { - stbtt_uint32 first = ttUSHORT(data + index_map + 6); - stbtt_uint32 count = ttUSHORT(data + index_map + 8); - if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) - return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); - return 0; - } else if (format == 2) { - STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean - return 0; - } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges - stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; - stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; - stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); - stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - - // do a binary search of the segments - stbtt_uint32 endCount = index_map + 14; - stbtt_uint32 search = endCount; - - if (unicode_codepoint > 0xffff) - return 0; - - // they lie from endCount .. endCount + segCount - // but searchRange is the nearest power of two, so... - if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) - search += rangeShift*2; - - // now decrement to bias correctly to find smallest - search -= 2; - while (entrySelector) { - stbtt_uint16 end; - searchRange >>= 1; - end = ttUSHORT(data + search + searchRange*2); - if (unicode_codepoint > end) - search += searchRange*2; - --entrySelector; - } - search += 2; - - { - stbtt_uint16 offset, start, last; - stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - last = ttUSHORT(data + endCount + 2*item); - if (unicode_codepoint < start || unicode_codepoint > last) - return 0; - - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); - } - } else if (format == 12 || format == 13) { - stbtt_uint32 ngroups = ttULONG(data+index_map+12); - stbtt_int32 low,high; - low = 0; high = (stbtt_int32)ngroups; - // Binary search the right group. - while (low < high) { - stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high - stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); - stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); - if ((stbtt_uint32) unicode_codepoint < start_char) - high = mid; - else if ((stbtt_uint32) unicode_codepoint > end_char) - low = mid+1; - else { - stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); - if (format == 12) - return start_glyph + unicode_codepoint-start_char; - else // format == 13 - return start_glyph; - } - } - return 0; // not found - } - // @TODO - STBTT_assert(0); - return 0; -} - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -{ - return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -} - -static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) -{ - v->type = type; - v->x = (stbtt_int16) x; - v->y = (stbtt_int16) y; - v->cx = (stbtt_int16) cx; - v->cy = (stbtt_int16) cy; -} - -static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -{ - int g1,g2; - - STBTT_assert(!info->cff.size); - - if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range - if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format - - if (info->indexToLocFormat == 0) { - g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; - g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; - } else { - g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); - g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); - } - - return g1==g2 ? -1 : g1; // if length is 0, return -1 -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); - -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - if (info->cff.size) { - stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); - } else { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; - - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); - } - return 1; -} - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -{ - return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -} - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt_int16 numberOfContours; - int g; - if (info->cff.size) - return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; - g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 1; - numberOfContours = ttSHORT(info->data + g); - return numberOfContours == 0; -} - -static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, - stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) -{ - if (start_off) { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); - } - return num_vertices; -} - -static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - stbtt_int16 numberOfContours; - stbtt_uint8 *endPtsOfContours; - stbtt_uint8 *data = info->data; - stbtt_vertex *vertices=0; - int num_vertices=0; - int g = stbtt__GetGlyfOffset(info, glyph_index); - - *pvertices = NULL; - - if (g < 0) return 0; - - numberOfContours = ttSHORT(data + g); - - if (numberOfContours > 0) { - stbtt_uint8 flags=0,flagcount; - stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; - stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; - stbtt_uint8 *points; - endPtsOfContours = (data + g + 10); - ins = ttUSHORT(data + g + 10 + numberOfContours * 2); - points = data + g + 10 + numberOfContours * 2 + 2 + ins; - - n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); - - m = n + 2*numberOfContours; // a loose bound on how many vertices we might need - vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); - if (vertices == 0) - return 0; - - next_move = 0; - flagcount=0; - - // in first pass, we load uninterpreted data into the allocated array - // above, shifted to the end of the array so we won't overwrite it when - // we create our final data starting from the front - - off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated - - // first load flags - - for (i=0; i < n; ++i) { - if (flagcount == 0) { - flags = *points++; - if (flags & 8) - flagcount = *points++; - } else - --flagcount; - vertices[off+i].type = flags; - } - - // now load x coordinates - x=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 2) { - stbtt_int16 dx = *points++; - x += (flags & 16) ? dx : -dx; // ??? - } else { - if (!(flags & 16)) { - x = x + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].x = (stbtt_int16) x; - } - - // now load y coordinates - y=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 4) { - stbtt_int16 dy = *points++; - y += (flags & 32) ? dy : -dy; // ??? - } else { - if (!(flags & 32)) { - y = y + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].y = (stbtt_int16) y; - } - - // now convert them to our format - num_vertices=0; - sx = sy = cx = cy = scx = scy = 0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - x = (stbtt_int16) vertices[off+i].x; - y = (stbtt_int16) vertices[off+i].y; - - if (next_move == i) { - if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - - // now start the new one - start_off = !(flags & 1); - if (start_off) { - // if we start off with an off-curve point, then when we need to find a point on the curve - // where we can start, and we need to save some state for when we wraparound. - scx = x; - scy = y; - if (!(vertices[off+i+1].type & 1)) { - // next point is also a curve point, so interpolate an on-point curve - sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; - sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; - } else { - // otherwise just use the next point as our start point - sx = (stbtt_int32) vertices[off+i+1].x; - sy = (stbtt_int32) vertices[off+i+1].y; - ++i; // we're using point i+1 as the starting point, so skip it - } - } else { - sx = x; - sy = y; - } - stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); - was_off = 0; - next_move = 1 + ttUSHORT(endPtsOfContours+j*2); - ++j; - } else { - if (!(flags & 1)) { // if it's a curve - if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); - cx = x; - cy = y; - was_off = 1; - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); - was_off = 0; - } - } - } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours < 0) { - // Compound shapes. - int more = 1; - stbtt_uint8 *comp = data + g + 10; - num_vertices = 0; - vertices = 0; - while (more) { - stbtt_uint16 flags, gidx; - int comp_num_verts = 0, i; - stbtt_vertex *comp_verts = 0, *tmp = 0; - float mtx[6] = {1,0,0,1,0,0}, m, n; - - flags = ttSHORT(comp); comp+=2; - gidx = ttSHORT(comp); comp+=2; - - if (flags & 2) { // XY values - if (flags & 1) { // shorts - mtx[4] = ttSHORT(comp); comp+=2; - mtx[5] = ttSHORT(comp); comp+=2; - } else { - mtx[4] = ttCHAR(comp); comp+=1; - mtx[5] = ttCHAR(comp); comp+=1; - } - } - else { - // @TODO handle matching point - STBTT_assert(0); - } - if (flags & (1<<3)) { // WE_HAVE_A_SCALE - mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } - - // Find transformation scales. - m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); - n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); - - // Get indexed glyph. - comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); - if (comp_num_verts > 0) { - // Transform vertices. - for (i = 0; i < comp_num_verts; ++i) { - stbtt_vertex* v = &comp_verts[i]; - stbtt_vertex_type x,y; - x=v->x; y=v->y; - v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - x=v->cx; y=v->cy; - v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - } - // Append vertices. - tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); - if (!tmp) { - if (vertices) STBTT_free(vertices, info->userdata); - if (comp_verts) STBTT_free(comp_verts, info->userdata); - return 0; - } - if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); - STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); - if (vertices) STBTT_free(vertices, info->userdata); - vertices = tmp; - STBTT_free(comp_verts, info->userdata); - num_vertices += comp_num_verts; - } - // More components ? - more = flags & (1<<5); - } - } else { - // numberOfCounters == 0, do nothing - } - - *pvertices = vertices; - return num_vertices; -} - -typedef struct -{ - int bounds; - int started; - float first_x, first_y; - float x, y; - stbtt_int32 min_x, max_x, min_y, max_y; - - stbtt_vertex *pvertices; - int num_vertices; -} stbtt__csctx; - -#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} - -static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) -{ - if (x > c->max_x || !c->started) c->max_x = x; - if (y > c->max_y || !c->started) c->max_y = y; - if (x < c->min_x || !c->started) c->min_x = x; - if (y < c->min_y || !c->started) c->min_y = y; - c->started = 1; -} - -static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) -{ - if (c->bounds) { - stbtt__track_vertex(c, x, y); - if (type == STBTT_vcubic) { - stbtt__track_vertex(c, cx, cy); - stbtt__track_vertex(c, cx1, cy1); - } - } else { - stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); - c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; - c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; - } - c->num_vertices++; -} - -static void stbtt__csctx_close_shape(stbtt__csctx *ctx) -{ - if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) -{ - stbtt__csctx_close_shape(ctx); - ctx->first_x = ctx->x = ctx->x + dx; - ctx->first_y = ctx->y = ctx->y + dy; - stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) -{ - ctx->x += dx; - ctx->y += dy; - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) -{ - float cx1 = ctx->x + dx1; - float cy1 = ctx->y + dy1; - float cx2 = cx1 + dx2; - float cy2 = cy1 + dy2; - ctx->x = cx2 + dx3; - ctx->y = cy2 + dy3; - stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); -} - -static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) -{ - int count = stbtt__cff_index_count(&idx); - int bias = 107; - if (count >= 33900) - bias = 32768; - else if (count >= 1240) - bias = 1131; - n += bias; - if (n < 0 || n >= count) - return stbtt__new_buf(NULL, 0); - return stbtt__cff_index_get(idx, n); -} - -static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt__buf fdselect = info->fdselect; - int nranges, start, end, v, fmt, fdselector = -1, i; - - stbtt__buf_seek(&fdselect, 0); - fmt = stbtt__buf_get8(&fdselect); - if (fmt == 0) { - // untested - stbtt__buf_skip(&fdselect, glyph_index); - fdselector = stbtt__buf_get8(&fdselect); - } else if (fmt == 3) { - nranges = stbtt__buf_get16(&fdselect); - start = stbtt__buf_get16(&fdselect); - for (i = 0; i < nranges; i++) { - v = stbtt__buf_get8(&fdselect); - end = stbtt__buf_get16(&fdselect); - if (glyph_index >= start && glyph_index < end) { - fdselector = v; - break; - } - start = end; - } - } - if (fdselector == -1) stbtt__new_buf(NULL, 0); - return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); -} - -static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) -{ - int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; - int has_subrs = 0, clear_stack; - float s[48]; - stbtt__buf subr_stack[10], subrs = info->subrs, b; - float f; - -#define STBTT__CSERR(s) (0) - - // this currently ignores the initial width value, which isn't needed if we have hmtx - b = stbtt__cff_index_get(info->charstrings, glyph_index); - while (b.cursor < b.size) { - i = 0; - clear_stack = 1; - b0 = stbtt__buf_get8(&b); - switch (b0) { - // @TODO implement hinting - case 0x13: // hintmask - case 0x14: // cntrmask - if (in_header) - maskbits += (sp / 2); // implicit "vstem" - in_header = 0; - stbtt__buf_skip(&b, (maskbits + 7) / 8); - break; - - case 0x01: // hstem - case 0x03: // vstem - case 0x12: // hstemhm - case 0x17: // vstemhm - maskbits += (sp / 2); - break; - - case 0x15: // rmoveto - in_header = 0; - if (sp < 2) return STBTT__CSERR("rmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); - break; - case 0x04: // vmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("vmoveto stack"); - stbtt__csctx_rmove_to(c, 0, s[sp-1]); - break; - case 0x16: // hmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("hmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-1], 0); - break; - - case 0x05: // rlineto - if (sp < 2) return STBTT__CSERR("rlineto stack"); - for (; i + 1 < sp; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical - // starting from a different place. - - case 0x07: // vlineto - if (sp < 1) return STBTT__CSERR("vlineto stack"); - goto vlineto; - case 0x06: // hlineto - if (sp < 1) return STBTT__CSERR("hlineto stack"); - for (;;) { - if (i >= sp) break; - stbtt__csctx_rline_to(c, s[i], 0); - i++; - vlineto: - if (i >= sp) break; - stbtt__csctx_rline_to(c, 0, s[i]); - i++; - } - break; - - case 0x1F: // hvcurveto - if (sp < 4) return STBTT__CSERR("hvcurveto stack"); - goto hvcurveto; - case 0x1E: // vhcurveto - if (sp < 4) return STBTT__CSERR("vhcurveto stack"); - for (;;) { - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); - i += 4; - hvcurveto: - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); - i += 4; - } - break; - - case 0x08: // rrcurveto - if (sp < 6) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x18: // rcurveline - if (sp < 8) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp - 2; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - case 0x19: // rlinecurve - if (sp < 8) return STBTT__CSERR("rlinecurve stack"); - for (; i + 1 < sp - 6; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x1A: // vvcurveto - case 0x1B: // hhcurveto - if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); - f = 0.0; - if (sp & 1) { f = s[i]; i++; } - for (; i + 3 < sp; i += 4) { - if (b0 == 0x1B) - stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); - else - stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); - f = 0.0; - } - break; - - case 0x0A: // callsubr - if (!has_subrs) { - if (info->fdselect.size) - subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); - has_subrs = 1; - } - // FALLTHROUGH - case 0x1D: // callgsubr - if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); - v = (int) s[--sp]; - if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); - subr_stack[subr_stack_height++] = b; - b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); - if (b.size == 0) return STBTT__CSERR("subr not found"); - b.cursor = 0; - clear_stack = 0; - break; - - case 0x0B: // return - if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); - b = subr_stack[--subr_stack_height]; - clear_stack = 0; - break; - - case 0x0E: // endchar - stbtt__csctx_close_shape(c); - return 1; - - case 0x0C: { // two-byte escape - float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; - float dx, dy; - int b1 = stbtt__buf_get8(&b); - switch (b1) { - // @TODO These "flex" implementations ignore the flex-depth and resolution, - // and always draw beziers. - case 0x22: // hflex - if (sp < 7) return STBTT__CSERR("hflex stack"); - dx1 = s[0]; - dx2 = s[1]; - dy2 = s[2]; - dx3 = s[3]; - dx4 = s[4]; - dx5 = s[5]; - dx6 = s[6]; - stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); - break; - - case 0x23: // flex - if (sp < 13) return STBTT__CSERR("flex stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = s[10]; - dy6 = s[11]; - //fd is s[12] - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - case 0x24: // hflex1 - if (sp < 9) return STBTT__CSERR("hflex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dx4 = s[5]; - dx5 = s[6]; - dy5 = s[7]; - dx6 = s[8]; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); - break; - - case 0x25: // flex1 - if (sp < 11) return STBTT__CSERR("flex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = dy6 = s[10]; - dx = dx1+dx2+dx3+dx4+dx5; - dy = dy1+dy2+dy3+dy4+dy5; - if (STBTT_fabs(dx) > STBTT_fabs(dy)) - dy6 = -dy; - else - dx6 = -dx; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - default: - return STBTT__CSERR("unimplemented"); - } - } break; - - default: - if (b0 != 255 && b0 != 28 && b0 < 32) - return STBTT__CSERR("reserved operator"); - - // push immediate - if (b0 == 255) { - f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; - } else { - stbtt__buf_skip(&b, -1); - f = (float)(stbtt_int16)stbtt__cff_int(&b); - } - if (sp >= 48) return STBTT__CSERR("push stack overflow"); - s[sp++] = f; - clear_stack = 0; - break; - } - if (clear_stack) sp = 0; - } - return STBTT__CSERR("no endchar"); - -#undef STBTT__CSERR -} - -static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - // runs the charstring twice, once to count and once to output (to avoid realloc) - stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); - stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); - if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { - *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); - output_ctx.pvertices = *pvertices; - if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { - STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); - return output_ctx.num_vertices; - } - } - *pvertices = NULL; - return 0; -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - stbtt__csctx c = STBTT__CSCTX_INIT(1); - int r = stbtt__run_charstring(info, glyph_index, &c); - if (x0) *x0 = r ? c.min_x : 0; - if (y0) *y0 = r ? c.min_y : 0; - if (x1) *x1 = r ? c.max_x : 0; - if (y1) *y1 = r ? c.max_y : 0; - return r ? c.num_vertices : 0; -} - -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - if (!info->cff.size) - return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); - else - return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); -} - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -{ - stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); - if (glyph_index < numOfLongHorMetrics) { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); - } else { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); - } -} - -STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) -{ - stbtt_uint8 *data = info->data + info->kern; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - return ttUSHORT(data+10); -} - -STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) -{ - stbtt_uint8 *data = info->data + info->kern; - int k, length; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - length = ttUSHORT(data+10); - if (table_length < length) - length = table_length; - - for (k = 0; k < length; k++) - { - table[k].glyph1 = ttUSHORT(data+18+(k*6)); - table[k].glyph2 = ttUSHORT(data+20+(k*6)); - table[k].advance = ttSHORT(data+22+(k*6)); - } - - return length; -} - -static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint8 *data = info->data + info->kern; - stbtt_uint32 needle, straw; - int l, r, m; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - l = 0; - r = ttUSHORT(data+10) - 1; - needle = glyph1 << 16 | glyph2; - while (l <= r) { - m = (l + r) >> 1; - straw = ttULONG(data+18+(m*6)); // note: unaligned read - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else - return ttSHORT(data+22+(m*6)); - } - return 0; -} - -static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) -{ - stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); - switch (coverageFormat) { - case 1: { - stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); - - // Binary search. - stbtt_int32 l=0, r=glyphCount-1, m; - int straw, needle=glyph; - while (l <= r) { - stbtt_uint8 *glyphArray = coverageTable + 4; - stbtt_uint16 glyphID; - m = (l + r) >> 1; - glyphID = ttUSHORT(glyphArray + 2 * m); - straw = glyphID; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - return m; - } - } - break; - } - - case 2: { - stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); - stbtt_uint8 *rangeArray = coverageTable + 4; - - // Binary search. - stbtt_int32 l=0, r=rangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *rangeRecord; - m = (l + r) >> 1; - rangeRecord = rangeArray + 6 * m; - strawStart = ttUSHORT(rangeRecord); - strawEnd = ttUSHORT(rangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else { - stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); - return startCoverageIndex + glyph - strawStart; - } - } - break; - } - - default: return -1; // unsupported - } - - return -1; -} - -static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) -{ - stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); - switch (classDefFormat) - { - case 1: { - stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); - stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); - stbtt_uint8 *classDef1ValueArray = classDefTable + 6; - - if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) - return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - break; - } - - case 2: { - stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); - stbtt_uint8 *classRangeRecords = classDefTable + 4; - - // Binary search. - stbtt_int32 l=0, r=classRangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *classRangeRecord; - m = (l + r) >> 1; - classRangeRecord = classRangeRecords + 6 * m; - strawStart = ttUSHORT(classRangeRecord); - strawEnd = ttUSHORT(classRangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else - return (stbtt_int32)ttUSHORT(classRangeRecord + 4); - } - break; - } - - default: - return -1; // Unsupported definition type, return an error. - } - - // "All glyphs not assigned to a class fall into class 0". (OpenType spec) - return 0; -} - -// Define to STBTT_assert(x) if you want to break on unimplemented formats. -#define STBTT_GPOS_TODO_assert(x) - -static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint16 lookupListOffset; - stbtt_uint8 *lookupList; - stbtt_uint16 lookupCount; - stbtt_uint8 *data; - stbtt_int32 i, sti; - - if (!info->gpos) return 0; - - data = info->data + info->gpos; - - if (ttUSHORT(data+0) != 1) return 0; // Major version 1 - if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 - - lookupListOffset = ttUSHORT(data+8); - lookupList = data + lookupListOffset; - lookupCount = ttUSHORT(lookupList); - - for (i=0; i= pairSetCount) return 0; - - needle=glyph2; - r=pairValueCount-1; - l=0; - - // Binary search. - while (l <= r) { - stbtt_uint16 secondGlyph; - stbtt_uint8 *pairValue; - m = (l + r) >> 1; - pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; - secondGlyph = ttUSHORT(pairValue); - straw = secondGlyph; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - stbtt_int16 xAdvance = ttSHORT(pairValue + 2); - return xAdvance; - } - } - } else - return 0; - break; - } - - case 2: { - stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); - stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); - if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? - stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); - stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); - int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); - int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); - - stbtt_uint16 class1Count = ttUSHORT(table + 12); - stbtt_uint16 class2Count = ttUSHORT(table + 14); - stbtt_uint8 *class1Records, *class2Records; - stbtt_int16 xAdvance; - - if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed - if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed - - class1Records = table + 16; - class2Records = class1Records + 2 * (glyph1class * class2Count); - xAdvance = ttSHORT(class2Records + 2 * glyph2class); - return xAdvance; - } else - return 0; - break; - } - - default: - return 0; // Unsupported position format - } - } - } - - return 0; -} - -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) -{ - int xAdvance = 0; - - if (info->gpos) - xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - else if (info->kern) - xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); - - return xAdvance; -} - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) -{ - if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs - return 0; - return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); -} - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -{ - stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -} - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -{ - if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); - if (descent) *descent = ttSHORT(info->data+info->hhea + 6); - if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -} - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) -{ - int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); - if (!tab) - return 0; - if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); - if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); - if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); - return 1; -} - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) -{ - *x0 = ttSHORT(info->data + info->head + 36); - *y0 = ttSHORT(info->data + info->head + 38); - *x1 = ttSHORT(info->data + info->head + 40); - *y1 = ttSHORT(info->data + info->head + 42); -} - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -{ - int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); - return (float) height / fheight; -} - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) -{ - int unitsPerEm = ttUSHORT(info->data + info->head + 18); - return pixels / unitsPerEm; -} - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -{ - STBTT_free(v, info->userdata); -} - -STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) -{ - int i; - stbtt_uint8 *data = info->data; - stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); - - int numEntries = ttUSHORT(svg_doc_list); - stbtt_uint8 *svg_docs = svg_doc_list + 2; - - for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) - return svg_doc; - } - return 0; -} - -STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) -{ - stbtt_uint8 *data = info->data; - stbtt_uint8 *svg_doc; - - if (info->svg == 0) - return 0; - - svg_doc = stbtt_FindSVGDoc(info, gl); - if (svg_doc != NULL) { - *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); - return ttULONG(svg_doc + 8); - } else { - return 0; - } -} - -STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) -{ - return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); -} - -////////////////////////////////////////////////////////////////////////////// -// -// antialiasing software rasterizer -// - -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning - if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { - // e.g. space character - if (ix0) *ix0 = 0; - if (iy0) *iy0 = 0; - if (ix1) *ix1 = 0; - if (iy1) *iy1 = 0; - } else { - // move to integral bboxes (treating pixels as little squares, what pixels get touched)? - if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); - if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); - if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); - if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); - } -} - -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); -} - -////////////////////////////////////////////////////////////////////////////// -// -// Rasterizer - -typedef struct stbtt__hheap_chunk -{ - struct stbtt__hheap_chunk *next; -} stbtt__hheap_chunk; - -typedef struct stbtt__hheap -{ - struct stbtt__hheap_chunk *head; - void *first_free; - int num_remaining_in_head_chunk; -} stbtt__hheap; - -static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) -{ - if (hh->first_free) { - void *p = hh->first_free; - hh->first_free = * (void **) p; - return p; - } else { - if (hh->num_remaining_in_head_chunk == 0) { - int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); - stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); - if (c == NULL) - return NULL; - c->next = hh->head; - hh->head = c; - hh->num_remaining_in_head_chunk = count; - } - --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; - } -} - -static void stbtt__hheap_free(stbtt__hheap *hh, void *p) -{ - *(void **) p = hh->first_free; - hh->first_free = p; -} - -static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) -{ - stbtt__hheap_chunk *c = hh->head; - while (c) { - stbtt__hheap_chunk *n = c->next; - STBTT_free(c, userdata); - c = n; - } -} - -typedef struct stbtt__edge { - float x0,y0, x1,y1; - int invert; -} stbtt__edge; - - -typedef struct stbtt__active_edge -{ - struct stbtt__active_edge *next; - #if STBTT_RASTERIZER_VERSION==1 - int x,dx; - float ey; - int direction; - #elif STBTT_RASTERIZER_VERSION==2 - float fx,fdx,fdy; - float direction; - float sy; - float ey; - #else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" - #endif -} stbtt__active_edge; - -#if STBTT_RASTERIZER_VERSION == 1 -#define STBTT_FIXSHIFT 10 -#define STBTT_FIX (1 << STBTT_FIXSHIFT) -#define STBTT_FIXMASK (STBTT_FIX-1) - -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - if (!z) return z; - - // round dx down to avoid overshooting - if (dxdy < 0) - z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); - else - z->dx = STBTT_ifloor(STBTT_FIX * dxdy); - - z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount - z->x -= off_x * STBTT_FIX; - - z->ey = e->y1; - z->next = 0; - z->direction = e->invert ? 1 : -1; - return z; -} -#elif STBTT_RASTERIZER_VERSION == 2 -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - //STBTT_assert(e->y0 <= start_point); - if (!z) return z; - z->fdx = dxdy; - z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; - z->fx = e->x0 + dxdy * (start_point - e->y0); - z->fx -= off_x; - z->direction = e->invert ? 1.0f : -1.0f; - z->sy = e->y0; - z->ey = e->y1; - z->next = 0; - return z; -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#if STBTT_RASTERIZER_VERSION == 1 -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -{ - // non-zero winding fill - int x0=0, w=0; - - while (e) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->direction; - } else { - int x1 = e->x; w += e->direction; - // if we went to zero, we need to draw - if (w == 0) { - int i = x0 >> STBTT_FIXSHIFT; - int j = x1 >> STBTT_FIXSHIFT; - - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = scanline[i] + (stbtt_uint8) max_weight; - } - } - } - } - - e = e->next; - } -} - -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0; - int max_weight = (255 / vsubsample); // weight per vertical scanline - int s; // vertical subsample index - unsigned char scanline_data[512], *scanline; - - if (result->w > 512) - scanline = (unsigned char *) STBTT_malloc(result->w, userdata); - else - scanline = scanline_data; - - y = off_y * vsubsample; - e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; - - while (j < result->h) { - STBTT_memset(scanline, 0, result->w); - for (s=0; s < vsubsample; ++s) { - // find center of pixel for this scanline - float scan_y = y + 0.5f; - stbtt__active_edge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for(;;) { - int changed=0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - stbtt__active_edge *t = *step; - stbtt__active_edge *q = t->next; - - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e->y0 <= scan_y) { - if (e->y1 > scan_y) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - if (z != NULL) { - // find insertion point - if (active == NULL) - active = z; - else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - } - ++e; - } - - // now process all active edges in XOR fashion - if (active) - stbtt__fill_active_edges(scanline, result->w, active, max_weight); - - ++y; - } - STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} - -#elif STBTT_RASTERIZER_VERSION == 2 - -// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 -// (i.e. it has already been clipped to those) -static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) -{ - if (y0 == y1) return; - STBTT_assert(y0 < y1); - STBTT_assert(e->sy <= e->ey); - if (y0 > e->ey) return; - if (y1 < e->sy) return; - if (y0 < e->sy) { - x0 += (x1-x0) * (e->sy - y0) / (y1-y0); - y0 = e->sy; - } - if (y1 > e->ey) { - x1 += (x1-x0) * (e->ey - y1) / (y1-y0); - y1 = e->ey; - } - - if (x0 == x) - STBTT_assert(x1 <= x+1); - else if (x0 == x+1) - STBTT_assert(x1 >= x); - else if (x0 <= x) - STBTT_assert(x1 <= x); - else if (x0 >= x+1) - STBTT_assert(x1 >= x+1); - else - STBTT_assert(x1 >= x && x1 <= x+1); - - if (x0 <= x && x1 <= x) - scanline[x] += e->direction * (y1-y0); - else if (x0 >= x+1 && x1 >= x+1) - ; - else { - STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); - scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position - } -} - -static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) -{ - STBTT_assert(top_width >= 0); - STBTT_assert(bottom_width >= 0); - return (top_width + bottom_width) / 2.0f * height; -} - -static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) -{ - return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); -} - -static float stbtt__sized_triangle_area(float height, float width) -{ - return height * width / 2; -} - -static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) -{ - float y_bottom = y_top+1; - - while (e) { - // brute force every pixel - - // compute intersection points with top & bottom - STBTT_assert(e->ey >= y_top); - - if (e->fdx == 0) { - float x0 = e->fx; - if (x0 < len) { - if (x0 >= 0) { - stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); - stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); - } else { - stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); - } - } - } else { - float x0 = e->fx; - float dx = e->fdx; - float xb = x0 + dx; - float x_top, x_bottom; - float sy0,sy1; - float dy = e->fdy; - STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); - - // compute endpoints of line segment clipped to this scanline (if the - // line segment starts on this scanline. x0 is the intersection of the - // line with y_top, but that may be off the line segment. - if (e->sy > y_top) { - x_top = x0 + dx * (e->sy - y_top); - sy0 = e->sy; - } else { - x_top = x0; - sy0 = y_top; - } - if (e->ey < y_bottom) { - x_bottom = x0 + dx * (e->ey - y_top); - sy1 = e->ey; - } else { - x_bottom = xb; - sy1 = y_bottom; - } - - if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { - // from here on, we don't have to range check x values - - if ((int) x_top == (int) x_bottom) { - float height; - // simple case, only spans one pixel - int x = (int) x_top; - height = (sy1 - sy0) * e->direction; - STBTT_assert(x >= 0 && x < len); - scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); - scanline_fill[x] += height; // everything right of this pixel is filled - } else { - int x,x1,x2; - float y_crossing, y_final, step, sign, area; - // covers 2+ pixels - if (x_top > x_bottom) { - // flip scanline vertically; signed area is the same - float t; - sy0 = y_bottom - (sy0 - y_top); - sy1 = y_bottom - (sy1 - y_top); - t = sy0, sy0 = sy1, sy1 = t; - t = x_bottom, x_bottom = x_top, x_top = t; - dx = -dx; - dy = -dy; - t = x0, x0 = xb, xb = t; - } - STBTT_assert(dy >= 0); - STBTT_assert(dx >= 0); - - x1 = (int) x_top; - x2 = (int) x_bottom; - // compute intersection with y axis at x1+1 - y_crossing = y_top + dy * (x1+1 - x0); - - // compute intersection with y axis at x2 - y_final = y_top + dy * (x2 - x0); - - // x1 x_top x2 x_bottom - // y_top +------|-----+------------+------------+--------|---+------------+ - // | | | | | | - // | | | | | | - // sy0 | Txxxxx|............|............|............|............| - // y_crossing | *xxxxx.......|............|............|............| - // | | xxxxx..|............|............|............| - // | | /- xx*xxxx........|............|............| - // | | dy < | xxxxxx..|............|............| - // y_final | | \- | xx*xxx.........|............| - // sy1 | | | | xxxxxB...|............| - // | | | | | | - // | | | | | | - // y_bottom +------------+------------+------------+------------+------------+ - // - // goal is to measure the area covered by '.' in each pixel - - // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 - // @TODO: maybe test against sy1 rather than y_bottom? - if (y_crossing > y_bottom) - y_crossing = y_bottom; - - sign = e->direction; - - // area of the rectangle covered from sy0..y_crossing - area = sign * (y_crossing-sy0); - - // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) - scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); - - // check if final y_crossing is blown up; no test case for this - if (y_final > y_bottom) { - y_final = y_bottom; - dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom - } - - // in second pixel, area covered by line segment found in first pixel - // is always a rectangle 1 wide * the height of that line segment; this - // is exactly what the variable 'area' stores. it also gets a contribution - // from the line segment within it. the THIRD pixel will get the first - // pixel's rectangle contribution, the second pixel's rectangle contribution, - // and its own contribution. the 'own contribution' is the same in every pixel except - // the leftmost and rightmost, a trapezoid that slides down in each pixel. - // the second pixel's contribution to the third pixel will be the - // rectangle 1 wide times the height change in the second pixel, which is dy. - - step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, - // which multiplied by 1-pixel-width is how much pixel area changes for each step in x - // so the area advances by 'step' every time - - for (x = x1+1; x < x2; ++x) { - scanline[x] += area + step/2; // area of trapezoid is 1*step/2 - area += step; - } - STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down - STBTT_assert(sy1 > y_final-0.01f); - - // area covered in the last pixel is the rectangle from all the pixels to the left, - // plus the trapezoid filled by the line segment in this pixel all the way to the right edge - scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); - - // the rest of the line is filled based on the total height of the line segment in this pixel - scanline_fill[x2] += sign * (sy1-sy0); - } - } else { - // if edge goes outside of box we're drawing, we require - // clipping logic. since this does not match the intended use - // of this library, we use a different, very slow brute - // force implementation - // note though that this does happen some of the time because - // x_top and x_bottom can be extrapolated at the top & bottom of - // the shape and actually lie outside the bounding box - int x; - for (x=0; x < len; ++x) { - // cases: - // - // there can be up to two intersections with the pixel. any intersection - // with left or right edges can be handled by splitting into two (or three) - // regions. intersections with top & bottom do not necessitate case-wise logic. - // - // the old way of doing this found the intersections with the left & right edges, - // then used some simple logic to produce up to three segments in sorted order - // from top-to-bottom. however, this had a problem: if an x edge was epsilon - // across the x border, then the corresponding y position might not be distinct - // from the other y segment, and it might ignored as an empty segment. to avoid - // that, we need to explicitly produce segments based on x positions. - - // rename variables to clearly-defined pairs - float y0 = y_top; - float x1 = (float) (x); - float x2 = (float) (x+1); - float x3 = xb; - float y3 = y_bottom; - - // x = e->x + e->dx * (y-y_top) - // (y-y_top) = (x - e->x) / e->dx - // y = (x - e->x) / e->dx + y_top - float y1 = (x - x0) / dx + y_top; - float y2 = (x+1 - x0) / dx + y_top; - - if (x0 < x1 && x3 > x2) { // three segments descending down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x1 && x0 > x2) { // three segments descending down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else { // one segment - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); - } - } - } - } - e = e->next; - } -} - -// directly AA rasterize edges w/o supersampling -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0, i; - float scanline_data[129], *scanline, *scanline2; - - STBTT__NOTUSED(vsubsample); - - if (result->w > 64) - scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); - else - scanline = scanline_data; - - scanline2 = scanline + result->w; - - y = off_y; - e[n].y0 = (float) (off_y + result->h) + 1; - - while (j < result->h) { - // find center of pixel for this scanline - float scan_y_top = y + 0.0f; - float scan_y_bottom = y + 1.0f; - stbtt__active_edge **step = &active; - - STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); - STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); - - // update all active edges; - // remove all active edges that terminate before the top of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y_top) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - step = &((*step)->next); // advance through list - } - } - - // insert all edges that start before the bottom of this scanline - while (e->y0 <= scan_y_bottom) { - if (e->y0 != e->y1) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - if (z != NULL) { - if (j == 0 && off_y != 0) { - if (z->ey < scan_y_top) { - // this can happen due to subpixel positioning and some kind of fp rounding error i think - z->ey = scan_y_top; - } - } - STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds - // insert at front - z->next = active; - active = z; - } - } - ++e; - } - - // now process all active edges - if (active) - stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); - - { - float sum = 0; - for (i=0; i < result->w; ++i) { - float k; - int m; - sum += scanline2[i]; - k = scanline[i] + sum; - k = (float) STBTT_fabs(k)*255 + 0.5f; - m = (int) k; - if (m > 255) m = 255; - result->pixels[j*result->stride + i] = (unsigned char) m; - } - } - // advance all the edges - step = &active; - while (*step) { - stbtt__active_edge *z = *step; - z->fx += z->fdx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - - ++y; - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) - -static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) -{ - int i,j; - for (i=1; i < n; ++i) { - stbtt__edge t = p[i], *a = &t; - j = i; - while (j > 0) { - stbtt__edge *b = &p[j-1]; - int c = STBTT__COMPARE(a,b); - if (!c) break; - p[j] = p[j-1]; - --j; - } - if (i != j) - p[j] = t; - } -} - -static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) -{ - /* threshold for transitioning to insertion sort */ - while (n > 12) { - stbtt__edge t; - int c01,c12,c,m,i,j; - - /* compute median of three */ - m = n >> 1; - c01 = STBTT__COMPARE(&p[0],&p[m]); - c12 = STBTT__COMPARE(&p[m],&p[n-1]); - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ - if (c01 != c12) { - /* otherwise, we'll need to swap something else to middle */ - int z; - c = STBTT__COMPARE(&p[0],&p[n-1]); - /* 0>mid && midn => n; 0 0 */ - /* 0n: 0>n => 0; 0 n */ - z = (c == c12) ? 0 : n-1; - t = p[z]; - p[z] = p[m]; - p[m] = t; - } - /* now p[m] is the median-of-three */ - /* swap it to the beginning so it won't move around */ - t = p[0]; - p[0] = p[m]; - p[m] = t; - - /* partition loop */ - i=1; - j=n-1; - for(;;) { - /* handling of equality is crucial here */ - /* for sentinels & efficiency with duplicates */ - for (;;++i) { - if (!STBTT__COMPARE(&p[i], &p[0])) break; - } - for (;;--j) { - if (!STBTT__COMPARE(&p[0], &p[j])) break; - } - /* make sure we haven't crossed */ - if (i >= j) break; - t = p[i]; - p[i] = p[j]; - p[j] = t; - - ++i; - --j; - } - /* recurse on smaller side, iterate on larger */ - if (j < (n-i)) { - stbtt__sort_edges_quicksort(p,j); - p = p+i; - n = n-i; - } else { - stbtt__sort_edges_quicksort(p+i, n-i); - n = j; - } - } -} - -static void stbtt__sort_edges(stbtt__edge *p, int n) -{ - stbtt__sort_edges_quicksort(p, n); - stbtt__sort_edges_ins_sort(p, n); -} - -typedef struct -{ - float x,y; -} stbtt__point; - -static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) -{ - float y_scale_inv = invert ? -scale_y : scale_y; - stbtt__edge *e; - int n,i,j,k,m; -#if STBTT_RASTERIZER_VERSION == 1 - int vsubsample = result->h < 8 ? 15 : 5; -#elif STBTT_RASTERIZER_VERSION == 2 - int vsubsample = 1; -#else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - // vsubsample should divide 255 evenly; otherwise we won't reach full opacity - - // now we have to blow out the windings into explicit edge lists - n = 0; - for (i=0; i < windings; ++i) - n += wcount[i]; - - e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel - if (e == 0) return; - n = 0; - - m=0; - for (i=0; i < windings; ++i) { - stbtt__point *p = pts + m; - m += wcount[i]; - j = wcount[i]-1; - for (k=0; k < wcount[i]; j=k++) { - int a=k,b=j; - // skip the edge if horizontal - if (p[j].y == p[k].y) - continue; - // add edge from j to k to the list - e[n].invert = 0; - if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { - e[n].invert = 1; - a=j,b=k; - } - e[n].x0 = p[a].x * scale_x + shift_x; - e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; - e[n].x1 = p[b].x * scale_x + shift_x; - e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; - ++n; - } - } - - // now sort the edges by their highest point (should snap to integer, and then by x) - //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); - stbtt__sort_edges(e, n); - - // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule - stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); - - STBTT_free(e, userdata); -} - -static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -{ - if (!points) return; // during first pass, it's unallocated - points[n].x = x; - points[n].y = y; -} - -// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching -static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -{ - // midpoint - float mx = (x0 + 2*x1 + x2)/4; - float my = (y0 + 2*y1 + y2)/4; - // versus directly drawn line - float dx = (x0+x2)/2 - mx; - float dy = (y0+y2)/2 - my; - if (n > 16) // 65536 segments on one curve better be enough! - return 1; - if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA - stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x2,y2); - *num_points = *num_points+1; - } - return 1; -} - -static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) -{ - // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough - float dx0 = x1-x0; - float dy0 = y1-y0; - float dx1 = x2-x1; - float dy1 = y2-y1; - float dx2 = x3-x2; - float dy2 = y3-y2; - float dx = x3-x0; - float dy = y3-y0; - float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); - float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); - float flatness_squared = longlen*longlen-shortlen*shortlen; - - if (n > 16) // 65536 segments on one curve better be enough! - return; - - if (flatness_squared > objspace_flatness_squared) { - float x01 = (x0+x1)/2; - float y01 = (y0+y1)/2; - float x12 = (x1+x2)/2; - float y12 = (y1+y2)/2; - float x23 = (x2+x3)/2; - float y23 = (y2+y3)/2; - - float xa = (x01+x12)/2; - float ya = (y01+y12)/2; - float xb = (x12+x23)/2; - float yb = (y12+y23)/2; - - float mx = (xa+xb)/2; - float my = (ya+yb)/2; - - stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x3,y3); - *num_points = *num_points+1; - } -} - -// returns number of contours -static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -{ - stbtt__point *points=0; - int num_points=0; - - float objspace_flatness_squared = objspace_flatness * objspace_flatness; - int i,n=0,start=0, pass; - - // count how many "moves" there are to get the contour count - for (i=0; i < num_verts; ++i) - if (vertices[i].type == STBTT_vmove) - ++n; - - *num_contours = n; - if (n == 0) return 0; - - *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); - - if (*contour_lengths == 0) { - *num_contours = 0; - return 0; - } - - // make two passes through the points so we don't need to realloc - for (pass=0; pass < 2; ++pass) { - float x=0,y=0; - if (pass == 1) { - points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); - if (points == NULL) goto error; - } - num_points = 0; - n= -1; - for (i=0; i < num_verts; ++i) { - switch (vertices[i].type) { - case STBTT_vmove: - // start the next contour - if (n >= 0) - (*contour_lengths)[n] = num_points - start; - ++n; - start = num_points; - - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x,y); - break; - case STBTT_vline: - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x, y); - break; - case STBTT_vcurve: - stbtt__tesselate_curve(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - case STBTT_vcubic: - stbtt__tesselate_cubic(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].cx1, vertices[i].cy1, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - } - } - (*contour_lengths)[n] = num_points - start; - } - - return points; -error: - STBTT_free(points, userdata); - STBTT_free(*contour_lengths, userdata); - *contour_lengths = 0; - *num_contours = 0; - return NULL; -} - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) -{ - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count = 0; - int *winding_lengths = NULL; - stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); - if (windings) { - stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); - STBTT_free(winding_lengths, userdata); - STBTT_free(windings, userdata); - } -} - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - int ix0,iy0,ix1,iy1; - stbtt__bitmap gbm; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) { - STBTT_free(vertices, info->userdata); - return NULL; - } - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); - - // now we get the size - gbm.w = (ix1 - ix0); - gbm.h = (iy1 - iy0); - gbm.pixels = NULL; // in case we error - - if (width ) *width = gbm.w; - if (height) *height = gbm.h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - if (gbm.w && gbm.h) { - gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); - if (gbm.pixels) { - gbm.stride = gbm.w; - - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); - } - } - STBTT_free(vertices, info->userdata); - return gbm.pixels; -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) -{ - int ix0,iy0; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); - gbm.pixels = output; - gbm.w = out_w; - gbm.h = out_h; - gbm.stride = out_stride; - - if (gbm.w && gbm.h) - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); - - STBTT_free(vertices, info->userdata); -} - -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -{ - stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); -} - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-CRAPPY packing to keep source code small - -static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata) -{ - float scale; - int x,y,bottom_y, i; - stbtt_fontinfo f; - f.userdata = NULL; - if (!stbtt_InitFont(&f, data, offset)) - return -1; - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - x=y=1; - bottom_y = 1; - - scale = stbtt_ScaleForPixelHeight(&f, pixel_height); - - for (i=0; i < num_chars; ++i) { - int advance, lsb, x0,y0,x1,y1,gw,gh; - int g = stbtt_FindGlyphIndex(&f, first_char + i); - stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); - stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); - gw = x1-x0; - gh = y1-y0; - if (x + gw + 1 >= pw) - y = bottom_y, x = 1; // advance to next row - if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row - return -i; - STBTT_assert(x+gw < pw); - STBTT_assert(y+gh < ph); - stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); - chardata[i].x0 = (stbtt_int16) x; - chardata[i].y0 = (stbtt_int16) y; - chardata[i].x1 = (stbtt_int16) (x + gw); - chardata[i].y1 = (stbtt_int16) (y + gh); - chardata[i].xadvance = scale * advance; - chardata[i].xoff = (float) x0; - chardata[i].yoff = (float) y0; - x = x + gw + 1; - if (y+gh+1 > bottom_y) - bottom_y = y+gh+1; - } - return bottom_y; -} - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -{ - float d3d_bias = opengl_fillrule ? 0 : -0.5f; - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); - - q->x0 = round_x + d3d_bias; - q->y0 = round_y + d3d_bias; - q->x1 = round_x + b->x1 - b->x0 + d3d_bias; - q->y1 = round_y + b->y1 - b->y0 + d3d_bias; - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// rectangle packing replacement routines if you don't have stb_rect_pack.h -// - -#ifndef STB_RECT_PACK_VERSION - -typedef int stbrp_coord; - -//////////////////////////////////////////////////////////////////////////////////// -// // -// // -// COMPILER WARNING ?!?!? // -// // -// // -// if you get a compile warning due to these symbols being defined more than // -// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // -// // -//////////////////////////////////////////////////////////////////////////////////// - -typedef struct -{ - int width,height; - int x,y,bottom_y; -} stbrp_context; - -typedef struct -{ - unsigned char x; -} stbrp_node; - -struct stbrp_rect -{ - stbrp_coord x,y; - int id,w,h,was_packed; -}; - -static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) -{ - con->width = pw; - con->height = ph; - con->x = 0; - con->y = 0; - con->bottom_y = 0; - STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); -} - -static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) -{ - int i; - for (i=0; i < num_rects; ++i) { - if (con->x + rects[i].w > con->width) { - con->x = 0; - con->y = con->bottom_y; - } - if (con->y + rects[i].h > con->height) - break; - rects[i].x = con->x; - rects[i].y = con->y; - rects[i].was_packed = 1; - con->x += rects[i].w; - if (con->y + rects[i].h > con->bottom_y) - con->bottom_y = con->y + rects[i].h; - } - for ( ; i < num_rects; ++i) - rects[i].was_packed = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If -// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) -{ - stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); - int num_nodes = pw - padding; - stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); - - if (context == NULL || nodes == NULL) { - if (context != NULL) STBTT_free(context, alloc_context); - if (nodes != NULL) STBTT_free(nodes , alloc_context); - return 0; - } - - spc->user_allocator_context = alloc_context; - spc->width = pw; - spc->height = ph; - spc->pixels = pixels; - spc->pack_info = context; - spc->nodes = nodes; - spc->padding = padding; - spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; - spc->h_oversample = 1; - spc->v_oversample = 1; - spc->skip_missing = 0; - - stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); - - if (pixels) - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - - return 1; -} - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) -{ - STBTT_free(spc->nodes , spc->user_allocator_context); - STBTT_free(spc->pack_info, spc->user_allocator_context); -} - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) -{ - STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); - STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); - if (h_oversample <= STBTT_MAX_OVERSAMPLE) - spc->h_oversample = h_oversample; - if (v_oversample <= STBTT_MAX_OVERSAMPLE) - spc->v_oversample = v_oversample; -} - -STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) -{ - spc->skip_missing = skip; -} - -#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) - -static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_w = w - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < h; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < w; ++i) { - STBTT_assert(pixels[i] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i] = (unsigned char) (total / kernel_width); - } - - pixels += stride_in_bytes; - } -} - -static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_h = h - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < w; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < h; ++i) { - STBTT_assert(pixels[i*stride_in_bytes] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - - pixels += 1; - } -} - -static float stbtt__oversample_shift(int oversample) -{ - if (!oversample) - return 0.0f; - - // The prefilter is a box filter of width "oversample", - // which shifts phase by (oversample - 1)/2 pixels in - // oversampled space. We want to shift in the opposite - // direction to counter this. - return (float)-(oversample - 1) / (2.0f * (float)oversample); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k; - int missing_glyph_added = 0; - - k=0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - ranges[i].h_oversample = (unsigned char) spc->h_oversample; - ranges[i].v_oversample = (unsigned char) spc->v_oversample; - for (j=0; j < ranges[i].num_chars; ++j) { - int x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { - rects[k].w = rects[k].h = 0; - } else { - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); - if (glyph == 0) - missing_glyph_added = 1; - } - ++k; - } - } - - return k; -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, - output, - out_w - (prefilter_x - 1), - out_h - (prefilter_y - 1), - out_stride, - scale_x, - scale_y, - shift_x, - shift_y, - glyph); - - if (prefilter_x > 1) - stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); - - if (prefilter_y > 1) - stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); - - *sub_x = stbtt__oversample_shift(prefilter_x); - *sub_y = stbtt__oversample_shift(prefilter_y); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k, missing_glyph = -1, return_value = 1; - - // save current values - int old_h_over = spc->h_oversample; - int old_v_over = spc->v_oversample; - - k = 0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - float recip_h,recip_v,sub_x,sub_y; - spc->h_oversample = ranges[i].h_oversample; - spc->v_oversample = ranges[i].v_oversample; - recip_h = 1.0f / spc->h_oversample; - recip_v = 1.0f / spc->v_oversample; - sub_x = stbtt__oversample_shift(spc->h_oversample); - sub_y = stbtt__oversample_shift(spc->v_oversample); - for (j=0; j < ranges[i].num_chars; ++j) { - stbrp_rect *r = &rects[k]; - if (r->was_packed && r->w != 0 && r->h != 0) { - stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; - int advance, lsb, x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbrp_coord pad = (stbrp_coord) spc->padding; - - // pad on left and top - r->x += pad; - r->y += pad; - r->w -= pad; - r->h -= pad; - stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); - stbtt_GetGlyphBitmapBox(info, glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - &x0,&y0,&x1,&y1); - stbtt_MakeGlyphBitmapSubpixel(info, - spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w - spc->h_oversample+1, - r->h - spc->v_oversample+1, - spc->stride_in_bytes, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - glyph); - - if (spc->h_oversample > 1) - stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->h_oversample); - - if (spc->v_oversample > 1) - stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->v_oversample); - - bc->x0 = (stbtt_int16) r->x; - bc->y0 = (stbtt_int16) r->y; - bc->x1 = (stbtt_int16) (r->x + r->w); - bc->y1 = (stbtt_int16) (r->y + r->h); - bc->xadvance = scale * advance; - bc->xoff = (float) x0 * recip_h + sub_x; - bc->yoff = (float) y0 * recip_v + sub_y; - bc->xoff2 = (x0 + r->w) * recip_h + sub_x; - bc->yoff2 = (y0 + r->h) * recip_v + sub_y; - - if (glyph == 0) - missing_glyph = j; - } else if (spc->skip_missing) { - return_value = 0; - } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { - ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; - } else { - return_value = 0; // if any fail, report failure - } - - ++k; - } - } - - // restore original values - spc->h_oversample = old_h_over; - spc->v_oversample = old_v_over; - - return return_value; -} - -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) -{ - stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); -} - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) -{ - stbtt_fontinfo info; - int i,j,n, return_value = 1; - //stbrp_context *context = (stbrp_context *) spc->pack_info; - stbrp_rect *rects; - - // flag all characters as NOT packed - for (i=0; i < num_ranges; ++i) - for (j=0; j < ranges[i].num_chars; ++j) - ranges[i].chardata_for_range[j].x0 = - ranges[i].chardata_for_range[j].y0 = - ranges[i].chardata_for_range[j].x1 = - ranges[i].chardata_for_range[j].y1 = 0; - - n = 0; - for (i=0; i < num_ranges; ++i) - n += ranges[i].num_chars; - - rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); - if (rects == NULL) - return 0; - - info.userdata = spc->user_allocator_context; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); - - n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); - - stbtt_PackFontRangesPackRects(spc, rects, n); - - return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); - - STBTT_free(rects, spc->user_allocator_context); - return return_value; -} - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) -{ - stbtt_pack_range range; - range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; - range.array_of_unicode_codepoints = NULL; - range.num_chars = num_chars_in_range; - range.chardata_for_range = chardata_for_range; - range.font_size = font_size; - return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); -} - -STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) -{ - int i_ascent, i_descent, i_lineGap; - float scale; - stbtt_fontinfo info; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); - scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); - stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); - *ascent = (float) i_ascent * scale; - *descent = (float) i_descent * scale; - *lineGap = (float) i_lineGap * scale; -} - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) -{ - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_packedchar *b = chardata + char_index; - - if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); - q->x0 = x; - q->y0 = y; - q->x1 = x + b->xoff2 - b->xoff; - q->y1 = y + b->yoff2 - b->yoff; - } else { - q->x0 = *xpos + b->xoff; - q->y0 = *ypos + b->yoff; - q->x1 = *xpos + b->xoff2; - q->y1 = *ypos + b->yoff2; - } - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// sdf computation -// - -#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) -#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) - -static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) -{ - float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; - float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; - float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; - float roperp = orig[1]*ray[0] - orig[0]*ray[1]; - - float a = q0perp - 2*q1perp + q2perp; - float b = q1perp - q0perp; - float c = q0perp - roperp; - - float s0 = 0., s1 = 0.; - int num_s = 0; - - if (a != 0.0) { - float discr = b*b - a*c; - if (discr > 0.0) { - float rcpna = -1 / a; - float d = (float) STBTT_sqrt(discr); - s0 = (b+d) * rcpna; - s1 = (b-d) * rcpna; - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { - if (num_s == 0) s0 = s1; - ++num_s; - } - } - } else { - // 2*b*s + c = 0 - // s = -c / (2*b) - s0 = c / (-2 * b); - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - } - - if (num_s == 0) - return 0; - else { - float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); - float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; - - float q0d = q0[0]*rayn_x + q0[1]*rayn_y; - float q1d = q1[0]*rayn_x + q1[1]*rayn_y; - float q2d = q2[0]*rayn_x + q2[1]*rayn_y; - float rod = orig[0]*rayn_x + orig[1]*rayn_y; - - float q10d = q1d - q0d; - float q20d = q2d - q0d; - float q0rd = q0d - rod; - - hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; - hits[0][1] = a*s0+b; - - if (num_s > 1) { - hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; - hits[1][1] = a*s1+b; - return 2; - } else { - return 1; - } - } -} - -static int equal(float *a, float *b) -{ - return (a[0] == b[0] && a[1] == b[1]); -} - -static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) -{ - int i; - float orig[2], ray[2] = { 1, 0 }; - float y_frac; - int winding = 0; - - // make sure y never passes through a vertex of the shape - y_frac = (float) STBTT_fmod(y, 1.0f); - if (y_frac < 0.01f) - y += 0.01f; - else if (y_frac > 0.99f) - y -= 0.01f; - - orig[0] = x; - orig[1] = y; - - // test a ray from (-infinity,y) to (x,y) - for (i=0; i < nverts; ++i) { - if (verts[i].type == STBTT_vline) { - int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; - int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } - if (verts[i].type == STBTT_vcurve) { - int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; - int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; - int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; - int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); - int by = STBTT_max(y0,STBTT_max(y1,y2)); - if (y > ay && y < by && x > ax) { - float q0[2],q1[2],q2[2]; - float hits[2][2]; - q0[0] = (float)x0; - q0[1] = (float)y0; - q1[0] = (float)x1; - q1[1] = (float)y1; - q2[0] = (float)x2; - q2[1] = (float)y2; - if (equal(q0,q1) || equal(q1,q2)) { - x0 = (int)verts[i-1].x; - y0 = (int)verts[i-1].y; - x1 = (int)verts[i ].x; - y1 = (int)verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } else { - int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); - if (num_hits >= 1) - if (hits[0][0] < 0) - winding += (hits[0][1] < 0 ? -1 : 1); - if (num_hits >= 2) - if (hits[1][0] < 0) - winding += (hits[1][1] < 0 ? -1 : 1); - } - } - } - } - return winding; -} - -static float stbtt__cuberoot( float x ) -{ - if (x<0) - return -(float) STBTT_pow(-x,1.0f/3.0f); - else - return (float) STBTT_pow( x,1.0f/3.0f); -} - -// x^3 + a*x^2 + b*x + c = 0 -static int stbtt__solve_cubic(float a, float b, float c, float* r) -{ - float s = -a / 3; - float p = b - a*a / 3; - float q = a * (2*a*a - 9*b) / 27 + c; - float p3 = p*p*p; - float d = q*q + 4*p3 / 27; - if (d >= 0) { - float z = (float) STBTT_sqrt(d); - float u = (-q + z) / 2; - float v = (-q - z) / 2; - u = stbtt__cuberoot(u); - v = stbtt__cuberoot(v); - r[0] = s + u + v; - return 1; - } else { - float u = (float) STBTT_sqrt(-p/3); - float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative - float m = (float) STBTT_cos(v); - float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; - r[0] = s + u * 2 * m; - r[1] = s - u * (m + n); - r[2] = s - u * (m - n); - - //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? - //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); - //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); - return 3; - } -} - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - float scale_x = scale, scale_y = scale; - int ix0,iy0,ix1,iy1; - int w,h; - unsigned char *data; - - if (scale == 0) return NULL; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); - - // if empty, return NULL - if (ix0 == ix1 || iy0 == iy1) - return NULL; - - ix0 -= padding; - iy0 -= padding; - ix1 += padding; - iy1 += padding; - - w = (ix1 - ix0); - h = (iy1 - iy0); - - if (width ) *width = w; - if (height) *height = h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - // invert for y-downwards bitmaps - scale_y = -scale_y; - - { - int x,y,i,j; - float *precompute; - stbtt_vertex *verts; - int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); - data = (unsigned char *) STBTT_malloc(w * h, info->userdata); - precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); - - for (i=0,j=num_verts-1; i < num_verts; j=i++) { - if (verts[i].type == STBTT_vline) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; - float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; - float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; - float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); - else - precompute[i] = 0.0f; - } else - precompute[i] = 0.0f; - } - - for (y=iy0; y < iy1; ++y) { - for (x=ix0; x < ix1; ++x) { - float val; - float min_dist = 999999.0f; - float sx = (float) x + 0.5f; - float sy = (float) y + 0.5f; - float x_gspace = (sx / scale_x); - float y_gspace = (sy / scale_y); - - int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path - - for (i=0; i < num_verts; ++i) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - - if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { - float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; - - float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); - if (dist2 < min_dist*min_dist) - min_dist = (float) STBTT_sqrt(dist2); - - // coarse culling against bbox - //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && - // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) - dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; - STBTT_assert(i != 0); - if (dist < min_dist) { - // check position along line - // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) - // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) - float dx = x1-x0, dy = y1-y0; - float px = x0-sx, py = y0-sy; - // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy - // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve - float t = -(px*dx + py*dy) / (dx*dx + dy*dy); - if (t >= 0.0f && t <= 1.0f) - min_dist = dist; - } - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; - float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; - float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); - float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); - float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); - float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); - // coarse culling against bbox to avoid computing cubic unnecessarily - if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { - int num=0; - float ax = x1-x0, ay = y1-y0; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float mx = x0 - sx, my = y0 - sy; - float res[3] = {0.f,0.f,0.f}; - float px,py,t,it,dist2; - float a_inv = precompute[i]; - if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula - float a = 3*(ax*bx + ay*by); - float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); - float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { - res[num++] = -c/b; - } - } else { - float discriminant = b*b - 4*a*c; - if (discriminant < 0) - num = 0; - else { - float root = (float) STBTT_sqrt(discriminant); - res[0] = (-b - root)/(2*a); - res[1] = (-b + root)/(2*a); - num = 2; // don't bother distinguishing 1-solution case, as code below will still work - } - } - } else { - float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point - float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; - float d = (mx*ax+my*ay) * a_inv; - num = stbtt__solve_cubic(b, c, d, res); - } - dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); - if (dist2 < min_dist*min_dist) - min_dist = (float) STBTT_sqrt(dist2); - - if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { - t = res[0], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { - t = res[1], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { - t = res[2], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - } - } - } - if (winding == 0) - min_dist = -min_dist; // if outside the shape, value is negative - val = onedge_value + pixel_dist_scale * min_dist; - if (val < 0) - val = 0; - else if (val > 255) - val = 255; - data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; - } - } - STBTT_free(precompute, info->userdata); - STBTT_free(verts, info->userdata); - } - return data; -} - -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -////////////////////////////////////////////////////////////////////////////// -// -// font name matching -- recommended not to use this -// - -// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) -{ - stbtt_int32 i=0; - - // convert utf16 to utf8 and compare the results while converting - while (len2) { - stbtt_uint16 ch = s2[0]*256 + s2[1]; - if (ch < 0x80) { - if (i >= len1) return -1; - if (s1[i++] != ch) return -1; - } else if (ch < 0x800) { - if (i+1 >= len1) return -1; - if (s1[i++] != 0xc0 + (ch >> 6)) return -1; - if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; - } else if (ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint32 c; - stbtt_uint16 ch2 = s2[2]*256 + s2[3]; - if (i+3 >= len1) return -1; - c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - if (s1[i++] != 0xf0 + (c >> 18)) return -1; - if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; - s2 += 2; // plus another 2 below - len2 -= 2; - } else if (ch >= 0xdc00 && ch < 0xe000) { - return -1; - } else { - if (i+2 >= len1) return -1; - if (s1[i++] != 0xe0 + (ch >> 12)) return -1; - if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; - } - s2 += 2; - len2 -= 2; - } - return i; -} - -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) -{ - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); -} - -// returns results in whatever encoding you request... but note that 2-byte encodings -// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -{ - stbtt_int32 i,count,stringOffset; - stbtt_uint8 *fc = font->data; - stbtt_uint32 offset = font->fontstart; - stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return NULL; - - count = ttUSHORT(fc+nm+2); - stringOffset = nm + ttUSHORT(fc+nm+4); - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) - && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { - *length = ttUSHORT(fc+loc+8); - return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); - } - } - return NULL; -} - -static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -{ - stbtt_int32 i; - stbtt_int32 count = ttUSHORT(fc+nm+2); - stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); - - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_int32 id = ttUSHORT(fc+loc+6); - if (id == target_id) { - // find the encoding - stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); - - // is this a Unicode encoding? - if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - stbtt_int32 slen = ttUSHORT(fc+loc+8); - stbtt_int32 off = ttUSHORT(fc+loc+10); - - // check if there's a prefix match - stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); - if (matchlen >= 0) { - // check for target_id+1 immediately following, with same encoding & language - if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { - slen = ttUSHORT(fc+loc+12+8); - off = ttUSHORT(fc+loc+12+10); - if (slen == 0) { - if (matchlen == nlen) - return 1; - } else if (matchlen < nlen && name[matchlen] == ' ') { - ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) - return 1; - } - } else { - // if nothing immediately following - if (matchlen == nlen) - return 1; - } - } - } - - // @TODO handle other encodings - } - } - return 0; -} - -static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -{ - stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); - stbtt_uint32 nm,hd; - if (!stbtt__isfont(fc+offset)) return 0; - - // check italics/bold/underline flags in macStyle... - if (flags) { - hd = stbtt__find_table(fc, offset, "head"); - if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; - } - - nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return 0; - - if (flags) { - // if we checked the macStyle flags, then just check the family and ignore the subfamily - if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } else { - if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } - - return 0; -} - -static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) -{ - stbtt_int32 i; - for (i=0;;++i) { - stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); - if (off < 0) return off; - if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) - return off; - } -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, - float pixel_height, unsigned char *pixels, int pw, int ph, - int first_char, int num_chars, stbtt_bakedchar *chardata) -{ - return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); -} - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) -{ - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); -} - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) -{ - return stbtt_GetNumberOfFonts_internal((unsigned char *) data); -} - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) -{ - return stbtt_InitFont_internal(info, (unsigned char *) data, offset); -} - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) -{ - return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); -} - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -{ - return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif // STB_TRUETYPE_IMPLEMENTATION - - -// FULL VERSION HISTORY -// -// 1.25 (2021-07-11) many fixes -// 1.24 (2020-02-05) fix warning -// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) -// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined -// 1.21 (2019-02-25) fix warning -// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() -// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) allow user-defined fabs() replacement -// fix memory leak if fontsize=0.0 -// fix warning from duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// allow PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes -// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ -// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match -// non-oversampled; STBTT_POINT_SIZE for packed case only -// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling -// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) -// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID -// 0.8b (2014-07-07) fix a warning -// 0.8 (2014-05-25) fix a few more warnings -// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back -// 0.6c (2012-07-24) improve documentation -// 0.6b (2012-07-20) fix a few more warnings -// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, -// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty -// 0.5 (2011-12-09) bugfixes: -// subpixel glyph renderer computed wrong bounding box -// first vertex of shape can be off-curve (FreeSans) -// 0.4b (2011-12-03) fixed an error in the font baking example -// 0.4 (2011-12-01) kerning, subpixel rendering (tor) -// bugfixes for: -// codepoint-to-glyph conversion using table fmt=12 -// codepoint-to-glyph conversion using table fmt=4 -// stbtt_GetBakedQuad with non-square texture (Zer) -// updated Hello World! sample to use kerning and subpixel -// fixed some warnings -// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) -// userdata, malloc-from-userdata, non-zero fill (stb) -// 0.2 (2009-03-11) Fix unsigned/signed char warnings -// 0.1 (2009-03-09) First public release -// - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -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 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. ------------------------------------------------------------------------------- -*/ diff --git a/vendor/lunasvg/plutovg/source/plutovg-surface.c b/vendor/lunasvg/plutovg/source/plutovg-surface.c deleted file mode 100644 index f12cd38..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-surface.c +++ /dev/null @@ -1,295 +0,0 @@ -#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; - } - } - } -} diff --git a/vendor/lunasvg/plutovg/source/plutovg-utils.h b/vendor/lunasvg/plutovg/source/plutovg-utils.h deleted file mode 100644 index 68c86bd..0000000 --- a/vendor/lunasvg/plutovg/source/plutovg-utils.h +++ /dev/null @@ -1,211 +0,0 @@ -#ifndef PLUTOVG_UTILS_H -#define PLUTOVG_UTILS_H - -#include -#include -#include -#include -#include -#include -#include - -#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 diff --git a/vendor/lunasvg/source/graphics.cpp b/vendor/lunasvg/source/graphics.cpp deleted file mode 100644 index 248c087..0000000 --- a/vendor/lunasvg/source/graphics.cpp +++ /dev/null @@ -1,693 +0,0 @@ -#include "graphics.h" -#include "lunasvg.h" - -#include -#include - -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& 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::create(const Bitmap& bitmap) -{ - return std::shared_ptr(new Canvas(bitmap)); -} - -std::shared_ptr 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(new Canvas(0, 0, 1, 1)); - auto l = static_cast(std::floor(x)); - auto t = static_cast(std::floor(y)); - auto r = static_cast(std::ceil(x + width)); - auto b = static_cast(std::ceil(y + height)); - return std::shared_ptr(new Canvas(l, t, r - l, b - t)); -} - -std::shared_ptr 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(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(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(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(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(strokeData.lineCap())); - plutovg_canvas_set_line_join(m_canvas, static_cast(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(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(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(canvas.x()), static_cast(canvas.y()) }; - plutovg_canvas_set_matrix(m_canvas, &m_translation); - plutovg_canvas_set_operator(m_canvas, static_cast(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(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(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(x), -static_cast(y)}) - , m_x(x), m_y(y) -{ -} - -} // namespace lunasvg diff --git a/vendor/lunasvg/source/graphics.h b/vendor/lunasvg/source/graphics.h deleted file mode 100644 index 7e6bc12..0000000 --- a/vendor/lunasvg/source/graphics.h +++ /dev/null @@ -1,562 +0,0 @@ -#ifndef LUNASVG_GRAPHICS_H -#define LUNASVG_GRAPHICS_H - -#include - -#include -#include -#include -#include -#include -#include -#include - -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(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& 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; - -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; - -class Bitmap; - -class Canvas { -public: - static std::shared_ptr create(const Bitmap& bitmap); - static std::shared_ptr create(float x, float y, float width, float height); - static std::shared_ptr 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 diff --git a/vendor/lunasvg/source/lunasvg.cpp b/vendor/lunasvg/source/lunasvg.cpp deleted file mode 100644 index 0a4399f..0000000 --- a/vendor/lunasvg/source/lunasvg.cpp +++ /dev/null @@ -1,536 +0,0 @@ -#include "lunasvg.h" -#include "svgelement.h" -#include "svgrenderstate.h" - -#include -#include -#include - -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(m_node); - return TextNode(); -} - -Element Node::toElement() const -{ - if(m_node && m_node->isElement()) - return static_cast(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(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(std::ceil(elementBounds.w)); - height = static_cast(std::ceil(elementBounds.h)); - } else if(width > 0 && height <= 0) { - height = static_cast(std::ceil(width * elementBounds.h / elementBounds.w)); - } else if(height > 0 && width <= 0) { - width = static_cast(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(m_node); - if(element && layoutIfNeeded) - element->rootElement()->layoutIfNeeded(); - return element; -} - -std::unique_ptr 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::loadFromData(const std::string& string) -{ - return loadFromData(string.data(), string.size()); -} - -std::unique_ptr Document::loadFromData(const char* data) -{ - return loadFromData(data, std::strlen(data)); -} - -std::unique_ptr Document::loadFromData(const char* data, size_t length) -{ - std::unique_ptr 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(std::ceil(intrinsicWidth)); - height = static_cast(std::ceil(intrinsicHeight)); - } else if(width > 0 && height <= 0) { - height = static_cast(std::ceil(width * intrinsicHeight / intrinsicWidth)); - } else if(height > 0 && width <= 0) { - width = static_cast(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 diff --git a/vendor/lunasvg/source/svgelement.cpp b/vendor/lunasvg/source/svgelement.cpp deleted file mode 100644 index 7d62cc6..0000000 --- a/vendor/lunasvg/source/svgelement.cpp +++ /dev/null @@ -1,1225 +0,0 @@ -#include "svgelement.h" -#include "svgpaintelement.h" -#include "svggeometryelement.h" -#include "svgtextelement.h" -#include "svgproperty.h" -#include "svglayoutstate.h" -#include "svgrenderstate.h" - -#include - -namespace lunasvg { - -ElementID elementid(std::string_view name) -{ - static const struct { - std::string_view name; - ElementID value; - } table[] = { - {"a", ElementID::G}, - {"circle", ElementID::Circle}, - {"clipPath", ElementID::ClipPath}, - {"defs", ElementID::Defs}, - {"ellipse", ElementID::Ellipse}, - {"g", ElementID::G}, - {"image", ElementID::Image}, - {"line", ElementID::Line}, - {"linearGradient", ElementID::LinearGradient}, - {"marker", ElementID::Marker}, - {"mask", ElementID::Mask}, - {"path", ElementID::Path}, - {"pattern", ElementID::Pattern}, - {"polygon", ElementID::Polygon}, - {"polyline", ElementID::Polyline}, - {"radialGradient", ElementID::RadialGradient}, - {"rect", ElementID::Rect}, - {"stop", ElementID::Stop}, - {"style", ElementID::Style}, - {"svg", ElementID::Svg}, - {"symbol", ElementID::Symbol}, - {"text", ElementID::Text}, - {"tspan", ElementID::Tspan}, - {"use", ElementID::Use} - }; - - 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 ElementID::Unknown; - return it->value; -} - -SVGTextNode::SVGTextNode(Document* document) - : SVGNode(document) -{ -} - -void SVGTextNode::setData(const std::string& data) -{ - rootElement()->setNeedsLayout(); - m_data.assign(data); -} - -std::unique_ptr SVGTextNode::clone(bool deep) const -{ - auto node = std::make_unique(document()); - node->setData(m_data); - return node; -} - -const std::string emptyString; - -std::unique_ptr SVGElement::create(Document* document, ElementID id) -{ - switch(id) { - case ElementID::Svg: - return std::make_unique(document); - case ElementID::Path: - return std::make_unique(document); - case ElementID::G: - return std::make_unique(document); - case ElementID::Rect: - return std::make_unique(document); - case ElementID::Circle: - return std::make_unique(document); - case ElementID::Ellipse: - return std::make_unique(document); - case ElementID::Line: - return std::make_unique(document); - case ElementID::Defs: - return std::make_unique(document); - case ElementID::Polygon: - case ElementID::Polyline: - return std::make_unique(document, id); - case ElementID::Stop: - return std::make_unique(document); - case ElementID::LinearGradient: - return std::make_unique(document); - case ElementID::RadialGradient: - return std::make_unique(document); - case ElementID::Symbol: - return std::make_unique(document); - case ElementID::Use: - return std::make_unique(document); - case ElementID::Pattern: - return std::make_unique(document); - case ElementID::Mask: - return std::make_unique(document); - case ElementID::ClipPath: - return std::make_unique(document); - case ElementID::Marker: - return std::make_unique(document); - case ElementID::Image: - return std::make_unique(document); - case ElementID::Style: - return std::make_unique(document); - case ElementID::Text: - return std::make_unique(document); - case ElementID::Tspan: - return std::make_unique(document); - default: - assert(false); - } - - return nullptr; -} - -SVGElement::SVGElement(Document* document, ElementID id) - : SVGNode(document) - , m_id(id) -{ -} - -bool SVGElement::hasAttribute(std::string_view name) const -{ - auto id = propertyid(name); - if(id == PropertyID::Unknown) - return false; - return hasAttribute(id); -} - -const std::string& SVGElement::getAttribute(std::string_view name) const -{ - auto id = propertyid(name); - if(id == PropertyID::Unknown) - return emptyString; - return getAttribute(id); -} - -bool SVGElement::setAttribute(std::string_view name, const std::string& value) -{ - auto id = propertyid(name); - if(id == PropertyID::Unknown) - return false; - return setAttribute(0x1000, id, value); -} - -const Attribute* SVGElement::findAttribute(PropertyID id) const -{ - for(const auto& attribute : m_attributes) { - if(id == attribute.id()) { - return &attribute; - } - } - - return nullptr; -} - -bool SVGElement::hasAttribute(PropertyID id) const -{ - for(const auto& attribute : m_attributes) { - if(id == attribute.id()) { - return true; - } - } - - return false; -} - -const std::string& SVGElement::getAttribute(PropertyID id) const -{ - for(const auto& attribute : m_attributes) { - if(id == attribute.id()) { - return attribute.value(); - } - } - - return emptyString; -} - -bool SVGElement::setAttribute(int specificity, PropertyID id, const std::string& value) -{ - for(auto& attribute : m_attributes) { - if(id == attribute.id()) { - if(specificity < attribute.specificity()) - return false; - parseAttribute(id, value); - attribute = Attribute(specificity, id, value); - return true; - } - } - - parseAttribute(id, value); - m_attributes.emplace_front(specificity, id, value); - return true; -} - -void SVGElement::setAttributes(const AttributeList& attributes) -{ - for(const auto& attribute : attributes) { - setAttribute(attribute); - } -} - -bool SVGElement::setAttribute(const Attribute& attribute) -{ - return setAttribute(attribute.specificity(), attribute.id(), attribute.value()); -} - -void SVGElement::parseAttribute(PropertyID id, const std::string& value) -{ - rootElement()->setNeedsLayout(); - if(auto property = getProperty(id)) { - property->parse(value); - } -} - -SVGElement* SVGElement::previousElement() const -{ - auto parent = parentElement(); - if(parent == nullptr) - return nullptr; - const auto& children = parent->children(); - auto it = children.begin(); - auto end = children.end(); - SVGElement* element = nullptr; - for(; it != end; ++it) { - SVGNode* node = &**it; - if(node->isTextNode()) - continue; - if(node == this) - return element; - element = static_cast(node); - } - - return nullptr; -} - -SVGElement* SVGElement::nextElement() const -{ - auto parent = parentElement(); - if(parent == nullptr) - return nullptr; - const auto& children = parent->children(); - auto it = children.rbegin(); - auto end = children.rend(); - SVGElement* element = nullptr; - for(; it != end; ++it) { - SVGNode* node = &**it; - if(node->isTextNode()) - continue; - if(node == this) - return element; - element = static_cast(node); - } - - return nullptr; -} - -SVGNode* SVGElement::addChild(std::unique_ptr child) -{ - child->setParentElement(this); - m_children.push_back(std::move(child)); - return &*m_children.back(); -} - -SVGNode* SVGElement::firstChild() const -{ - if(m_children.empty()) - return nullptr; - return &*m_children.front(); -} - -SVGNode* SVGElement::lastChild() const -{ - if(m_children.empty()) - return nullptr; - return &*m_children.back(); -} - -Rect SVGElement::fillBoundingBox() const -{ - auto fillBoundingBox = Rect::Invalid; - for(const auto& child : m_children) { - if(auto element = toSVGElement(child); element && !element->isHiddenElement()) { - fillBoundingBox.unite(element->localTransform().mapRect(element->fillBoundingBox())); - } - } - - if(!fillBoundingBox.isValid()) - fillBoundingBox = Rect::Empty; - return fillBoundingBox; -} - -Rect SVGElement::strokeBoundingBox() const -{ - auto strokeBoundingBox = Rect::Invalid; - for(const auto& child : m_children) { - if(auto element = toSVGElement(child); element && !element->isHiddenElement()) { - strokeBoundingBox.unite(element->localTransform().mapRect(element->strokeBoundingBox())); - } - } - - if(!strokeBoundingBox.isValid()) - strokeBoundingBox = Rect::Empty; - return strokeBoundingBox; -} - -Rect SVGElement::paintBoundingBox() const -{ - if(m_paintBoundingBox.isValid()) - return m_paintBoundingBox; - m_paintBoundingBox = Rect::Empty; - m_paintBoundingBox = strokeBoundingBox(); - assert(m_paintBoundingBox.isValid()); - if(m_clipper) m_paintBoundingBox.intersect(m_clipper->clipBoundingBox(this)); - if(m_masker) m_paintBoundingBox.intersect(m_masker->maskBoundingBox(this)); - return m_paintBoundingBox; -} - -SVGMarkerElement* SVGElement::getMarker(std::string_view id) const -{ - auto element = rootElement()->getElementById(id); - if(element && element->id() == ElementID::Marker) - return static_cast(element); - return nullptr; -} - -SVGClipPathElement* SVGElement::getClipper(std::string_view id) const -{ - auto element = rootElement()->getElementById(id); - if(element && element->id() == ElementID::ClipPath) - return static_cast(element); - return nullptr; -} - -SVGMaskElement* SVGElement::getMasker(std::string_view id) const -{ - auto element = rootElement()->getElementById(id); - if(element && element->id() == ElementID::Mask) - return static_cast(element); - return nullptr; -} - -SVGPaintElement* SVGElement::getPainter(std::string_view id) const -{ - auto element = rootElement()->getElementById(id); - if(element && element->isPaintElement()) - return static_cast(element); - return nullptr; -} - -SVGElement* SVGElement::elementFromPoint(float x, float y) -{ - auto it = m_children.rbegin(); - auto end = m_children.rend(); - for(; it != end; ++it) { - auto child = toSVGElement(*it); - if(child && !child->isHiddenElement()) { - if(auto element = child->elementFromPoint(x, y)) { - return element; - } - } - } - - if(isPointableElement()) { - auto transform = localTransform(); - for(auto parent = parentElement(); parent; parent = parent->parentElement()) - transform.postMultiply(parent->localTransform()); - auto bbox = transform.mapRect(paintBoundingBox()); - if(bbox.contains(x, y)) { - return this; - } - } - - return nullptr; -} - -void SVGElement::addProperty(SVGProperty& value) -{ - m_properties.push_front(&value); -} - -SVGProperty* SVGElement::getProperty(PropertyID id) const -{ - for(auto property : m_properties) { - if(id == property->id()) { - return property; - } - } - - return nullptr; -} - -Size SVGElement::currentViewportSize() const -{ - auto parent = parentElement(); - if(parent == nullptr) { - auto element = static_cast(this); - const auto& viewBox = element->viewBox(); - if(viewBox.value().isValid()) - return viewBox.value().size(); - return Size(300, 150); - } - - if(parent->id() == ElementID::Svg) { - auto element = static_cast(parent); - const auto& viewBox = element->viewBox(); - if(viewBox.value().isValid()) - return viewBox.value().size(); - LengthContext lengthContext(element); - auto width = lengthContext.valueForLength(element->width()); - auto height = lengthContext.valueForLength(element->height()); - return Size(width, height); - } - - return parent->currentViewportSize(); -} - -void SVGElement::cloneChildren(SVGElement* parentElement) const -{ - for(const auto& child : m_children) { - parentElement->addChild(child->clone(true)); - } -} - -std::unique_ptr SVGElement::clone(bool deep) const -{ - auto element = SVGElement::create(document(), m_id); - element->setAttributes(m_attributes); - if(deep) { cloneChildren(element.get()); } - return element; -} - -void SVGElement::build() -{ - for(const auto& child : m_children) { - if(auto element = toSVGElement(child)) { - element->build(); - } - } -} - -void SVGElement::layoutElement(const SVGLayoutState& state) -{ - m_paintBoundingBox = Rect::Invalid; - m_clipper = getClipper(state.clip_path()); - m_masker = getMasker(state.mask()); - m_opacity = state.opacity(); - - m_font_size = state.font_size(); - m_display = state.display(); - m_overflow = state.overflow(); - m_visibility = state.visibility(); - m_pointer_events = state.pointer_events(); -} - -void SVGElement::layoutChildren(SVGLayoutState& state) -{ - for(const auto& child : m_children) { - if(auto element = toSVGElement(child)) { - element->layout(state); - } - } -} - -void SVGElement::layout(SVGLayoutState& state) -{ - SVGLayoutState newState(state, this); - layoutElement(newState); - layoutChildren(newState); -} - -void SVGElement::renderChildren(SVGRenderState& state) const -{ - for(const auto& child : m_children) { - if(auto element = toSVGElement(child)) { - element->render(state); - } - } -} - -void SVGElement::render(SVGRenderState& state) const -{ -} - -bool SVGElement::isHiddenElement() const -{ - if(isDisplayNone()) - return true; - switch(m_id) { - case ElementID::Defs: - case ElementID::Symbol: - case ElementID::Marker: - case ElementID::ClipPath: - case ElementID::Mask: - case ElementID::LinearGradient: - case ElementID::RadialGradient: - case ElementID::Pattern: - case ElementID::Stop: - return true; - default: - return false; - } -} - -bool SVGElement::isPointableElement() const -{ - if(m_pointer_events != PointerEvents::None - && m_visibility != Visibility::Hidden - && m_display != Display::None - && m_opacity != 0.f) { - switch(m_id) { - case ElementID::Line: - case ElementID::Rect: - case ElementID::Ellipse: - case ElementID::Circle: - case ElementID::Polyline: - case ElementID::Polygon: - case ElementID::Path: - case ElementID::Text: - case ElementID::Image: - return true; - default: - break; - } - } - - return false; -} - -SVGStyleElement::SVGStyleElement(Document* document) - : SVGElement(document, ElementID::Style) -{ -} - -SVGFitToViewBox::SVGFitToViewBox(SVGElement* element) - : m_viewBox(PropertyID::ViewBox) - , m_preserveAspectRatio(PropertyID::PreserveAspectRatio) -{ - element->addProperty(m_viewBox); - element->addProperty(m_preserveAspectRatio); -} - -Transform SVGFitToViewBox::viewBoxToViewTransform(const Size& viewportSize) const -{ - const auto& viewBoxRect = m_viewBox.value(); - if(viewBoxRect.isEmpty() || viewportSize.isEmpty()) - return Transform::Identity; - return m_preserveAspectRatio.getTransform(viewBoxRect, viewportSize); -} - -Rect SVGFitToViewBox::getClipRect(const Size& viewportSize) const -{ - const auto& viewBoxRect = m_viewBox.value(); - if(viewBoxRect.isEmpty() || viewportSize.isEmpty()) - return Rect(0, 0, viewportSize.w, viewportSize.h); - return m_preserveAspectRatio.getClipRect(viewBoxRect, viewportSize); -} - -SVGURIReference::SVGURIReference(SVGElement* element) - : m_href(PropertyID::Href) -{ - element->addProperty(m_href); -} - -SVGElement* SVGURIReference::getTargetElement(const Document* document) const -{ - std::string_view value(m_href.value()); - if(value.empty() || value.front() != '#') - return nullptr; - return document->rootElement()->getElementById(value.substr(1)); -} - -bool SVGPaintServer::applyPaint(SVGRenderState& state) const -{ - if(!isRenderable()) - return false; - if(m_element) return m_element->applyPaint(state, m_opacity); - state->setColor(m_color.colorWithAlpha(m_opacity)); - return true; -} - -SVGGraphicsElement::SVGGraphicsElement(Document* document, ElementID id) - : SVGElement(document, id) - , m_transform(PropertyID::Transform) -{ - addProperty(m_transform); -} - -SVGPaintServer SVGGraphicsElement::getPaintServer(const Paint& paint, float opacity) const -{ - if(paint.isNone()) - return SVGPaintServer(); - if(auto element = getPainter(paint.id())) - return SVGPaintServer(element, paint.color(), opacity); - return SVGPaintServer(nullptr, paint.color(), opacity); -} - -StrokeData SVGGraphicsElement::getStrokeData(const SVGLayoutState& state) const -{ - LengthContext lengthContext(this); - StrokeData strokeData(lengthContext.valueForLength(state.stroke_width(), LengthDirection::Diagonal)); - strokeData.setMiterLimit(state.stroke_miterlimit()); - strokeData.setLineCap(state.stroke_linecap()); - strokeData.setLineJoin(state.stroke_linejoin()); - strokeData.setDashOffset(lengthContext.valueForLength(state.stroke_dashoffset(), LengthDirection::Diagonal)); - - DashArray dashArray; - for(const auto& dash : state.stroke_dasharray()) - dashArray.push_back(lengthContext.valueForLength(dash, LengthDirection::Diagonal)); - strokeData.setDashArray(std::move(dashArray)); - return strokeData; -} - -SVGSVGElement::SVGSVGElement(Document* document) - : SVGGraphicsElement(document, ElementID::Svg) - , SVGFitToViewBox(this) - , m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) - , m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) -{ - addProperty(m_x); - addProperty(m_y); - addProperty(m_width); - addProperty(m_height); -} - -Transform SVGSVGElement::localTransform() const -{ - LengthContext lengthContext(this); - const Rect viewportRect = { - lengthContext.valueForLength(m_x), - lengthContext.valueForLength(m_y), - lengthContext.valueForLength(m_width), - lengthContext.valueForLength(m_height) - }; - - if(isRootElement()) - return viewBoxToViewTransform(viewportRect.size()); - return SVGGraphicsElement::localTransform() * Transform::translated(viewportRect.x, viewportRect.y) * viewBoxToViewTransform(viewportRect.size()); -} - -void SVGSVGElement::render(SVGRenderState& state) const -{ - if(isDisplayNone()) - return; - LengthContext lengthContext(this); - const Size viewportSize = { - lengthContext.valueForLength(m_width), - lengthContext.valueForLength(m_height) - }; - - if(viewportSize.isEmpty()) - return; - SVGBlendInfo blendInfo(this); - SVGRenderState newState(this, state, localTransform()); - newState.beginGroup(blendInfo); - if(isOverflowHidden()) - newState->clipRect(getClipRect(viewportSize), FillRule::NonZero, newState.currentTransform()); - renderChildren(newState); - newState.endGroup(blendInfo); -} - -SVGRootElement::SVGRootElement(Document* document) - : SVGSVGElement(document) -{ -} - -SVGRootElement* SVGRootElement::layoutIfNeeded() -{ - if(needsLayout()) - forceLayout(); - return this; -} - -SVGElement* SVGRootElement::getElementById(std::string_view id) const -{ - auto it = m_idCache.find(id); - if(it == m_idCache.end()) - return nullptr; - return it->second; -} - -void SVGRootElement::addElementById(const std::string& id, SVGElement* element) -{ - m_idCache.emplace(id, element); -} - -void SVGRootElement::layout(SVGLayoutState& state) -{ - SVGSVGElement::layout(state); - - LengthContext lengthContext(this); - if(!width().isPercent()) { - m_intrinsicWidth = lengthContext.valueForLength(width()); - } else { - m_intrinsicWidth = 0.f; - } - - if(!height().isPercent()) { - m_intrinsicHeight = lengthContext.valueForLength(height()); - } else { - m_intrinsicHeight = 0.f; - } - - const auto& viewBoxRect = viewBox().value(); - if(!viewBoxRect.isEmpty() && (!m_intrinsicWidth || !m_intrinsicHeight)) { - auto intrinsicRatio = viewBoxRect.w / viewBoxRect.h; - if(!m_intrinsicWidth && m_intrinsicHeight) - m_intrinsicWidth = m_intrinsicHeight * intrinsicRatio; - else if(m_intrinsicWidth && !m_intrinsicHeight) { - m_intrinsicHeight = m_intrinsicWidth / intrinsicRatio; - } - } - - if(viewBoxRect.isValid() && (!m_intrinsicWidth || !m_intrinsicHeight)) { - m_intrinsicWidth = viewBoxRect.w; - m_intrinsicHeight = viewBoxRect.h; - } - - if(!m_intrinsicWidth || !m_intrinsicHeight) { - auto boundingBox = paintBoundingBox(); - if(!m_intrinsicWidth) - m_intrinsicWidth = boundingBox.right(); - if(!m_intrinsicHeight) { - m_intrinsicHeight = boundingBox.bottom(); - } - } -} - -void SVGRootElement::forceLayout() -{ - SVGLayoutState state; - layout(state); -} - -SVGUseElement::SVGUseElement(Document* document) - : SVGGraphicsElement(document, ElementID::Use) - , SVGURIReference(this) - , m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) - , m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) -{ - addProperty(m_x); - addProperty(m_y); - addProperty(m_width); - addProperty(m_height); -} - -Transform SVGUseElement::localTransform() const -{ - LengthContext lengthContext(this); - const Point translation = { - lengthContext.valueForLength(m_x), - lengthContext.valueForLength(m_y) - }; - - return SVGGraphicsElement::localTransform() * Transform::translated(translation.x, translation.y); -} - -void SVGUseElement::render(SVGRenderState& state) const -{ - if(isDisplayNone()) - return; - SVGBlendInfo blendInfo(this); - SVGRenderState newState(this, state, localTransform()); - newState.beginGroup(blendInfo); - renderChildren(newState); - newState.endGroup(blendInfo); -} - -void SVGUseElement::build() -{ - if(auto targetElement = getTargetElement(document())) { - if(auto newElement = cloneTargetElement(targetElement)) { - addChild(std::move(newElement)); - } - } - - SVGGraphicsElement::build(); -} - -inline bool isDisallowedElement(const SVGElement* element) -{ - switch(element->id()) { - case ElementID::Circle: - case ElementID::Ellipse: - case ElementID::G: - case ElementID::Image: - case ElementID::Line: - case ElementID::Path: - case ElementID::Polygon: - case ElementID::Polyline: - case ElementID::Rect: - case ElementID::Svg: - case ElementID::Symbol: - case ElementID::Text: - case ElementID::Tspan: - case ElementID::Use: - return false; - default: - return true; - } -} - -std::unique_ptr SVGUseElement::cloneTargetElement(SVGElement* targetElement) -{ - if(targetElement == this || isDisallowedElement(targetElement)) - return nullptr; - const auto& idAttr = targetElement->getAttribute(PropertyID::Id); - auto parent = parentElement(); - while(parent) { - auto attribute = parent->findAttribute(PropertyID::Id); - if(attribute && idAttr == attribute->value()) - return nullptr; - parent = parent->parentElement(); - } - - auto tagId = targetElement->id(); - if(tagId == ElementID::Symbol) { - tagId = ElementID::Svg; - } - - auto newElement = SVGElement::create(document(), tagId); - newElement->setAttributes(targetElement->attributes()); - if(newElement->id() == ElementID::Svg) { - for(const auto& attribute : attributes()) { - if(attribute.id() == PropertyID::Width || attribute.id() == PropertyID::Height) { - newElement->setAttribute(attribute); - } - } - } - - if(newElement->id() != ElementID::Use) - targetElement->cloneChildren(newElement.get()); - return newElement; -} - -SVGImageElement::SVGImageElement(Document* document) - : SVGGraphicsElement(document, ElementID::Image) - , m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) - , m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid, 100.f, LengthUnits::Percent) - , m_preserveAspectRatio(PropertyID::PreserveAspectRatio) -{ - addProperty(m_x); - addProperty(m_y); - addProperty(m_width); - addProperty(m_height); - addProperty(m_preserveAspectRatio); -} - -Rect SVGImageElement::fillBoundingBox() const -{ - LengthContext lengthContext(this); - const Rect viewportRect = { - lengthContext.valueForLength(m_x), - lengthContext.valueForLength(m_y), - lengthContext.valueForLength(m_width), - lengthContext.valueForLength(m_height) - }; - - return viewportRect; -} - -Rect SVGImageElement::strokeBoundingBox() const -{ - return fillBoundingBox(); -} - -void SVGImageElement::render(SVGRenderState& state) const -{ - if(m_image.isNull() || isDisplayNone() || isVisibilityHidden()) - return; - Rect dstRect(fillBoundingBox()); - Rect srcRect(0, 0, m_image.width(), m_image.height()); - if(dstRect.isEmpty() || srcRect.isEmpty()) - return; - m_preserveAspectRatio.transformRect(dstRect, srcRect); - - SVGBlendInfo blendInfo(this); - SVGRenderState newState(this, state, localTransform()); - newState.beginGroup(blendInfo); - newState->drawImage(m_image, dstRect, srcRect, newState.currentTransform()); - newState.endGroup(blendInfo); -} - -static Bitmap loadImageResource(const std::string& href) -{ - if(href.compare(0, 5, "data:") == 0) { - std::string_view input(href); - auto index = input.find(',', 5); - if(index == std::string_view::npos) - return Bitmap(); - input.remove_prefix(index + 1); - return plutovg_surface_load_from_image_base64(input.data(), input.length()); - } - - return plutovg_surface_load_from_image_file(href.data()); -} - -void SVGImageElement::parseAttribute(PropertyID id, const std::string& value) -{ - if(id == PropertyID::Href) { - m_image = loadImageResource(value); - } else { - SVGGraphicsElement::parseAttribute(id, value); - } -} - -SVGSymbolElement::SVGSymbolElement(Document* document) - : SVGGraphicsElement(document, ElementID::Symbol) - , SVGFitToViewBox(this) -{ -} - -SVGGElement::SVGGElement(Document* document) - : SVGGraphicsElement(document, ElementID::G) -{ -} - -void SVGGElement::render(SVGRenderState& state) const -{ - if(isDisplayNone()) - return; - SVGBlendInfo blendInfo(this); - SVGRenderState newState(this, state, localTransform()); - newState.beginGroup(blendInfo); - renderChildren(newState); - newState.endGroup(blendInfo); -} - -SVGDefsElement::SVGDefsElement(Document* document) - : SVGGraphicsElement(document, ElementID::Defs) -{ -} - -SVGMarkerElement::SVGMarkerElement(Document* document) - : SVGElement(document, ElementID::Marker) - , SVGFitToViewBox(this) - , m_refX(PropertyID::RefX, LengthDirection::Horizontal, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_refY(PropertyID::RefY, LengthDirection::Vertical, LengthNegativeMode::Allow, 0.f, LengthUnits::None) - , m_markerWidth(PropertyID::MarkerWidth, LengthDirection::Horizontal, LengthNegativeMode::Forbid, 3.f, LengthUnits::None) - , m_markerHeight(PropertyID::MarkerHeight, LengthDirection::Vertical, LengthNegativeMode::Forbid, 3.f, LengthUnits::None) - , m_markerUnits(PropertyID::MarkerUnits, MarkerUnits::StrokeWidth) - , m_orient(PropertyID::Orient) -{ - addProperty(m_refX); - addProperty(m_refY); - addProperty(m_markerWidth); - addProperty(m_markerHeight); - addProperty(m_markerUnits); - addProperty(m_orient); -} - -Point SVGMarkerElement::refPoint() const -{ - LengthContext lengthContext(this); - const Point refPoint = { - lengthContext.valueForLength(m_refX), - lengthContext.valueForLength(m_refY) - }; - - return refPoint; -} - -Size SVGMarkerElement::markerSize() const -{ - LengthContext lengthContext(this); - const Size markerSize = { - lengthContext.valueForLength(m_markerWidth), - lengthContext.valueForLength(m_markerHeight) - }; - - return markerSize; -} - -Transform SVGMarkerElement::markerTransform(const Point& origin, float angle, float strokeWidth) const -{ - auto transform = Transform::translated(origin.x, origin.y); - if(m_orient.orientType() == SVGAngle::OrientType::Angle) { - transform.rotate(m_orient.value()); - } else { - transform.rotate(angle); - } - - auto viewTransform = viewBoxToViewTransform(markerSize()); - auto refOrigin = viewTransform.mapPoint(refPoint()); - if(m_markerUnits.value() == MarkerUnits::StrokeWidth) - transform.scale(strokeWidth, strokeWidth); - transform.translate(-refOrigin.x, -refOrigin.y); - return transform * viewTransform; -} - -Rect SVGMarkerElement::markerBoundingBox(const Point& origin, float angle, float strokeWidth) const -{ - return markerTransform(origin, angle, strokeWidth).mapRect(paintBoundingBox()); -} - -void SVGMarkerElement::renderMarker(SVGRenderState& state, const Point& origin, float angle, float strokeWidth) const -{ - if(state.hasCycleReference(this)) - return; - SVGBlendInfo blendInfo(this); - SVGRenderState newState(this, state, markerTransform(origin, angle, strokeWidth)); - newState.beginGroup(blendInfo); - if(isOverflowHidden()) - newState->clipRect(getClipRect(markerSize()), FillRule::NonZero, newState.currentTransform()); - renderChildren(newState); - newState.endGroup(blendInfo); -} - -Transform SVGMarkerElement::localTransform() const -{ - return viewBoxToViewTransform(markerSize()); -} - -SVGClipPathElement::SVGClipPathElement(Document* document) - : SVGGraphicsElement(document, ElementID::ClipPath) - , m_clipPathUnits(PropertyID::ClipPathUnits, Units::UserSpaceOnUse) -{ - addProperty(m_clipPathUnits); -} - -Rect SVGClipPathElement::clipBoundingBox(const SVGElement* element) const -{ - auto clipBoundingBox = paintBoundingBox(); - if(m_clipPathUnits.value() == Units::ObjectBoundingBox) { - auto bbox = element->fillBoundingBox(); - clipBoundingBox.x = clipBoundingBox.x * bbox.w + bbox.x; - clipBoundingBox.y = clipBoundingBox.y * bbox.h + bbox.y; - clipBoundingBox.w = clipBoundingBox.w * bbox.w; - clipBoundingBox.h = clipBoundingBox.h * bbox.h; - } - - return localTransform().mapRect(clipBoundingBox); -} - -void SVGClipPathElement::applyClipMask(SVGRenderState& state) const -{ - if(state.hasCycleReference(this)) - return; - auto maskImage = Canvas::create(state.currentTransform().mapRect(state.paintBoundingBox())); - auto currentTransform = state.currentTransform() * localTransform(); - if(m_clipPathUnits.value() == Units::ObjectBoundingBox) { - auto bbox = state.fillBoundingBox(); - currentTransform.translate(bbox.x, bbox.y); - currentTransform.scale(bbox.w, bbox.h); - } - - SVGRenderState newState(this, &state, currentTransform, SVGRenderMode::Clipping, maskImage); - renderChildren(newState); - if(clipper()) { - clipper()->applyClipMask(newState); - } - - state->blendCanvas(*maskImage, BlendMode::Dst_In, 1.f); -} - -inline const SVGGeometryElement* toSVGGeometryElement(const SVGNode* node) -{ - if(node && node->isGeometryElement()) - return static_cast(node); - return nullptr; -} - -void SVGClipPathElement::applyClipPath(SVGRenderState& state) const -{ - auto currentTransform = state.currentTransform() * localTransform(); - if(m_clipPathUnits.value() == Units::ObjectBoundingBox) { - auto bbox = state.fillBoundingBox(); - currentTransform.translate(bbox.x, bbox.y); - currentTransform.scale(bbox.w, bbox.h); - } - - for(const auto& child : children()) { - auto element = toSVGElement(child); - if(element == nullptr || element->isDisplayNone()) - continue; - Transform clipTransform(currentTransform); - auto shapeElement = toSVGGeometryElement(element); - if(shapeElement == nullptr) { - if(element->id() != ElementID::Use) - continue; - clipTransform.multiply(element->localTransform()); - shapeElement = toSVGGeometryElement(element->firstChild()); - } - - if(shapeElement == nullptr || !shapeElement->isRenderable()) - continue; - state->clipPath(shapeElement->path(), shapeElement->clip_rule(), clipTransform * shapeElement->localTransform()); - return; - } - - state->clipRect(Rect::Empty, FillRule::NonZero, Transform::Identity); -} - -bool SVGClipPathElement::requiresMasking() const -{ - if(clipper()) - return true; - const SVGGeometryElement* prevShapeElement = nullptr; - for(const auto& child : children()) { - auto element = toSVGElement(child); - if(element == nullptr || element->isDisplayNone()) - continue; - auto shapeElement = toSVGGeometryElement(element); - if(shapeElement == nullptr) { - if(element->isTextPositioningElement()) - return true; - if(element->id() != ElementID::Use) - continue; - if(element->clipper()) - return true; - shapeElement = toSVGGeometryElement(element->firstChild()); - } - - if(shapeElement == nullptr || !shapeElement->isRenderable()) - continue; - if(prevShapeElement || shapeElement->clipper()) - return true; - prevShapeElement = shapeElement; - } - - return false; -} - -SVGMaskElement::SVGMaskElement(Document* document) - : SVGElement(document, ElementID::Mask) - , m_x(PropertyID::X, LengthDirection::Horizontal, LengthNegativeMode::Allow, -10.f, LengthUnits::Percent) - , m_y(PropertyID::Y, LengthDirection::Vertical, LengthNegativeMode::Allow, -10.f, LengthUnits::Percent) - , m_width(PropertyID::Width, LengthDirection::Horizontal, LengthNegativeMode::Forbid, 120.f, LengthUnits::Percent) - , m_height(PropertyID::Height, LengthDirection::Vertical, LengthNegativeMode::Forbid, 120.f, LengthUnits::Percent) - , m_maskUnits(PropertyID::MaskUnits, Units::ObjectBoundingBox) - , m_maskContentUnits(PropertyID::MaskContentUnits, Units::UserSpaceOnUse) -{ - addProperty(m_x); - addProperty(m_y); - addProperty(m_width); - addProperty(m_height); - addProperty(m_maskUnits); - addProperty(m_maskContentUnits); -} - -Rect SVGMaskElement::maskRect(const SVGElement* element) const -{ - LengthContext lengthContext(this, m_maskUnits.value()); - Rect maskRect = { - lengthContext.valueForLength(m_x), - lengthContext.valueForLength(m_y), - lengthContext.valueForLength(m_width), - lengthContext.valueForLength(m_height) - }; - - if(m_maskUnits.value() == Units::ObjectBoundingBox) { - auto bbox = element->fillBoundingBox(); - maskRect.x = maskRect.x * bbox.w + bbox.x; - maskRect.y = maskRect.y * bbox.h + bbox.y; - maskRect.w = maskRect.w * bbox.w; - maskRect.h = maskRect.h * bbox.h; - } - - return maskRect; -} - -Rect SVGMaskElement::maskBoundingBox(const SVGElement* element) const -{ - auto maskBoundingBox = paintBoundingBox(); - if(m_maskContentUnits.value() == Units::ObjectBoundingBox) { - auto bbox = element->fillBoundingBox(); - maskBoundingBox.x = maskBoundingBox.x * bbox.w + bbox.x; - maskBoundingBox.y = maskBoundingBox.y * bbox.h + bbox.y; - maskBoundingBox.w = maskBoundingBox.w * bbox.w; - maskBoundingBox.h = maskBoundingBox.h * bbox.h; - } - - return maskBoundingBox.intersected(maskRect(element)); -} - -void SVGMaskElement::applyMask(SVGRenderState& state) const -{ - if(state.hasCycleReference(this)) - return; - auto maskImage = Canvas::create(state.currentTransform().mapRect(state.paintBoundingBox())); - maskImage->clipRect(maskRect(state.element()), FillRule::NonZero, state.currentTransform()); - - auto currentTransform = state.currentTransform(); - if(m_maskContentUnits.value() == Units::ObjectBoundingBox) { - auto bbox = state.fillBoundingBox(); - currentTransform.translate(bbox.x, bbox.y); - currentTransform.scale(bbox.w, bbox.h); - } - - SVGRenderState newState(this, &state, currentTransform, SVGRenderMode::Painting, maskImage); - renderChildren(newState); - if(clipper()) - clipper()->applyClipMask(newState); - if(masker()) { - masker()->applyMask(newState); - } - - if(m_mask_type == MaskType::Luminance) - maskImage->convertToLuminanceMask(); - state->blendCanvas(*maskImage, BlendMode::Dst_In, 1.f); -} - -void SVGMaskElement::layoutElement(const SVGLayoutState& state) -{ - m_mask_type = state.mask_type(); - SVGElement::layoutElement(state); -} - -} // namespace lunasvg diff --git a/vendor/lunasvg/source/svgelement.h b/vendor/lunasvg/source/svgelement.h deleted file mode 100644 index ff5d008..0000000 --- a/vendor/lunasvg/source/svgelement.h +++ /dev/null @@ -1,498 +0,0 @@ -#ifndef LUNASVG_SVGELEMENT_H -#define LUNASVG_SVGELEMENT_H - -#include "lunasvg.h" -#include "svgproperty.h" - -#include -#include -#include -#include - -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 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 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; - -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>; -using SVGPropertyList = std::forward_list; - -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 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 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 - 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 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(node); - return nullptr; -} - -inline SVGElement* toSVGElement(SVGNode* node) -{ - if(node && node->isElement()) - return static_cast(node); - return nullptr; -} - -inline SVGElement* toSVGElement(const std::unique_ptr& node) -{ - return toSVGElement(node.get()); -} - -template -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> 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 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() 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 m_markerUnits; - SVGAngle m_orient; -}; - -class SVGClipPathElement final : public SVGGraphicsElement { -public: - SVGClipPathElement(Document* document); - - const SVGEnumeration& 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 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& maskUnits() const { return m_maskUnits; } - const SVGEnumeration& 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 m_maskUnits; - SVGEnumeration m_maskContentUnits; - MaskType m_mask_type = MaskType::Luminance; -}; - -} // namespace lunasvg - -#endif // LUNASVG_SVGELEMENT_H diff --git a/vendor/lunasvg/source/svggeometryelement.cpp b/vendor/lunasvg/source/svggeometryelement.cpp deleted file mode 100644 index e2c9708..0000000 --- a/vendor/lunasvg/source/svggeometryelement.cpp +++ /dev/null @@ -1,324 +0,0 @@ -#include "svggeometryelement.h" -#include "svglayoutstate.h" -#include "svgrenderstate.h" - -#include - -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 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 diff --git a/vendor/lunasvg/source/svggeometryelement.h b/vendor/lunasvg/source/svggeometryelement.h deleted file mode 100644 index b02d8b3..0000000 --- a/vendor/lunasvg/source/svggeometryelement.h +++ /dev/null @@ -1,139 +0,0 @@ -#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; - -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 diff --git a/vendor/lunasvg/source/svglayoutstate.cpp b/vendor/lunasvg/source/svglayoutstate.cpp deleted file mode 100644 index bf5b92d..0000000 --- a/vendor/lunasvg/source/svglayoutstate.cpp +++ /dev/null @@ -1,607 +0,0 @@ -#include "svglayoutstate.h" -#include "svgelement.h" -#include "svgparserutils.h" - -#include - -namespace lunasvg { - -static std::optional 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 -static Enum parseEnumValue(std::string_view input, const SVGEnumerationEntry(&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 entries[] = { - {Display::Inline, "inline"}, - {Display::None, "none"} - }; - - return parseEnumValue(input, entries, Display::Inline); -} - -static Visibility parseVisibility(std::string_view input) -{ - static const SVGEnumerationEntry 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 entries[] = { - {Overflow::Visible, "visible"}, - {Overflow::Hidden, "hidden"} - }; - - return parseEnumValue(input, entries, Overflow::Visible); -} - -static PointerEvents parsePointerEvents(std::string_view input) -{ - static const SVGEnumerationEntry 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 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 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 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 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 entries[] = { - {Direction::Ltr, "ltr"}, - {Direction::Rtl, "rtl"} - }; - - return parseEnumValue(input, entries, Direction::Ltr); -} - -static WritingMode parseWritingMode(std::string_view input) -{ - static const SVGEnumerationEntry 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 entries[] = { - {TextOrientation::Mixed, "mixed"}, - {TextOrientation::Upright, "upright"} - }; - - return parseEnumValue(input, entries, TextOrientation::Mixed); -} - -static TextAnchor parseTextAnchor(std::string_view input) -{ - static const SVGEnumerationEntry 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 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 entries[] = { - {MaskType::Luminance, "luminance"}, - {MaskType::Alpha, "alpha"} - }; - - return parseEnumValue(input, entries, MaskType::Luminance); -} - -static FillRule parseFillRule(std::string_view input) -{ - static const SVGEnumerationEntry entries[] = { - {FillRule::NonZero, "nonzero"}, - {FillRule::EvenOdd, "evenodd"} - }; - - return parseEnumValue(input, entries, FillRule::NonZero); -} - -static LineCap parseLineCap(std::string_view input) -{ - static const SVGEnumerationEntry 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 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 diff --git a/vendor/lunasvg/source/svglayoutstate.h b/vendor/lunasvg/source/svglayoutstate.h deleted file mode 100644 index 4d08ad9..0000000 --- a/vendor/lunasvg/source/svglayoutstate.h +++ /dev/null @@ -1,129 +0,0 @@ -#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 diff --git a/vendor/lunasvg/source/svgpaintelement.cpp b/vendor/lunasvg/source/svgpaintelement.cpp deleted file mode 100644 index 9432d6e..0000000 --- a/vendor/lunasvg/source/svgpaintelement.cpp +++ /dev/null @@ -1,364 +0,0 @@ -#include "svgpaintelement.h" -#include "svglayoutstate.h" -#include "svgrenderstate.h" - -#include -#include - -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 processedGradients; - const SVGGradientElement* current = this; - while(true) { - current->collectGradientAttributes(attributes); - if(current->id() == ElementID::LinearGradient) { - auto element = static_cast(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(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(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 processedGradients; - const SVGGradientElement* current = this; - while(true) { - current->collectGradientAttributes(attributes); - if(current->id() == ElementID::RadialGradient) { - auto element = static_cast(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(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 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(targetElement); - if(processedPatterns.count(current) > 0) { - break; - } - } - - attributes.setDefaultValues(this); - return attributes; -} - -} // namespace lunasvg diff --git a/vendor/lunasvg/source/svgpaintelement.h b/vendor/lunasvg/source/svgpaintelement.h deleted file mode 100644 index 19f605a..0000000 --- a/vendor/lunasvg/source/svgpaintelement.h +++ /dev/null @@ -1,288 +0,0 @@ -#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& gradientUnits() const { return m_gradientUnits; } - const SVGEnumeration& spreadMethod() const { return m_spreadMethod; } - void collectGradientAttributes(SVGGradientAttributes& attributes) const; - -private: - SVGTransform m_gradientTransform; - SVGEnumeration m_gradientUnits; - SVGEnumeration 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& patternUnits() const { return m_patternUnits; } - const SVGEnumeration& 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 m_patternUnits; - SVGEnumeration 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 diff --git a/vendor/lunasvg/source/svgparser.cpp b/vendor/lunasvg/source/svgparser.cpp deleted file mode 100644 index 183937c..0000000 --- a/vendor/lunasvg/source/svgparser.cpp +++ /dev/null @@ -1,956 +0,0 @@ -#include "lunasvg.h" -#include "svgelement.h" -#include "svgparserutils.h" - -#include - -namespace lunasvg { - -struct SimpleSelector; - -using Selector = std::vector; -using SelectorList = std::vector; - -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 attributeSelectors; - std::vector pseudoClassSelectors; -}; - -struct Declaration { - int specificity; - PropertyID id; - std::string value; -}; - -using DeclarationList = std::vector; - -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; - -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(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(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 diff --git a/vendor/lunasvg/source/svgparserutils.h b/vendor/lunasvg/source/svgparserutils.h deleted file mode 100644 index 2cf2cf0..0000000 --- a/vendor/lunasvg/source/svgparserutils.h +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef LUNASVG_SVGPARSERUTILS_H -#define LUNASVG_SVGPARSERUTILS_H - -#include -#include -#include - -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 -inline bool parseInteger(std::string_view& input, T& integer, int base = 10) -{ - constexpr bool isSigned = std::numeric_limits::is_signed; - constexpr T intMax = std::numeric_limits::max(); - const T maxMultiplier = intMax / static_cast(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(digitValue) > (intMax % static_cast(base)) + isNegative)) - return false; - value = static_cast(base) * value + static_cast(digitValue); - input.remove_prefix(1); - } while(!input.empty() && isIntegralDigit(input.front(), base)); - - using SignedType = typename std::make_signed::type; - if(isNegative) - integer = -static_cast(value); - else - integer = value; - return true; -} - -template -inline bool parseNumber(std::string_view& input, T& number) -{ - constexpr T maxValue = std::numeric_limits::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(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(1); - do { - fraction = static_cast(10) * fraction + (input.front() - '0'); - divisor *= static_cast(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(std::pow(10.0, expsign * exponent)); - return number >= -maxValue && number <= maxValue; -} - -} // namespace lunasvg - -#endif // LUNASVG_SVGPARSERUTILS_H diff --git a/vendor/lunasvg/source/svgproperty.cpp b/vendor/lunasvg/source/svgproperty.cpp deleted file mode 100644 index 51c7ecc..0000000 --- a/vendor/lunasvg/source/svgproperty.cpp +++ /dev/null @@ -1,705 +0,0 @@ -#include "svgproperty.h" -#include "svgelement.h" -#include "svgparserutils.h" - -#include - -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::parse(std::string_view input) -{ - static const SVGEnumerationEntry entries[] = { - {SpreadMethod::Pad, "pad"}, - {SpreadMethod::Reflect, "reflect"}, - {SpreadMethod::Repeat, "repeat"} - }; - - return parseEnum(input, entries); -} - -template<> -bool SVGEnumeration::parse(std::string_view input) -{ - static const SVGEnumerationEntry entries[] = { - {Units::UserSpaceOnUse, "userSpaceOnUse"}, - {Units::ObjectBoundingBox, "objectBoundingBox"} - }; - - return parseEnum(input, entries); -} - -template<> -bool SVGEnumeration::parse(std::string_view input) -{ - static const SVGEnumerationEntry entries[] = { - {MarkerUnits::StrokeWidth, "strokeWidth"}, - {MarkerUnits::UserSpaceOnUse, "userSpaceOnUse"} - }; - - return parseEnum(input, entries); -} - -template<> -bool SVGEnumeration::parse(std::string_view input) -{ - static const SVGEnumerationEntry entries[] = { - {LengthAdjust::Spacing, "spacing"}, - {LengthAdjust::SpacingAndGlyphs, "spacingAndGlyphs"} - }; - - return parseEnum(input, entries); -} - -template -template -bool SVGEnumeration::parseEnum(std::string_view input, const SVGEnumerationEntry(&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 diff --git a/vendor/lunasvg/source/svgproperty.h b/vendor/lunasvg/source/svgproperty.h deleted file mode 100644 index 74a23a8..0000000 --- a/vendor/lunasvg/source/svgproperty.h +++ /dev/null @@ -1,570 +0,0 @@ -#ifndef LUNASVG_SVGPROPERTY_H -#define LUNASVG_SVGPROPERTY_H - -#include "graphics.h" - -#include - -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 -using SVGEnumerationEntry = std::pair; - -template -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 - bool parseEnum(std::string_view input, const SVGEnumerationEntry(&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; - -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; - -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; - -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 diff --git a/vendor/lunasvg/source/svgrenderstate.cpp b/vendor/lunasvg/source/svgrenderstate.cpp deleted file mode 100644 index 550d389..0000000 --- a/vendor/lunasvg/source/svgrenderstate.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#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 diff --git a/vendor/lunasvg/source/svgrenderstate.h b/vendor/lunasvg/source/svgrenderstate.h deleted file mode 100644 index 1e0d434..0000000 --- a/vendor/lunasvg/source/svgrenderstate.h +++ /dev/null @@ -1,69 +0,0 @@ -#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) - : 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() 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 m_canvas; -}; - -} // namespace lunasvg - -#endif // LUNASVG_SVGRENDERSTATE_H diff --git a/vendor/lunasvg/source/svgtextelement.cpp b/vendor/lunasvg/source/svgtextelement.cpp deleted file mode 100644 index 6d43e57..0000000 --- a/vendor/lunasvg/source/svgtextelement.cpp +++ /dev/null @@ -1,579 +0,0 @@ -#include "svgtextelement.h" -#include "svglayoutstate.h" -#include "svgrenderstate.h" - -#include - -namespace lunasvg { - -inline const SVGTextNode* toSVGTextNode(const SVGNode* node) -{ - assert(node && node->isTextNode()); - return static_cast(node); -} - -inline const SVGTextPositioningElement* toSVGTextPositioningElement(const SVGNode* node) -{ - assert(node && node->isTextPositioningElement()); - return static_cast(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 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 diff --git a/vendor/lunasvg/source/svgtextelement.h b/vendor/lunasvg/source/svgtextelement.h deleted file mode 100644 index 71b0f11..0000000 --- a/vendor/lunasvg/source/svgtextelement.h +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef LUNASVG_SVGTEXTELEMENT_H -#define LUNASVG_SVGTEXTELEMENT_H - -#include "svgelement.h" - -#include - -namespace lunasvg { - -class SVGTextPositioningElement; -class SVGTextElement; - -struct SVGCharacterPosition { - std::optional x; - std::optional y; - std::optional dx; - std::optional dy; - std::optional rotate; -}; - -using SVGCharacterPositions = std::map; - -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; - -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; - -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 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 diff --git a/vendor/nanosvg/nanosvg.h b/vendor/nanosvg/nanosvg.h new file mode 100644 index 0000000..f5884c8 --- /dev/null +++ b/vendor/nanosvg/nanosvg.h @@ -0,0 +1,3132 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example + * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) + * + * Arc calculation code based on canvg (https://code.google.com/p/canvg/) + * + * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * + */ + +#ifndef NANOSVG_H +#define NANOSVG_H + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. +// +// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. +// +// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! +// +// The shapes in the SVG images are transformed by the viewBox and converted to specified units. +// That is, you should get the same looking data as your designed in your favorite app. +// +// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose +// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. +// +// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// DPI (dots-per-inch) controls how the unit conversion is done. +// +// If you don't know or care about the units stuff, "px" and 96 should get you going. + + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + printf("size: %f x %f\n", image->width, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_UNDEF = -1, + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2 +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2 +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2 +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1 +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +enum NSVGpaintOrder { + NSVG_PAINT_FILL = 0x00, + NSVG_PAINT_MARKERS = 0x01, + NSVG_PAINT_STROKE = 0x02, +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + signed char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char paintOrder; // Encoded paint order (3×2-bit fields) see NSVGpaintOrder + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + char fillGradient[64]; // Optional 'id' of fill gradient + char strokeGradient[64]; // Optional 'id' of stroke gradient + float xform[6]; // Root transformation for fill/stroke gradient + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. +void nsvgDelete(NSVGimage* image); + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1 +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + signed char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; + unsigned char paintOrder; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + NSVGshape* shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static unsigned char nsvg__encodePaintOrder(enum NSVGpaintOrder a, enum NSVGpaintOrder b, enum NSVGpaintOrder c) { + return (a & 0x03) | ((b & 0x03) << 2) | ((c & 0x03) << 4); +} + +static NSVGparser* nsvg__createParser(void) +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + p->attr[0].paintOrder = nsvg__encodePaintOrder(NSVG_PAINT_FILL, NSVG_PAINT_STROKE, NSVG_PAINT_MARKERS); + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + if (p->npts > 0) { + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); + } +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + if (id == NULL || *id == '\0') + return NULL; + while (grad != NULL) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, float *xform, signed char* paintType) +{ + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + int refIter; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + refIter = 0; + while (ref != NULL) { + NSVGgradientData* nextRef = NULL; + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + nextRef = nsvg__findGradientData(p, ref->ref); + if (nextRef == ref) break; // prevent infite loops on malformed data + ref = nextRef; + refIter++; + if (refIter > 32) break; // prevent infite loops on malformed data + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape* shape; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + memcpy(shape->fillGradient, attr->fillGradient, sizeof shape->fillGradient); + memcpy(shape->strokeGradient, attr->strokeGradient, sizeof shape->strokeGradient); + memcpy(shape->xform, attr->xform, sizeof shape->xform); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + shape->paintOrder = attr->paintOrder; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + shape->fill.type = NSVG_PAINT_UNDEF; + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + shape->stroke.type = NSVG_PAINT_UNDEF; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + // Expect 1 + N*3 points (N = number of cubic bezier segments). + if ((p->npts % 3) != 1) + return; + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItemWhenArcFlag(const char* s, char* it) +{ + it[0] = '\0'; + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '0' || *s == '1') { + it[0] = *s++; + it[1] = '\0'; + return s; + } + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int r=0, g=0, b=0; + if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex + return NSVG_RGB(r, g, b); + if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa + return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), .. + return NSVG_RGB(128, 128, 128); +} + +// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters). +// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors +// for backwards compatibility. Note: other image viewers return black instead. + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int i; + unsigned int rgbi[3]; + float rgbf[3]; + // try decimal integers first + if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) { + // integers failed, try percent values (float, locale independent) + const char delimiter[3] = {',', ',', ')'}; + str += 4; // skip "rgb(" + for (i = 0; i < 3; i++) { + while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces + if (*str == '+') str++; // skip '+' (don't allow '-') + if (!*str) break; + rgbf[i] = nsvg__atof(str); + + // Note 1: it would be great if nsvg__atof() returned how many + // bytes it consumed but it doesn't. We need to skip the number, + // the '%' character, spaces, and the delimiter ',' or ')'. + + // Note 2: The following code does not allow values like "33.%", + // i.e. a decimal point w/o fractional part, but this is consistent + // with other image viewers, e.g. firefox, chrome, eog, gimp. + + while (*str && nsvg__isdigit(*str)) str++; // skip integer part + if (*str == '.') { + str++; + if (!nsvg__isdigit(*str)) break; // error: no digit after '.' + while (*str && nsvg__isdigit(*str)) str++; // skip fractional part + } + if (*str == '%') str++; else break; + while (*str && nsvg__isspace(*str)) str++; + if (*str == delimiter[i]) str++; + else break; + } + if (i == 3) { + rgbi[0] = roundf(rgbf[0] * 2.55f); + rgbi[1] = roundf(rgbf[1] * 2.55f); + rgbi[2] = roundf(rgbf[2] * 2.55f); + } else { + rgbi[0] = rgbi[1] = rgbi[2] = 128; + } + } + // clip values as the CSS spec requires + for (i = 0; i < 3; i++) { + if (rgbi[i] > 255) rgbi[i] = 255; + } + return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]); +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static int nsvg__isCoordinate(const char* s) +{ + // optional sign + if (*s == '-' || *s == '+') + s++; + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + int len; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + len = nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + len = nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + len = nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + len = nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + len = nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + len = nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + if (len != 0) { + str += len; + } else { + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str && *str == '#') + str++; + while (i < 63 && *str && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static unsigned char nsvg__parsePaintOrder(const char* str) +{ + if (strcmp(str, "normal") == 0 || strcmp(str, "fill stroke markers") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_FILL, NSVG_PAINT_STROKE, NSVG_PAINT_MARKERS); + else if (strcmp(str, "fill markers stroke") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_FILL, NSVG_PAINT_MARKERS, NSVG_PAINT_STROKE); + else if (strcmp(str, "markers fill stroke") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_MARKERS, NSVG_PAINT_FILL, NSVG_PAINT_STROKE); + else if (strcmp(str, "markers stroke fill") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_MARKERS, NSVG_PAINT_STROKE, NSVG_PAINT_FILL); + else if (strcmp(str, "stroke fill markers") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_STROKE, NSVG_PAINT_FILL, NSVG_PAINT_MARKERS); + else if (strcmp(str, "stroke markers fill") == 0) + return nsvg__encodePaintOrder(NSVG_PAINT_STROKE, NSVG_PAINT_MARKERS, NSVG_PAINT_FILL); + // TODO: handle inherit. + return nsvg__encodePaintOrder(NSVG_PAINT_FILL, NSVG_PAINT_STROKE, NSVG_PAINT_MARKERS); +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "paint-order") == 0) { + attr->paintOrder = nsvg__parsePaintOrder(value); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + case 'z': + case 'Z': + return 0; + } + return -1; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) + if ((hda < 1e-3f) && (hda > -1e-3f)) + hda *= 0.5f; + else + hda = (1.0f - cosf(hda)) / sinf(hda); + kappa = fabsf(4.0f / 3.0f * hda); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + char initPoint; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + initPoint = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + item[0] = '\0'; + if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4)) + s = nsvg__getNextPathItemWhenArcFlag(s, item); + if (!*item) + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (cmd != '\0' && nsvg__isCoordinate(item)) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + initPoint = 1; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (initPoint == 0) { + // Do not allow other commands until initial point has been set (moveTo called once). + cmd = '\0'; + } + if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + rargs = nsvg__getArgsPerElement(cmd); + if (rargs == -1) { + // Command not recognized + cmd = '\0'; + rargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, signed char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +static void nsvg__createGradients(NSVGparser* p) +{ + NSVGshape* shape; + + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + if (shape->fill.type == NSVG_PAINT_UNDEF) { + if (shape->fillGradient[0] != '\0') { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, shape->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, shape->fillGradient, localBounds, shape->xform, &shape->fill.type); + } + if (shape->fill.type == NSVG_PAINT_UNDEF) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + if (shape->stroke.type == NSVG_PAINT_UNDEF) { + if (shape->strokeGradient[0] != '\0') { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, shape->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, shape->strokeGradient, localBounds, shape->xform, &shape->stroke.type); + } + if (shape->stroke.type == NSVG_PAINT_UNDEF) { + shape->stroke.type = NSVG_PAINT_NONE; + } + } + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Create gradients after all definitions have been parsed + nsvg__createGradients(p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif // NANOSVG_IMPLEMENTATION + +#endif // NANOSVG_H diff --git a/vendor/nanosvg/nanosvgrast.h b/vendor/nanosvg/nanosvgrast.h new file mode 100644 index 0000000..ba512c7 --- /dev/null +++ b/vendor/nanosvg/nanosvgrast.h @@ -0,0 +1,1472 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#include "nanosvg.h" + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(void); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include +#include +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + signed char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer(void) +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } +static float nsvg__roundf(float x) { return (x >= 0) ? floorf(x + 0.5) : ceilf(x - 0.5); } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf((x2 - x4) * dy - (y2 - y4) * dx); + d3 = nsvg__absf((x3 - x4) * dy - (y3 - y4) * dx); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-nsvg__roundf(NSVG__FIX * -dxdy)); + else + z->dx = (int)nsvg__roundf(NSVG__FIX * dxdy); + z->x = (int)nsvg__roundf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { + if (isnan(a)) + return mn; + return a < mn ? mn : (a > mx ? mx : a); +} + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } else if (grad->nstops == 1) { + unsigned int color = nsvg__applyOpacity(grad->stops[0].color, opacity); + for (i = 0; i < 256; i++) + cache->colors[i] = color; + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + int j; + unsigned char paintOrder; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + for (j = 0; j < 3; j++) { + paintOrder = (shape->paintOrder >> (2 * j)) & 0x03; + + if (paintOrder == NSVG_PAINT_FILL && shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (paintOrder == NSVG_PAINT_STROKE && shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + + // dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif // NANOSVGRAST_IMPLEMENTATION + +#endif // NANOSVGRAST_H