From 8a11b3ce0b5610845a37487acbb5c5713598a9f7 Mon Sep 17 00:00:00 2001 From: Lpsd <40902730+Lpsd@users.noreply.github.com> Date: Fri, 24 May 2024 10:35:23 +0100 Subject: [PATCH 1/7] mbedTLS fix for cURL 8.8.0 https://github.com/curl/curl/issues/13748 --- vendor/curl/lib/config-linux.h | 2 +- vendor/curl/lib/config-macos.h | 2 +- vendor/curl/lib/vtls/mbedtls.c | 21 +++++++++++++-------- vendor/curl/premake5.lua | 3 +++ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/vendor/curl/lib/config-linux.h b/vendor/curl/lib/config-linux.h index 02a1306ed00..b0084d92dd5 100644 --- a/vendor/curl/lib/config-linux.h +++ b/vendor/curl/lib/config-linux.h @@ -869,7 +869,7 @@ /* #undef USE_MANUAL */ /* if mbedTLS is enabled */ -/* #undef USE_MBEDTLS */ +#define USE_MBEDTLS 1 /* if msh3 is in use */ /* #undef USE_MSH3 */ diff --git a/vendor/curl/lib/config-macos.h b/vendor/curl/lib/config-macos.h index bf5733fccac..c46b5ec014d 100644 --- a/vendor/curl/lib/config-macos.h +++ b/vendor/curl/lib/config-macos.h @@ -869,7 +869,7 @@ /* #undef USE_MANUAL */ /* if mbedTLS is enabled */ -/* #undef USE_MBEDTLS */ +#define USE_MBEDTLS 1 /* if msh3 is in use */ /* #undef USE_MSH3 */ diff --git a/vendor/curl/lib/vtls/mbedtls.c b/vendor/curl/lib/vtls/mbedtls.c index ec0b10dd9a9..1197d2956de 100644 --- a/vendor/curl/lib/vtls/mbedtls.c +++ b/vendor/curl/lib/vtls/mbedtls.c @@ -902,8 +902,6 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const mbedtls_x509_crt *peercert; - char cipher_str[64]; - uint16_t cipher_id; #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: @@ -932,11 +930,18 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } - cipher_id = (uint16_t) - mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); - mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); - infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str); - +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + { + char cipher_str[64]; + uint16_t cipher_id; + cipher_id = (uint16_t) + mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); + mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); + infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str); + } +#else + infof(data, "mbedTLS: Handshake complete"); +#endif ret = mbedtls_ssl_get_verify_result(&backend->ssl); if(!conn_config->verifyhost) @@ -1506,4 +1511,4 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbed_send, /* send data to encrypt */ }; -#endif /* USE_MBEDTLS */ +#endif /* USE_MBEDTLS */ \ No newline at end of file diff --git a/vendor/curl/premake5.lua b/vendor/curl/premake5.lua index d86088f267c..b3ac6a517f7 100644 --- a/vendor/curl/premake5.lua +++ b/vendor/curl/premake5.lua @@ -32,6 +32,9 @@ project "curl" defines { "USE_SCHANNEL", "USE_WINDOWS_SSPI", "USE_WIN32_IDN" } links { "crypt32", "Normaliz" } + filter { "system:not windows" } + defines { "USE_MBEDTLS" } + filter { "system:linux or bsd or macosx" } defines { "CURL_HIDDEN_SYMBOLS" } From 41f7bfaefe7b2c379603bef255c09a7b62263756 Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:26:37 +0000 Subject: [PATCH 2/7] Update lunasvg vendor to 3.5.0 --- .../lunasvg/3rdparty/plutovg/plutovg-font.c | 414 ------- vendor/lunasvg/LICENSE | 2 +- vendor/lunasvg/README.md | 90 +- vendor/lunasvg/include/lunasvg.h | 35 +- vendor/lunasvg/{3rdparty => }/plutovg/LICENSE | 0 vendor/lunasvg/plutovg/README.md | 99 ++ .../plutovg => plutovg/include}/plutovg.h | 267 ++++- vendor/lunasvg/plutovg/smiley.png | Bin 0 -> 6392 bytes .../plutovg => plutovg/source}/FTL.TXT | 0 .../source}/plutovg-blend.c | 0 .../source}/plutovg-canvas.c | 103 +- vendor/lunasvg/plutovg/source/plutovg-font.c | 1060 +++++++++++++++++ .../source}/plutovg-ft-math.c | 0 .../source}/plutovg-ft-math.h | 0 .../source}/plutovg-ft-raster.c | 264 +++- .../source}/plutovg-ft-raster.h | 0 .../source}/plutovg-ft-stroker.c | 0 .../source}/plutovg-ft-stroker.h | 0 .../source}/plutovg-ft-types.h | 0 .../source}/plutovg-matrix.c | 4 +- .../source}/plutovg-paint.c | 18 +- .../plutovg => plutovg/source}/plutovg-path.c | 20 +- .../source}/plutovg-private.h | 43 +- .../source}/plutovg-rasterize.c | 19 +- .../source}/plutovg-stb-image-write.h | 0 .../source}/plutovg-stb-image.h | 0 .../source}/plutovg-stb-truetype.h | 0 .../source}/plutovg-surface.c | 19 +- .../source}/plutovg-utils.h | 2 +- vendor/lunasvg/premake5.lua | 8 +- vendor/lunasvg/source/graphics.cpp | 153 +-- vendor/lunasvg/source/graphics.h | 24 +- vendor/lunasvg/source/lunasvg.cpp | 36 +- vendor/lunasvg/source/svgelement.cpp | 56 +- vendor/lunasvg/source/svgelement.h | 8 +- vendor/lunasvg/source/svglayoutstate.cpp | 82 +- vendor/lunasvg/source/svglayoutstate.h | 12 + vendor/lunasvg/source/svgpaintelement.cpp | 10 +- vendor/lunasvg/source/svgproperty.cpp | 22 +- vendor/lunasvg/source/svgproperty.h | 48 +- vendor/lunasvg/source/svgtextelement.cpp | 184 ++- vendor/lunasvg/source/svgtextelement.h | 21 +- 42 files changed, 2387 insertions(+), 736 deletions(-) delete mode 100644 vendor/lunasvg/3rdparty/plutovg/plutovg-font.c rename vendor/lunasvg/{3rdparty => }/plutovg/LICENSE (100%) create mode 100644 vendor/lunasvg/plutovg/README.md rename vendor/lunasvg/{3rdparty/plutovg => plutovg/include}/plutovg.h (88%) create mode 100644 vendor/lunasvg/plutovg/smiley.png rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/FTL.TXT (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-blend.c (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-canvas.c (86%) create mode 100644 vendor/lunasvg/plutovg/source/plutovg-font.c rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-math.c (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-math.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-raster.c (87%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-raster.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-stroker.c (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-stroker.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-ft-types.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-matrix.c (98%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-paint.c (98%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-path.c (98%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-private.h (70%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-rasterize.c (96%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-stb-image-write.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-stb-image.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-stb-truetype.h (100%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-surface.c (96%) rename vendor/lunasvg/{3rdparty/plutovg => plutovg/source}/plutovg-utils.h (98%) diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-font.c b/vendor/lunasvg/3rdparty/plutovg/plutovg-font.c deleted file mode 100644 index 2e67033cb4b..00000000000 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-font.c +++ /dev/null @@ -1,414 +0,0 @@ -#include "plutovg.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, int length, plutovg_text_encoding_t encoding) -{ - if(length != -1) - return length; - length = 0; - switch(encoding) { - case PLUTOVG_TEXT_ENCODING_UTF8: - case PLUTOVG_TEXT_ENCODING_LATIN1: { - 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) -{ - it->text = text; - it->length = plutovg_text_iterator_length(text, length, encoding); - 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_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; - int trailing_bytes = trailing[text[it->index]]; - if(it->index + trailing_bytes >= it->length) - trailing_bytes = 0; - switch(trailing_bytes) { - case 5: codepoint += text[it->index++]; codepoint <<= 6; - case 4: codepoint += text[it->index++]; codepoint <<= 6; - case 3: codepoint += text[it->index++]; codepoint <<= 6; - case 2: codepoint += text[it->index++]; codepoint <<= 6; - case 1: codepoint += text[it->index++]; codepoint <<= 6; - case 0: codepoint += text[it->index++]; - } - - codepoint -= offsets[trailing_bytes]; - 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; - } case PLUTOVG_TEXT_ENCODING_LATIN1: { - const uint8_t* text = it->text; - codepoint = text[it->index++]; - break; - } default: - assert(false); - } - - return codepoint; -} - -typedef struct { - stbtt_vertex* vertices; - int nvertices; - int index; - int advance_width; - int left_side_bearing; - int x1; - int y1; - int x2; - int y2; -} glyph_t; - -#define GLYPH_CACHE_SIZE 256 -struct plutovg_font_face { - int ref_count; - int ascent; - int descent; - int line_gap; - int x1; - int y1; - int x2; - int y2; - stbtt_fontinfo info; - glyph_t** glyphs[GLYPH_CACHE_SIZE]; - plutovg_destroy_func_t destroy_func; - void* closure; -}; - -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)); - face->ref_count = 1; - 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); - memset(face->glyphs, 0, sizeof(face->glyphs)); - face->destroy_func = destroy_func; - face->closure = closure; - return face; -} - -plutovg_font_face_t* plutovg_font_face_reference(plutovg_font_face_t* face) -{ - if(face == NULL) - return NULL; - ++face->ref_count; - return face; -} - -void plutovg_font_face_destroy(plutovg_font_face_t* face) -{ - if(face == NULL) - return; - if(--face->ref_count == 0) { - for(int i = 0; i < GLYPH_CACHE_SIZE; i++) { - if(face->glyphs[i] == NULL) - continue; - for(int j = 0; j < GLYPH_CACHE_SIZE; j++) { - glyph_t* glyph = face->glyphs[i][j]; - if(glyph == NULL) - continue; - stbtt_FreeShape(&face->info, glyph->vertices); - free(glyph); - } - - free(face->glyphs[i]); - } - - if(face->destroy_func) - face->destroy_func(face->closure); - free(face); - } -} - -int plutovg_font_face_get_reference_count(const plutovg_font_face_t* face) -{ - if(face) - return face->ref_count; - return 0; -} - -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 glyph_t* plutovg_font_face_get_glyph(plutovg_font_face_t* face, plutovg_codepoint_t codepoint) -{ - unsigned int msb = (codepoint >> 8) & 0xFF; - if(face->glyphs[msb] == NULL) { - face->glyphs[msb] = calloc(GLYPH_CACHE_SIZE, sizeof(glyph_t*)); - } - - unsigned int lsb = codepoint & 0xFF; - if(face->glyphs[msb][lsb] == NULL) { - glyph_t* glyph = malloc(sizeof(glyph_t)); - 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; - face->glyphs[msb][lsb] = glyph; - } - - return face->glyphs[msb][lsb]; -} - -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); - 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}; - 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; -} diff --git a/vendor/lunasvg/LICENSE b/vendor/lunasvg/LICENSE index 0c17a4b39bb..62a964e73f6 100644 --- a/vendor/lunasvg/LICENSE +++ b/vendor/lunasvg/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2024 Samuel Ugochukwu +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 diff --git a/vendor/lunasvg/README.md b/vendor/lunasvg/README.md index 0f941b4b059..ccfc96aef80 100644 --- a/vendor/lunasvg/README.md +++ b/vendor/lunasvg/README.md @@ -1,6 +1,9 @@ -[![Releases](https://img.shields.io/badge/Version-3.2.0-orange.svg)](https://github.com/sammycage/lunasvg/releases) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sammycage/lunasvg/blob/master/LICENSE) -[![Build Status](https://github.com/sammycage/lunasvg/actions/workflows/main.yml/badge.svg)](https://github.com/sammycage/lunasvg/actions) +[![Actions](https://github.com/sammycage/lunasvg/actions/workflows/main.yml/badge.svg)](https://github.com/sammycage/lunasvg/actions) +[![License](https://img.shields.io/github/license/sammycage/lunasvg)](https://github.com/sammycage/lunasvg/blob/master/LICENSE) +[![Releases](https://img.shields.io/github/v/release/sammycage/lunasvg)](https://github.com/sammycage/lunasvg/releases) +[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.me/sammycage) + +> Interested in HTML rendering? Explore a related library, [PlutoBook](https://github.com/plutoprint/plutobook), built for paged HTML rendering. # LunaSVG @@ -95,6 +98,69 @@ int main() | --- | --- | | ![summer.png](https://github.com/user-attachments/assets/c7f16780-23f8-4acd-906a-2242f2d0d33b) | ![winter.png](https://github.com/user-attachments/assets/fdd65288-11c7-4e16-bb5a-2bf28de57145) | +--- + +## Hit Testing + +This example demonstrates SVG element hit testing using `elementFromPoint(x, y)` in LunaSVG. It loads an SVG containing three shapes, performs point-based hit detection, and applies a skew transform with a black stroke to each matched element. The results are saved as `original.png` and `modified.png` for visual comparison. + +```cpp +#include + +#include +#include + +using namespace lunasvg; + +static const char kSVGContent[] = R"SVG( + + + + + +)SVG"; + +int main() +{ + auto document = Document::loadFromData(kSVGContent); + + document->renderToBitmap().writeToPng("original.png"); + + const std::pair points[] = { + {30, 30}, // inside red-rect + {200, 70}, // center of blue-circle + {310, 50}, // inside green-rect + {0, 0}, // outside all shapes + }; + + for(const auto& [x, y] : points) { + if(auto element = document->elementFromPoint(x, y)) { + std::cout << "Element at (" << x << ", " << y << "): " << element.getAttribute("id") << "\n"; + + element.setAttribute("stroke", "black"); + element.setAttribute("stroke-width", "3"); + element.setAttribute("transform", "skewX(9)"); + } else { + std::cout << "No element found at (" << x << ", " << y << ")\n"; + } + } + + document->renderToBitmap().writeToPng("modified.png"); + return 0; +} +``` + +| `original.png` | `modified.png` | +| --- | --- | +| ![original.png](https://github.com/user-attachments/assets/bbffbd84-6311-484b-bfe3-219d7aec055b) | ![modified.png](https://github.com/user-attachments/assets/a7f6e502-a64f-48d5-8a01-901ad15b108b) | + +```log +Element at (30, 30): red-rect +Element at (200, 70): blue-circle +Element at (310, 50): green-rect +No element found at (0, 0) +``` + ## Features LunaSVG supports nearly all graphical features outlined in the SVG 1.1 and SVG 1.2 Tiny specifications. The primary exceptions are animation, filters, and scripts. As LunaSVG is designed for static rendering, animation is unlikely to be supported in the future. However, support for filters may be added. It currently handles a wide variety of elements, including: @@ -108,7 +174,7 @@ Follow the steps below to install LunaSVG using either [CMake](https://cmake.org ### Using CMake ```bash -git clone --recursive https://github.com/sammycage/lunasvg.git +git clone https://github.com/sammycage/lunasvg.git cd lunasvg cmake -B build . cmake --build build @@ -141,6 +207,17 @@ target_link_libraries(your_target_name PRIVATE lunasvg::lunasvg) Replace `your_target_name` with the name of your executable or library target. +Build Options +LunaSVG provides several build options that can be configured using -D flags when running cmake. Below is a list of available options: + +USE_SYSTEM_PLUTOVG (default: OFF): Use the system-installed plutovg library (version 1.0.0 or higher) instead of the bundled submodule. If the system library is not found, the build will fall back to using the submodule. +Example: + +```bash +cmake -B build -DUSE_SYSTEM_PLUTOVG=ON . +cmake --build build +``` + ### Using Meson ```bash @@ -207,3 +284,8 @@ $ svg2png input.svg 512x512 0xff00ffff - [eScada Solutions](https://www.escadasolutions.com) - [CARLA Simulator](https://carla.org/) - [AUI Framework](https://github.com/aui-framework/aui) +- [Software Companions](http://www.softwarecompanions.com) + +## License + +LunaSVG is licensed under the MIT License, see [LICENSE](https://github.com/sammycage/lunasvg/blob/master/LICENSE) for more information. diff --git a/vendor/lunasvg/include/lunasvg.h b/vendor/lunasvg/include/lunasvg.h index bee1366d771..d869eeeee9d 100644 --- a/vendor/lunasvg/include/lunasvg.h +++ b/vendor/lunasvg/include/lunasvg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024 Samuel Ugochukwu + * 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 @@ -28,7 +28,10 @@ #include #include -#if !defined(LUNASVG_BUILD_STATIC) && (defined(_WIN32) || defined(__CYGWIN__)) +#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) @@ -46,7 +49,7 @@ #endif #define LUNASVG_VERSION_MAJOR 3 -#define LUNASVG_VERSION_MINOR 2 +#define LUNASVG_VERSION_MINOR 5 #define LUNASVG_VERSION_MICRO 0 #define LUNASVG_VERSION_ENCODE(major, minor, micro) (((major) * 10000) + ((minor) * 100) + ((micro) * 1)) @@ -514,6 +517,12 @@ class LUNASVG_API Node { */ 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. @@ -644,7 +653,7 @@ class LUNASVG_API Element : public Node { private: Element(SVGElement* element); - SVGElement* element(bool layout = false) const; + SVGElement* element(bool layoutIfNeeded = false) const; friend class Node; friend class Document; }; @@ -741,6 +750,14 @@ class LUNASVG_API 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. @@ -754,11 +771,6 @@ class LUNASVG_API Document { */ Element documentElement() const; - /** - * @internal - */ - SVGRootElement* rootElement() const { return m_rootElement.get(); } - Document(Document&&); Document& operator=(Document&&); ~Document(); @@ -767,10 +779,13 @@ class LUNASVG_API Document { 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 +} // namespace lunasvg #endif // LUNASVG_H diff --git a/vendor/lunasvg/3rdparty/plutovg/LICENSE b/vendor/lunasvg/plutovg/LICENSE similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/LICENSE rename to vendor/lunasvg/plutovg/LICENSE diff --git a/vendor/lunasvg/plutovg/README.md b/vendor/lunasvg/plutovg/README.md new file mode 100644 index 00000000000..23b7faf0598 --- /dev/null +++ b/vendor/lunasvg/plutovg/README.md @@ -0,0 +1,99 @@ +[![Actions](https://github.com/sammycage/plutovg/actions/workflows/main.yml/badge.svg)](https://github.com/sammycage/plutovg/actions) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sammycage/plutovg/blob/main/LICENSE) +[![Releases](https://img.shields.io/github/v/release/sammycage/plutovg)](https://github.com/sammycage/plutovg/releases) +[![CodeFactor](https://www.codefactor.io/repository/github/sammycage/plutovg/badge)](https://www.codefactor.io/repository/github/sammycage/plutovg) + +# PlutoVG +PlutoVG is a standalone 2D vector graphics library in C. + +## Features +- Path Filling, Stroking and Dashing +- Solid, Gradient and Texture Paints +- Fonts and Texts +- Clipping and Compositing +- Transformations +- Images + +## Example +```c +#include + +int main(void) +{ + const int width = 150; + const int height = 150; + + const float center_x = width / 2.f; + const float center_y = height / 2.f; + const float face_radius = 70; + const float mouth_radius = 50; + const float eye_radius = 10; + const float eye_offset_x = 25; + const float eye_offset_y = 20; + const float eye_x = center_x - eye_offset_x; + const float eye_y = center_y - eye_offset_y; + + plutovg_surface_t* surface = plutovg_surface_create(width, height); + plutovg_canvas_t* canvas = plutovg_canvas_create(surface); + + plutovg_canvas_save(canvas); + plutovg_canvas_arc(canvas, center_x, center_y, face_radius, 0, PLUTOVG_TWO_PI, 0); + plutovg_canvas_set_rgb(canvas, 1, 1, 0); + plutovg_canvas_fill_preserve(canvas); + plutovg_canvas_set_rgb(canvas, 0, 0, 0); + plutovg_canvas_set_line_width(canvas, 5); + plutovg_canvas_stroke(canvas); + plutovg_canvas_restore(canvas); + + plutovg_canvas_save(canvas); + plutovg_canvas_arc(canvas, eye_x, eye_y, eye_radius, 0, PLUTOVG_TWO_PI, 0); + plutovg_canvas_arc(canvas, center_x + eye_offset_x, eye_y, eye_radius, 0, PLUTOVG_TWO_PI, 0); + plutovg_canvas_set_rgb(canvas, 0, 0, 0); + plutovg_canvas_fill(canvas); + plutovg_canvas_restore(canvas); + + plutovg_canvas_save(canvas); + plutovg_canvas_arc(canvas, center_x, center_y, mouth_radius, 0, PLUTOVG_PI, 0); + plutovg_canvas_set_rgb(canvas, 0, 0, 0); + plutovg_canvas_set_line_width(canvas, 5); + plutovg_canvas_stroke(canvas); + plutovg_canvas_restore(canvas); + + plutovg_surface_write_to_png(surface, "smiley.png"); + plutovg_canvas_destroy(canvas); + plutovg_surface_destroy(surface); + return 0; +} +``` + +output: + +![smiley.png](smiley.png) + +## Installation + +Follow the steps below to install PlutoVG using either [Meson](https://mesonbuild.com/) or [CMake](https://cmake.org/). + +### Using Meson + +```bash +git clone https://github.com/sammycage/plutovg.git +cd plutovg +meson setup build +meson compile -C build +meson install -C build +``` + +### Using CMake + +```bash +git clone https://github.com/sammycage/plutovg.git +cd plutovg +cmake -B build . +cmake --build build +cmake --install build +``` + +## Projects using PlutoVG +- [LunaSVG](https://github.com/sammycage/lunasvg) +- [PlutoSVG](https://github.com/sammycage/plutosvg) diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg.h b/vendor/lunasvg/plutovg/include/plutovg.h similarity index 88% rename from vendor/lunasvg/3rdparty/plutovg/plutovg.h rename to vendor/lunasvg/plutovg/include/plutovg.h index 07f6ff6773c..4d0bf20d0d4 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg.h +++ b/vendor/lunasvg/plutovg/include/plutovg.h @@ -29,7 +29,10 @@ extern "C" { #endif -#if !defined(PLUTOVG_BUILD_STATIC) && (defined(_WIN32) || defined(__CYGWIN__)) +#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) @@ -46,9 +49,9 @@ extern "C" { #define PLUTOVG_API PLUTOVG_IMPORT #endif -#define PLUTOVG_VERSION_MAJOR 0 -#define PLUTOVG_VERSION_MINOR 0 -#define PLUTOVG_VERSION_MICRO 13 +#define PLUTOVG_VERSION_MAJOR 1 +#define PLUTOVG_VERSION_MINOR 3 +#define PLUTOVG_VERSION_MICRO 1 #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) @@ -101,6 +104,7 @@ typedef struct plutovg_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. @@ -113,6 +117,7 @@ typedef struct plutovg_rect { } 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. @@ -127,7 +132,6 @@ typedef struct plutovg_matrix { } 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) @@ -729,10 +733,10 @@ PLUTOVG_API bool plutovg_path_parse(plutovg_path_t* path, const char* data, int * @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_LATIN1 ///< Latin-1 encoding + PLUTOVG_TEXT_ENCODING_UTF32 ///< UTF-32 encoding } plutovg_text_encoding_t; /** @@ -889,6 +893,113 @@ PLUTOVG_API float plutovg_font_face_traverse_glyph_path(plutovg_font_face_t* fac */ 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. */ @@ -1129,9 +1240,10 @@ PLUTOVG_API int plutovg_surface_get_height(const plutovg_surface_t* surface); PLUTOVG_API int plutovg_surface_get_stride(const plutovg_surface_t* surface); /** - * @brief plutovg_surface_clear - * @param surface - * @param color + * @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); @@ -1539,6 +1651,57 @@ PLUTOVG_API void plutovg_canvas_set_paint(plutovg_canvas_t* canvas, plutovg_pain */ 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. * @@ -2064,28 +2227,94 @@ PLUTOVG_API void plutovg_canvas_get_current_point(const plutovg_canvas_t* canvas PLUTOVG_API plutovg_path_t* plutovg_canvas_get_path(const plutovg_canvas_t* canvas); /** - * @brief Gets the bounding box of the filled region. + * @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 extents The bounding box of the filled region. + * @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 void plutovg_canvas_fill_extents(const plutovg_canvas_t* canvas, plutovg_rect_t* extents); +PLUTOVG_API bool plutovg_canvas_clip_contains(plutovg_canvas_t* canvas, float x, float y); /** - * @brief Gets the bounding box of the stroked region. + * @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 The bounding box of the stroked region. + * @param extents A pointer to a `plutovg_rect_t` structure that receives the bounding box. */ -PLUTOVG_API void plutovg_canvas_stroke_extents(const plutovg_canvas_t* canvas, plutovg_rect_t* extents); +PLUTOVG_API void plutovg_canvas_fill_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents); /** - * @brief Gets the bounding box of the clipped region. + * @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 The bounding box of the clipped region. + * @param extents A pointer to a `plutovg_rect_t` structure that receives the bounding box. */ -PLUTOVG_API void plutovg_canvas_clip_extents(const plutovg_canvas_t* canvas, plutovg_rect_t* extents); +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. diff --git a/vendor/lunasvg/plutovg/smiley.png b/vendor/lunasvg/plutovg/smiley.png new file mode 100644 index 0000000000000000000000000000000000000000..85e1d91d0bbb7b130a1dea00a72496a59c3b16ca GIT binary patch literal 6392 zcmX9@dpy%$`2Xx;GdAQpbI<)gj9jz1hGMxcNgI{9#FUC|+l--H;;Wm`$5$dntEA~> z8AW|*ipW=mq*Z95(${6b{eHhc&U4P|oagnN=RD6j=Q;256p=#%F$y>Z001ySTl~Uh z^|}8o@(3CAgk(+u0Jby8j})05pNN&KH^SqfGP1{ql}TuwFai`5Ee*ru3v<{Pkim-^ z2Mv2Jv*BnaDWMZ~`!ZyK$=xo%tEy>k!oMG8Q((c@eUqIH`~7KE%L+qGox+TSC%Xf} zkLsXMijh;JP~%}#p&ok_D7+}v?US;8?E&}G%>icIuT;2;jjcSh zl|@h#PC%Kg0XNLHJrC+oa|S{SY^4#Xm=&e0A{=uA+Xu)`!dUb3@PwAhigxbNB2!3l zA>FW4F5yJ5u|MSkn-p8cW@}M$en^P)J-?V#$2I5;0dd)h_eUv_BQ_UYS(w2@RrI)l zmZw?S+A5v;&8YoNW;W!X(Q@c4Z^M0NrV?%T=4hnLh_kacmPkNfhg_Nm^ZbMS9>abq zWv!!{8RW!p4KnSI0 zt+m%XkM>9muzguQh90EDjg3yLF;ROB8;`z+F5US=AN%{HJRer#>6ex}H9=iAz&)2R z2RCx10;^N=s3LNgTnB2peS*rB&d!>c6391S+-l>{G zsj>kUu`8kL>?mvav-Hbr?e(3p6_o-dn6R5lV3+Gdp+wu)>}C5pzlNq>N$0hqN$J{( zuerly@Sb}aPOCSsCobDtheU3K-F;#P6&7R=*c-H<3&g$lC{qhtTdp+Z9DY^0m?Sp0 zSjzkLO-m@vS;Ve$#<+f!Z(g!f{ti&nnpY7af;XfEYU?{Aq`j_#RoP1Yz@lAntm0#_ z%WO6ew>wu=eb2IYB0|U{&o}(WKcU`Hj+_gt?v-3FE2BbpDxf23%c}%Lug4Ggsv1q_ z&!55LKZbI=Al}RYiMM+dbqdjF~dN-+Y z8oQ-Rl_3VGBE`1nRLJA-X8r|Pe+=Q(b+3dq>7uz%TCJPgphXvRllf+N(Z>6>GC^ll zgwk!70zxbgI1N=fqosfTEST|}1-vAKbeWv--`G|^IozR>%M8Ny?Xkfh9=(tl5Og_> zDR5gyT^CrhBe5*L=hNE?Rj$lXWoA|>QEOnc!zQhazC-$0Emhw~)6f=UW}fYK2Pdc1 z%N|Dvq`_uex4jBNrkPnaYE~Z~TFFhBf}V|}sA-tXG%V2xRMTkju(%%Wu$q)8yvQf| zdQJJ-W}+sW*WvJh0#)(bkMHgjOnNgkY+9YEU`l3hTLyqt4_2h`{xM;V#bx7`IO%)ywDH^5e8MO;Hud#i zIGHI5ZMR_0$Z-1Gl6k6S2k}MW_eATKnU@B(*AR(a&<+$~Am4U?l`z7-a13wKdT3Oa z675pegh>56x5L^)Cn+p*l4yP-F0JVK*wL)2eLQokGb<-osmmGkLZ?hqas}VwEeaFs zhj1Uw#k_@p@4B^EpPPGo@x{4DD$pbIcsyX;;HCui}W=QyViuq|5IBs{0L;vTQ2 zc0%2>iQR!_ACada-RrLW?R`fg;H($>u}iEH9TCZlKF?H`=Cq$J z&m!x(MbSJ&8!P?fnxX7}y93pRRR$siPA1r^)SJsJq=y4RG?qF;RK&MGGB+)WX8Yb% z@H?_ejMuPElqHjw%#sxN@QtSFe zAaFezyUMd_VSBV{vtW(rm{w}GVwSiD=}>Fh-hDeTEYeVT>5@!m#tkXSYvpOcjmhBt{AA8~xwHqUVW7JV5AQL7!5BN_g*SI|yq_ZPcoX|CJrBM?6Dz9^ z*9C|ZZp4e;=<^q^5k11x)#Wd1HC*YIFz3dec%U8CS0V220(NMpQtayqv>+%3&y|fzQk(&lmSwM3AI(S8XHJ5rZq&^yi+o^VVK?eN z`viA&QS>Ljy}&C3WXmuApli7-2Vu1zjbu)2foTfx zpZt_#(xQy$rm*LqD!>q9Q-qj7Qd9eCDwk5_-+Z0R@n&&8jt0mPJ?^Fz22o1Ir*!qS z5okD^{4#~?kqLS_#;vmJhc-5EhVA-T_DT(=_tWzX>@=8cLd^x6j>vyKi&+A`FeFHN zEjA>s+N6?3n!C>I#gXdg1R-t6rGy}nm0YlzCPOz_VE;^4X+8TF`r8>8y$)*rxO?v@ zeKu(Aj1VfR%eW4x#pL{%*sqfLZdftMo*yjipC8jaoZ51h3@ofg`pPY&voL3k`kIIw{BKhHyLW$0EQXn z9?ko&_g1EEcEQ7ZIoYEK1Id;LZmuTs?g-^oCi?Wy5@(fOlwvnxj`_*w4fNsT%aH7^ z3v^y77x09~KT4pgJbUo`>L z0{+ryRhjtIV0#2lr>1r9ehzS7) zY8I9Ge1!`jcHO5!-J*z%S8LDvsr|0Pes{03T-qVL=}gJhEzf->7L&HFRnFe;+NUq# zhWVM==vEc|Flx{9FUqM$o&FDXx+31Yl)vgJ(~wtr%GL-?>B8CG;)Z?IiHtHb-M>*} zOBE0PSYDv#yO50};tf~(N2dxYzqdzfgoh_wqq$AY%-a{@4>vFSb3J}@b?c*M~T)kd`mv4 z6*$IfG@1^gixzZtm$x8of56h$HhSpavZX!BIY`(c@__U5-AqFUc0b-j1!_I><}o6& zFyw;afiCFWLwcO~zs@w=$o8?{_+DZ|46=Bo8 zv*-lc;QvW$=NrA*1*_$y-HQ8iP%=S>)z+dEhZxV!@zVD99g%ZGeM8(oL`fev8S@8s z#^j4MJ(NMWmoNR1wa$owdfPeNK5O7 z?fVB>*W*R|h7FcgHMgW2ILq#$o!1KvE1|^CZ}*%Ie67*q3+%*lN)3VdhkotDt_s@4 z1kPMj@0z%M{zKRf6{?59Nts<-cCa1oC1(ie5DTyCmq+vR`;UO^lcN1hi@;&P+@WtX zADylKlR4oSU0k2RdN&~ze){$M$&4};Dv-|*xy^x#V||Cd)p$^pcfq3Ax?&V+XOUb= zQq9$UhveO{Bf}a_m?MWvqYK&;AZag3y;%3$A`Uosmj2R^jAFS4-6l3?aK-KiUz8ikGKixTjYiGL(7-%(^zxeUcnhXa-p75WV}!5oQZdZQU^4uylhbI$q5K?Cyy}=c^rguy>i!(UopW zd^53k`vW8!c35_b6i|x%!XKR_n0-*dPP`KKrG+05Kv9b}c0+~e$im_>SPL=r0+V0! zb~p3W-qO!*Nq(uivjjMGr{kcd;!bi&uB|Q5Q`xuwZjJvY-s4)w!1e!RNGUsCAz&9K zZvTHw!`2oZJE0!<%U%L{7Bl3OLZ!%I^~&WtH_)jxMrB1S5puMv;;%$-v~Kh%h4^bv zQrWrKX)}O&Q^$Badn`7vl&pCbv@RfB)DI~-8yeK53QJipNf+nJRhqMUx6DROQ8)V> z-`9?W`}f4KNKBYO=Ig ziH|m5TnD#ijmv#&O+7qFFGpK-+`i2)x0{%YkdE9mcCi!Pz zONBz@n5pR``Wt|7=R6u!9jS62Dw%y6FkYm%$J=57a&J!gQoJ$!EVLc9cba!K+E*|7 zMDEddqf@FD?fEnSXma?Cc}D`{H%dVJ=76PYH?-@s4&pD^rqoqr`XSt_BE`u6SX@<} z?Fn|i1HJlGtgiykPj1{!YLE@+v{!7%g5KUJ1la0nmU?{4AKrI$Mg<(d^^g2>2KpAI zr9;|=SB}8++N!btJ`bDb$zdq*_x;|D0Yj~?hI@7x`#GTTlvB2upJu{4*A~C138fOo zYLqd(+8lpoQhL#e)43)$zP@hIwg4F!Rkw*FJ@QG}`Wj5adPdEsaY+F2cjI{XIX*Ep zhhDyt)GzaQ^MPA$i);|%fxj=u3Jr&TAe)}UY_s6=cUHrC*T4q*AxZS74fAHz zL18gz(`KqiV9h8)Zl1gk*!2f7TPgT1t@p2kxL+Jp;&-mi?Ywc7<4(OLg1^L5ARZzb zlJ_Z?I`yA&>@ojuhn46kq96BN8~=LO=T1tJvgf%30P7M`>ug&pl+;-;Vg-j=-jCN?T4s0@Ps) zZF?bY;*~#n1(tRA4`zb>D(rH_XI*V=1VXe;7p!+66)bWFwj=d&`g>jRJ){hKm~XNr z4Ay&@5(flFrV|$eeVu9Sag0?*J4~CRHOW!cv``s+tBsHYb~PY%zb63ijN!Upl330| z-X!}5xm`yTQ~%+Ng4f>?^;G9c;dRH6rDijq9Pq&Bn9O{0G14sEVBa5gBFb+W69PfQFR8ijj|t18}Fhum#s z+z4mo|5l}r-f=`n0JzUpT3e;%$1E`^pt@-7Nss>&1S2+z1hz?xJ z;HzTk$obY?==zI^2E*N;K*aR>epKx7 zp7R1$qK|Z>wc&kqW8DxJwrx9mAY5>3POqa9YHMDK+ZNVj1S(9=cb(@G4}ggcq;LNw zcTDf)nYfcJF|o+9IqX53N%+_@=vvjpC?HypzoP2k2=_Wf)_Wmi{Gq~E4bIhCUJbs) zG)^Dd0}W79&olyJ=f`1@|6L+OVeyO7&f68Tjmls&DQ{J(t#P$%ICi;;ayiu9b;=nrh!7x#z`sKl%cdkMp#g`p=15vYd;6&5eaNiT_ zTd>%P4McQRJHC-1VIxcMtT#~$5^C!ar0rSA#j?jUdQX|q*=Nv~5e)&W1~8oL{SuG5 z;w}3Vf+$0rLB&`WHfvU?Kc)aWS9etpbi`y$mj7S`1qw5t>P(wkQ{n9Slq1kr;6bOi zh#h(CW;ap)6%fWDS*L}e@8u?&*faqB7vBQL1E^CaY=Tpbv1-=C8Evp;1;l1O8C2%d z&;i{zy|!hTDM7SU?oQUta|Q{8{UNkCjwYV-<}V?hq2UE9JfTCM=Y?(&PrAs`36I;1 zl`7St31sL;&Z~!8j-QDqPeHc*%Eddb=#hVz{x9Zb532k>NiYI{HyJg>Paz8WvWx~0 MO;-_AB(j*rT_o{ literal 0 HcmV?d00001 diff --git a/vendor/lunasvg/3rdparty/plutovg/FTL.TXT b/vendor/lunasvg/plutovg/source/FTL.TXT similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/FTL.TXT rename to vendor/lunasvg/plutovg/source/FTL.TXT diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-blend.c b/vendor/lunasvg/plutovg/source/plutovg-blend.c similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-blend.c rename to vendor/lunasvg/plutovg/source/plutovg-blend.c diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-canvas.c b/vendor/lunasvg/plutovg/source/plutovg-canvas.c similarity index 86% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-canvas.c rename to vendor/lunasvg/plutovg/source/plutovg-canvas.c index 187b2ef81e1..908ac4e9220 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-canvas.c +++ b/vendor/lunasvg/plutovg/source/plutovg-canvas.c @@ -82,11 +82,12 @@ static void plutovg_state_destroy(plutovg_state_t* state) plutovg_canvas_t* plutovg_canvas_create(plutovg_surface_t* surface) { plutovg_canvas_t* canvas = malloc(sizeof(plutovg_canvas_t)); - canvas->ref_count = 1; + 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); @@ -95,17 +96,13 @@ plutovg_canvas_t* plutovg_canvas_create(plutovg_surface_t* surface) plutovg_canvas_t* plutovg_canvas_reference(plutovg_canvas_t* canvas) { - if(canvas == NULL) - return NULL; - ++canvas->ref_count; + plutovg_increment_reference(canvas); return canvas; } void plutovg_canvas_destroy(plutovg_canvas_t* canvas) { - if(canvas == NULL) - return; - if(--canvas->ref_count == 0) { + if(plutovg_destroy_reference(canvas)) { while(canvas->state) { plutovg_state_t* state = canvas->state; canvas->state = state->next; @@ -118,6 +115,7 @@ void plutovg_canvas_destroy(plutovg_canvas_t* canvas) 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); @@ -128,9 +126,7 @@ void plutovg_canvas_destroy(plutovg_canvas_t* canvas) int plutovg_canvas_get_reference_count(const plutovg_canvas_t* canvas) { - if(canvas == NULL) - return 0; - return canvas->ref_count; + return plutovg_get_reference_count(canvas); } plutovg_surface_t* plutovg_canvas_get_surface(const plutovg_canvas_t* canvas) @@ -212,6 +208,43 @@ plutovg_paint_t* plutovg_canvas_get_paint(const plutovg_canvas_t* canvas, plutov 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); @@ -469,33 +502,45 @@ plutovg_path_t* plutovg_canvas_get_path(const plutovg_canvas_t* canvas) return canvas->path; } -void plutovg_canvas_fill_extents(const plutovg_canvas_t* canvas, plutovg_rect_t* extents) +bool plutovg_canvas_fill_contains(plutovg_canvas_t* canvas, float x, float y) { - plutovg_path_extents(canvas->path, extents, true); - plutovg_canvas_map_rect(canvas, extents, extents); + 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); } -void plutovg_canvas_stroke_extents(const plutovg_canvas_t* canvas, plutovg_rect_t* extents) +bool plutovg_canvas_stroke_contains(plutovg_canvas_t* canvas, float x, float y) { - plutovg_stroke_data_t* stroke = &canvas->state->stroke; - float cap_limit = stroke->style.width / 2.f; - if(stroke->style.cap == PLUTOVG_LINE_CAP_SQUARE) - cap_limit *= PLUTOVG_SQRT2; - float join_limit = stroke->style.width / 2.f; - if(stroke->style.join == PLUTOVG_LINE_JOIN_MITER) { - join_limit *= stroke->style.miter_limit; + 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 delta = plutovg_max(cap_limit, join_limit); - plutovg_path_extents(canvas->path, extents, true); - extents->x -= delta; - extents->y -= delta; - extents->w += delta * 2.f; - extents->h += delta * 2.f; - plutovg_canvas_map_rect(canvas, extents, extents); + 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(const plutovg_canvas_t* canvas, plutovg_rect_t* 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); diff --git a/vendor/lunasvg/plutovg/source/plutovg-font.c b/vendor/lunasvg/plutovg/source/plutovg-font.c new file mode 100644 index 00000000000..3dae6f9c8ee --- /dev/null +++ b/vendor/lunasvg/plutovg/source/plutovg-font.c @@ -0,0 +1,1060 @@ +#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_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 +#include + +#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/3rdparty/plutovg/plutovg-ft-math.c b/vendor/lunasvg/plutovg/source/plutovg-ft-math.c similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-math.c rename to vendor/lunasvg/plutovg/source/plutovg-ft-math.c diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-math.h b/vendor/lunasvg/plutovg/source/plutovg-ft-math.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-math.h rename to vendor/lunasvg/plutovg/source/plutovg-ft-math.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-raster.c b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c similarity index 87% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-raster.c rename to vendor/lunasvg/plutovg/source/plutovg-ft-raster.c index b31633ce293..4f2f073a62c 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-raster.c +++ b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c @@ -177,6 +177,9 @@ PVG_FT_END_STMNT 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; @@ -492,39 +495,38 @@ PVG_FT_END_STMNT /* Render a given line as a series of scanlines. */ /* */ static void - gray_render_line( RAS_ARG_ TPos to_x, - TPos to_y ) + 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( ras.y ); + 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 ) ) - goto End; + return; - fy1 = FRACT( ras.y ); + fy1 = FRACT( from_y ); fy2 = FRACT( to_y ); /* everything is on a single scanline */ if ( ey1 == ey2 ) { - gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); - goto End; + gray_render_scanline( RAS_VAR_ ey1, from_x, fy1, to_x, fy2 ); + return; } - dx = to_x - ras.x; - dy = to_y - ras.y; + dx = to_x - from_x; + dy = to_y - from_y; /* vertical line - avoid calling gray_render_scanline */ if ( dx == 0 ) { - TCoord ex = TRUNC( ras.x ); - TCoord two_fx = FRACT( ras.x ) << 1; + TCoord ex = TRUNC( from_x ); + TCoord two_fx = FRACT( from_x ) << 1; TPos area, max_ey1; @@ -590,7 +592,7 @@ PVG_FT_END_STMNT ras.area += (TArea)two_fx * delta; ras.cover += delta; - goto End; + return; } /* ok, we have to render several scanlines */ @@ -612,8 +614,8 @@ PVG_FT_END_STMNT /* keep track of its accumulation for accurate rendering. */ PVG_FT_DIV_MOD( TCoord, p, dy, delta, mod ); - x = ras.x + delta; - gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first ); + 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 ); @@ -650,10 +652,6 @@ PVG_FT_END_STMNT gray_render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL - first, to_x, fy2 ); - - End: - ras.x = to_x; - ras.y = to_y; } @@ -664,28 +662,27 @@ PVG_FT_END_STMNT /* Render a straight line across multiple cells in any direction. */ /* */ static void - gray_render_line( RAS_ARG_ TPos to_x, - TPos to_y ) + 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( ras.x ); + ex1 = TRUNC( from_x ); ex2 = TRUNC( to_x ); - ey1 = TRUNC( ras.y ); + 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 ) ) - goto End; + return; - dx = to_x - ras.x; - dy = to_y - ras.y; + dx = to_x - from_x; + dy = to_y - from_y; - fx1 = FRACT( ras.x ); - fy1 = FRACT( ras.y ); + fx1 = FRACT( from_x ); + fy1 = FRACT( from_y ); if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */ ; @@ -787,14 +784,176 @@ PVG_FT_END_STMNT ras.cover += ( fy2 - fy1 ); ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 ); - - End: - ras.x = to_x; - ras.y = to_y; } #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 ) { @@ -840,6 +999,8 @@ PVG_FT_END_STMNT 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; @@ -873,7 +1034,7 @@ PVG_FT_END_STMNT split <<= 1; } - gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + gray_line_to( RAS_VAR_ arc[0].x, arc[0].y ); arc -= 2; } while ( --draw ); @@ -940,6 +1101,8 @@ PVG_FT_END_STMNT 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; @@ -989,7 +1152,7 @@ PVG_FT_END_STMNT dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 ) goto Split; - gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + gray_line_to( RAS_VAR_ arc[0].x, arc[0].y ); if ( arc == bez_stack ) return; @@ -1024,6 +1187,8 @@ PVG_FT_END_STMNT 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; @@ -1361,7 +1526,7 @@ void PVG_FT_Outline_Get_CBox(const PVG_FT_Outline* outline, PVG_FT_BBox* acbox) vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); - gray_render_line(user, UPSCALE(vec.x), UPSCALE(vec.y)); + gray_line_to(user, UPSCALE(vec.x), UPSCALE(vec.y)); continue; } @@ -1443,7 +1608,7 @@ void PVG_FT_Outline_Get_CBox(const PVG_FT_Outline* outline, PVG_FT_BBox* acbox) } /* close the contour with a line segment */ - gray_render_line(user, UPSCALE(v_start.x), UPSCALE(v_start.y)); + gray_line_to(user, UPSCALE(v_start.x), UPSCALE(v_start.y)); Close: first = last + 1; @@ -1506,11 +1671,32 @@ void PVG_FT_Outline_Get_CBox(const PVG_FT_Outline* outline, PVG_FT_BBox* acbox) ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax ) return 0; - if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin; - if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin; + 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; + } - if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax; - if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax; + 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; diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-raster.h b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-raster.h rename to vendor/lunasvg/plutovg/source/plutovg-ft-raster.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-stroker.c b/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-stroker.c rename to vendor/lunasvg/plutovg/source/plutovg-ft-stroker.c diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-stroker.h b/vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-stroker.h rename to vendor/lunasvg/plutovg/source/plutovg-ft-stroker.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-ft-types.h b/vendor/lunasvg/plutovg/source/plutovg-ft-types.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-ft-types.h rename to vendor/lunasvg/plutovg/source/plutovg-ft-types.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-matrix.c b/vendor/lunasvg/plutovg/source/plutovg-matrix.c similarity index 98% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-matrix.c rename to vendor/lunasvg/plutovg/source/plutovg-matrix.c index 312a7009cb6..d784d7a47f0 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-matrix.c +++ b/vendor/lunasvg/plutovg/source/plutovg-matrix.c @@ -10,9 +10,7 @@ void plutovg_matrix_init(plutovg_matrix_t* matrix, float a, float b, float c, fl void plutovg_matrix_init_identity(plutovg_matrix_t* matrix) { - matrix->a = 1; matrix->b = 0; - matrix->c = 0; matrix->d = 1; - matrix->e = 0; matrix->f = 0; + plutovg_matrix_init(matrix, 1, 0, 0, 1, 0, 0); } void plutovg_matrix_init_translate(plutovg_matrix_t* matrix, float tx, float ty) diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-paint.c b/vendor/lunasvg/plutovg/source/plutovg-paint.c similarity index 98% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-paint.c rename to vendor/lunasvg/plutovg/source/plutovg-paint.c index 44fe3a967dc..43672ffdcdf 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-paint.c +++ b/vendor/lunasvg/plutovg/source/plutovg-paint.c @@ -91,9 +91,7 @@ static inline uint8_t hex_digit(uint8_t c) return c - '0'; if(c >= 'a' && c <= 'f') return 10 + c - 'a'; - if(c >= 'A' && c <= 'F') - return 10 + c - 'A'; - return 0; + return 10 + c - 'A'; } static inline uint8_t hex_byte(uint8_t c1, uint8_t c2) @@ -387,7 +385,7 @@ int plutovg_color_parse(plutovg_color_t* color, const char* data, int length) static void* plutovg_paint_create(plutovg_paint_type_t type, size_t size) { plutovg_paint_t* paint = malloc(size); - paint->ref_count = 1; + plutovg_init_reference(paint); paint->type = type; return paint; } @@ -469,17 +467,13 @@ plutovg_paint_t* plutovg_paint_create_texture(plutovg_surface_t* surface, plutov plutovg_paint_t* plutovg_paint_reference(plutovg_paint_t* paint) { - if(paint == NULL) - return NULL; - ++paint->ref_count; + plutovg_increment_reference(paint); return paint; } void plutovg_paint_destroy(plutovg_paint_t* paint) { - if(paint == NULL) - return; - if(--paint->ref_count == 0) { + 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); @@ -491,7 +485,5 @@ void plutovg_paint_destroy(plutovg_paint_t* paint) int plutovg_paint_get_reference_count(const plutovg_paint_t* paint) { - if(paint) - return paint->ref_count; - return 0; + return plutovg_get_reference_count(paint); } diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-path.c b/vendor/lunasvg/plutovg/source/plutovg-path.c similarity index 98% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-path.c rename to vendor/lunasvg/plutovg/source/plutovg-path.c index cfcc0d7faa1..2efaa6332dd 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-path.c +++ b/vendor/lunasvg/plutovg/source/plutovg-path.c @@ -38,28 +38,24 @@ plutovg_path_command_t plutovg_path_iterator_next(plutovg_path_iterator_t* it, p plutovg_path_t* plutovg_path_create(void) { plutovg_path_t* path = malloc(sizeof(plutovg_path_t)); - path->ref_count = 1; + plutovg_init_reference(path); path->num_points = 0; path->num_contours = 0; path->num_curves = 0; - path->start_point = PLUTOVG_MAKE_POINT(0, 0); + path->start_point = PLUTOVG_EMPTY_POINT; plutovg_array_init(path->elements); return path; } plutovg_path_t* plutovg_path_reference(plutovg_path_t* path) { - if(path == NULL) - return NULL; - ++path->ref_count; + plutovg_increment_reference(path); return path; } void plutovg_path_destroy(plutovg_path_t* path) { - if(path == NULL) - return; - if(--path->ref_count == 0) { + if(plutovg_destroy_reference(path)) { plutovg_array_destroy(path->elements); free(path); } @@ -67,9 +63,7 @@ void plutovg_path_destroy(plutovg_path_t* path) int plutovg_path_get_reference_count(const plutovg_path_t* path) { - if(path) - return path->ref_count; - return 0; + return plutovg_get_reference_count(path); } int plutovg_path_get_elements(const plutovg_path_t* path, const plutovg_path_element_t** elements) @@ -244,7 +238,7 @@ void plutovg_path_reserve(plutovg_path_t* path, int count) void plutovg_path_reset(plutovg_path_t* path) { plutovg_array_clear(path->elements); - path->start_point = PLUTOVG_MAKE_POINT(0, 0); + path->start_point = PLUTOVG_EMPTY_POINT; path->num_points = 0; path->num_contours = 0; path->num_curves = 0; @@ -611,7 +605,7 @@ void plutovg_path_traverse_dashed(const plutovg_path_t* path, float offset, cons dasher.phase = dasher.start_phase; dasher.index = dasher.start_index; dasher.toggle = dasher.start_toggle; - dasher.current_point = PLUTOVG_MAKE_POINT(0, 0); + dasher.current_point = PLUTOVG_EMPTY_POINT; dasher.traverse_func = traverse_func; dasher.closure = closure; plutovg_path_traverse_flatten(path, dash_traverse_func, &dasher); diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-private.h b/vendor/lunasvg/plutovg/source/plutovg-private.h similarity index 70% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-private.h rename to vendor/lunasvg/plutovg/source/plutovg-private.h index 334e8b80d47..838cd437b68 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-private.h +++ b/vendor/lunasvg/plutovg/source/plutovg-private.h @@ -3,8 +3,41 @@ #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 { - int ref_count; + plutovg_ref_count_t ref_count; int width; int height; int stride; @@ -12,7 +45,7 @@ struct plutovg_surface { }; struct plutovg_path { - int ref_count; + plutovg_ref_count_t ref_count; int num_points; int num_contours; int num_curves; @@ -31,7 +64,7 @@ typedef enum { } plutovg_paint_type_t; struct plutovg_paint { - int ref_count; + plutovg_ref_count_t ref_count; plutovg_paint_type_t type; }; @@ -120,11 +153,12 @@ typedef struct plutovg_state { } plutovg_state_t; struct plutovg_canvas { - int ref_count; + 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; @@ -135,6 +169,7 @@ void plutovg_span_buffer_init_rect(plutovg_span_buffer_t* span_buffer, int x, in 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); diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-rasterize.c b/vendor/lunasvg/plutovg/source/plutovg-rasterize.c similarity index 96% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-rasterize.c rename to vendor/lunasvg/plutovg/source/plutovg-rasterize.c index 2b6ea805531..559f0ee56ee 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-rasterize.c +++ b/vendor/lunasvg/plutovg/source/plutovg-rasterize.c @@ -55,6 +55,23 @@ void plutovg_span_buffer_copy(plutovg_span_buffer_t* span_buffer, const plutovg_ 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) @@ -172,7 +189,7 @@ static void ft_outline_destroy(PVG_FT_Outline* outline) free(outline); } -#define FT_COORD(x) (PVG_FT_Pos)((x) * 64) +#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); diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-stb-image-write.h b/vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-stb-image-write.h rename to vendor/lunasvg/plutovg/source/plutovg-stb-image-write.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-stb-image.h b/vendor/lunasvg/plutovg/source/plutovg-stb-image.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-stb-image.h rename to vendor/lunasvg/plutovg/source/plutovg-stb-image.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-stb-truetype.h b/vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h similarity index 100% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-stb-truetype.h rename to vendor/lunasvg/plutovg/source/plutovg-stb-truetype.h diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-surface.c b/vendor/lunasvg/plutovg/source/plutovg-surface.c similarity index 96% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-surface.c rename to vendor/lunasvg/plutovg/source/plutovg-surface.c index 79263cec4d6..f12cd38c4d7 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-surface.c +++ b/vendor/lunasvg/plutovg/source/plutovg-surface.c @@ -11,13 +11,14 @@ static plutovg_surface_t* plutovg_surface_create_uninitialized(int width, int height) { - if(width > STBI_MAX_DIMENSIONS || height > STBI_MAX_DIMENSIONS) + 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; - surface->ref_count = 1; + plutovg_init_reference(surface); surface->width = width; surface->height = height; surface->stride = width * 4; @@ -36,7 +37,7 @@ plutovg_surface_t* plutovg_surface_create(int width, int height) plutovg_surface_t* plutovg_surface_create_for_data(unsigned char* data, int width, int height, int stride) { plutovg_surface_t* surface = malloc(sizeof(plutovg_surface_t)); - surface->ref_count = 1; + plutovg_init_reference(surface); surface->width = width; surface->height = height; surface->stride = stride; @@ -149,26 +150,20 @@ plutovg_surface_t* plutovg_surface_load_from_image_base64(const char* data, int plutovg_surface_t* plutovg_surface_reference(plutovg_surface_t* surface) { - if(surface == NULL) - return NULL; - ++surface->ref_count; + plutovg_increment_reference(surface); return surface; } void plutovg_surface_destroy(plutovg_surface_t* surface) { - if(surface == NULL) - return; - if(--surface->ref_count == 0) { + if(plutovg_destroy_reference(surface)) { free(surface); } } int plutovg_surface_get_reference_count(const plutovg_surface_t* surface) { - if(surface) - return surface->ref_count; - return 0; + return plutovg_get_reference_count(surface); } unsigned char* plutovg_surface_get_data(const plutovg_surface_t* surface) diff --git a/vendor/lunasvg/3rdparty/plutovg/plutovg-utils.h b/vendor/lunasvg/plutovg/source/plutovg-utils.h similarity index 98% rename from vendor/lunasvg/3rdparty/plutovg/plutovg-utils.h rename to vendor/lunasvg/plutovg/source/plutovg-utils.h index 585d8fb1a39..68c86bd8418 100644 --- a/vendor/lunasvg/3rdparty/plutovg/plutovg-utils.h +++ b/vendor/lunasvg/plutovg/source/plutovg-utils.h @@ -16,7 +16,7 @@ #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) : (hi) < (v) ? (hi) : (v)) +#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) diff --git a/vendor/lunasvg/premake5.lua b/vendor/lunasvg/premake5.lua index 074706fd06a..a11fb592670 100644 --- a/vendor/lunasvg/premake5.lua +++ b/vendor/lunasvg/premake5.lua @@ -15,11 +15,11 @@ project "lunasvg" vpaths { ["Headers"] = "source/**.h", ["Headers/*"] = "include/**.h", - ["Headers/3rdparty/*"] = "3rdparty/**.h", + ["Headers/plutovg/*"] = "plutovg/**.h", ["Sources"] = "source/**.cpp", ["Sources/*"] = "source/**.c", - ["Sources/3rdparty"] = "3rdparty/**.cpp", - ["Sources/3rdparty/*"] = "3rdparty/**.c", + ["Sources/plutovg"] = "plutovg/**.cpp", + ["Sources/plutovg/*"] = "plutovg/**.c", ["*"] = "premake5.lua" } @@ -31,7 +31,7 @@ project "lunasvg" } includedirs { - "3rdparty/plutovg", + "plutovg", "source", "include" } diff --git a/vendor/lunasvg/source/graphics.cpp b/vendor/lunasvg/source/graphics.cpp index 38d00b2fa5c..248c087bf29 100644 --- a/vendor/lunasvg/source/graphics.cpp +++ b/vendor/lunasvg/source/graphics.cpp @@ -360,10 +360,8 @@ void PathIterator::next() m_index += m_elements[m_index].header.length; } -const std::string emptyString; - FontFace::FontFace(plutovg_font_face_t* face) - : m_face(face) + : m_face(plutovg_font_face_reference(face)) { } @@ -417,108 +415,62 @@ plutovg_font_face_t* FontFace::release() bool FontFaceCache::addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face) { if(!face.isNull()) - m_table[family].emplace_back(bold, italic, face); + plutovg_font_face_cache_add(m_cache, family.data(), bold, italic, face.get()); return !face.isNull(); } -FontFace FontFaceCache::getFontFace(const std::string_view& family, bool bold, bool italic) +FontFace FontFaceCache::getFontFace(const std::string& family, bool bold, bool italic) const { - auto it = m_table.find(family); - if(it == m_table.end()) { - return FontFace(); + if(auto face = plutovg_font_face_cache_get(m_cache, family.data(), bold, italic)) { + return FontFace(face); } - auto select = [bold, italic](const FontFaceEntry& a, const FontFaceEntry& b) { - if(std::get<2>(a).isNull()) - return b; - if(std::get<2>(b).isNull()) - return a; - int aScore = (bold == std::get<0>(a)) + (italic == std::get<1>(a)); - int bScore = (bold == std::get<0>(b)) + (italic == std::get<1>(b)); - return aScore > bScore ? a : b; + 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"} }; - FontFaceEntry entry; - for(const auto& item : it->second) { - entry = select(entry, item); + 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 std::get<2>(entry); + return FontFace(); } FontFaceCache::FontFaceCache() + : m_cache(plutovg_font_face_cache_create()) { - static const struct { - const char* filename; - const bool bold; - const bool italic; - } entries[] = { -#if defined(_WIN32) - {"C:/Windows/Fonts/arial.ttf", false, false}, - {"C:/Windows/Fonts/arialbd.ttf", true, false}, - {"C:/Windows/Fonts/ariali.ttf", false, true}, - {"C:/Windows/Fonts/arialbi.ttf", true, true}, -#elif defined(__APPLE__) - {"/Library/Fonts/Arial.ttf", false, false}, - {"/Library/Fonts/Arial Bold.ttf", true, false}, - {"/Library/Fonts/Arial Italic.ttf", false, true}, - {"/Library/Fonts/Arial Bold Italic.ttf", true, true}, - - {"/System/Library/Fonts/Supplemental/Arial.ttf", false, false}, - {"/System/Library/Fonts/Supplemental/Arial Bold.ttf", true, false}, - {"/System/Library/Fonts/Supplemental/Arial Italic.ttf", false, true}, - {"/System/Library/Fonts/Supplemental/Arial Bold Italic.ttf", true, true}, -#elif defined(__linux__) - {"/usr/share/fonts/dejavu/DejaVuSans.ttf", false, false}, - {"/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf", true, false}, - {"/usr/share/fonts/dejavu/DejaVuSans-Oblique.ttf", false, true}, - {"/usr/share/fonts/dejavu/DejaVuSans-BoldOblique.ttf", true, true}, - - {"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", false, false}, - {"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", true, false}, - {"/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf", false, true}, - {"/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf", true, true}, +#ifndef LUNASVG_DISABLE_LOAD_SYSTEM_FONTS + plutovg_font_face_cache_load_sys(m_cache); #endif - }; - - for(const auto& entry : entries) { - addFontFace(emptyString, entry.bold, entry.italic, FontFace(entry.filename)); - } } FontFaceCache* fontFaceCache() { - thread_local FontFaceCache cache; + static FontFaceCache cache; return &cache; } Font::Font(const FontFace& face, float size) : m_face(face), m_size(size) { -} - -float Font::ascent() const -{ - float ascent = 0; - if(m_size > 0.f && !m_face.isNull()) - plutovg_font_face_get_metrics(m_face.get(), m_size, &ascent, nullptr, nullptr, nullptr); - return ascent; -} - -float Font::descent() const -{ - float descent = 0; - if(m_size > 0.f && !m_face.isNull()) - plutovg_font_face_get_metrics(m_face.get(), m_size, nullptr, &descent, nullptr, nullptr); - return descent; -} - -float Font::height() const -{ - float ascent = 0, descent = 0; - if(m_size > 0.f && !m_face.isNull()) - plutovg_font_face_get_metrics(m_face.get(), m_size, &ascent, &descent, nullptr, nullptr); - return ascent + descent; + 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 @@ -543,8 +495,8 @@ std::shared_ptr Canvas::create(const Bitmap& bitmap) std::shared_ptr Canvas::create(float x, float y, float width, float height) { - constexpr int kMaxSize = 1 << 24; - if(width <= 0 || height <= 0 || width > kMaxSize || height > kMaxSize) + 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)); @@ -585,8 +537,7 @@ void Canvas::setTexture(const Canvas& source, TextureType type, float opacity, c void Canvas::fillPath(const Path& path, FillRule fillRule, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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); @@ -595,8 +546,7 @@ void Canvas::fillPath(const Path& path, FillRule fillRule, const Transform& tran void Canvas::strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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()); @@ -610,8 +560,7 @@ void Canvas::strokePath(const Path& path, const StrokeData& strokeData, const Tr void Canvas::fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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); @@ -621,8 +570,7 @@ void Canvas::fillText(const std::u32string_view& text, const Font& font, const P void Canvas::strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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); @@ -637,8 +585,7 @@ void Canvas::strokeText(const std::u32string_view& text, float strokeWidth, cons void Canvas::clipPath(const Path& path, FillRule clipRule, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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()); @@ -646,8 +593,7 @@ void Canvas::clipPath(const Path& path, FillRule clipRule, const Transform& tran void Canvas::clipRect(const Rect& rect, FillRule clipRule, const Transform& transform) { - plutovg_canvas_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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); @@ -658,8 +604,7 @@ void Canvas::drawImage(const Bitmap& image, const Rect& dstRect, const Rect& src 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_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_y); + 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); @@ -671,8 +616,7 @@ void Canvas::drawImage(const Bitmap& image, const Rect& dstRect, const Rect& src 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_reset_matrix(m_canvas); - plutovg_canvas_translate(m_canvas, -m_x, -m_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); @@ -708,11 +652,18 @@ void Canvas::convertToLuminanceMask() 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; - auto l = (2*r + 3*g + b) / 6; - pixels[x] = l << 24; + 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; } } } @@ -726,6 +677,7 @@ Canvas::~Canvas() 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) { } @@ -733,6 +685,7 @@ Canvas::Canvas(const Bitmap& bitmap) 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) { } diff --git a/vendor/lunasvg/source/graphics.h b/vendor/lunasvg/source/graphics.h index 737631ff4ff..7e6bc121bde 100644 --- a/vendor/lunasvg/source/graphics.h +++ b/vendor/lunasvg/source/graphics.h @@ -10,7 +10,6 @@ #include #include #include -#include namespace lunasvg { @@ -199,6 +198,9 @@ class Rect { 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; @@ -392,12 +394,10 @@ class PathIterator { int m_index; }; -extern const std::string emptyString; - class FontFace { public: FontFace() = default; - FontFace(plutovg_font_face_t* face); + 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); @@ -420,12 +420,11 @@ class FontFace { class FontFaceCache { public: bool addFontFace(const std::string& family, bool bold, bool italic, const FontFace& face); - FontFace getFontFace(const std::string_view& family, bool bold, bool italic); + FontFace getFontFace(const std::string& family, bool bold, bool italic) const; private: FontFaceCache(); - using FontFaceEntry = std::tuple; - std::map, std::less<>> m_table; + plutovg_font_face_cache_t* m_cache; friend FontFaceCache* fontFaceCache(); }; @@ -436,9 +435,10 @@ class Font { Font() = default; Font(const FontFace& face, float size); - float ascent() const; - float descent() const; - float height() const; + 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; @@ -451,6 +451,9 @@ class Font { 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 { @@ -549,6 +552,7 @@ class Canvas { 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; }; diff --git a/vendor/lunasvg/source/lunasvg.cpp b/vendor/lunasvg/source/lunasvg.cpp index bcbe98e5d6f..0a4399fbe58 100644 --- a/vendor/lunasvg/source/lunasvg.cpp +++ b/vendor/lunasvg/source/lunasvg.cpp @@ -359,7 +359,7 @@ Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) Matrix matrix(xScale, 0, 0, yScale, -elementBounds.x * xScale, -elementBounds.y * yScale); Bitmap bitmap(width, height); - bitmap.clear(backgroundColor); + if(backgroundColor) bitmap.clear(backgroundColor); render(bitmap, matrix); return bitmap; } @@ -408,11 +408,11 @@ NodeList Element::children() const return children; } -SVGElement* Element::element(bool layout) const +SVGElement* Element::element(bool layoutIfNeeded) const { auto element = static_cast(m_node); - if(element && layout) - element->rootElement()->updateLayout(); + if(element && layoutIfNeeded) + element->rootElement()->layoutIfNeeded(); return element; } @@ -448,22 +448,22 @@ std::unique_ptr Document::loadFromData(const char* data, size_t length float Document::width() const { - return m_rootElement->updateLayout()->intrinsicWidth(); + return rootElement(true)->intrinsicWidth(); } float Document::height() const { - return m_rootElement->updateLayout()->intrinsicHeight(); + return rootElement(true)->intrinsicHeight(); } Box Document::boundingBox() const { - return m_rootElement->updateLayout()->localTransform().mapRect(m_rootElement->paintBoundingBox()); + return rootElement(true)->localTransform().mapRect(rootElement()->paintBoundingBox()); } void Document::updateLayout() { - m_rootElement->updateLayout(); + m_rootElement->layoutIfNeeded(); } void Document::forceLayout() @@ -477,13 +477,13 @@ void Document::render(Bitmap& bitmap, const Matrix& matrix) const return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); - m_rootElement->updateLayout()->render(state); + rootElement(true)->render(state); } Bitmap Document::renderToBitmap(int width, int height, uint32_t backgroundColor) const { - auto intrinsicWidth = m_rootElement->updateLayout()->intrinsicWidth(); - auto intrinsicHeight = m_rootElement->intrinsicHeight(); + auto intrinsicWidth = rootElement(true)->intrinsicWidth(); + auto intrinsicHeight = rootElement()->intrinsicHeight(); if(intrinsicWidth == 0.f || intrinsicHeight == 0.f) return Bitmap(); if(width <= 0 && height <= 0) { @@ -500,11 +500,16 @@ Bitmap Document::renderToBitmap(int width, int height, uint32_t backgroundColor) Matrix matrix(xScale, 0, 0, yScale, 0, 0); Bitmap bitmap(width, height); - bitmap.clear(backgroundColor); + 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); @@ -515,6 +520,13 @@ 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; diff --git a/vendor/lunasvg/source/svgelement.cpp b/vendor/lunasvg/source/svgelement.cpp index dca432c134f..942ce97e779 100644 --- a/vendor/lunasvg/source/svgelement.cpp +++ b/vendor/lunasvg/source/svgelement.cpp @@ -66,6 +66,8 @@ std::unique_ptr SVGTextNode::clone(bool deep) const return node; } +const std::string emptyString; + std::unique_ptr SVGElement::create(Document* document, ElementID id) { switch(id) { @@ -356,6 +358,32 @@ SVGPaintElement* SVGElement::getPainter(const std::string_view& id) const 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); @@ -432,6 +460,7 @@ void SVGElement::layoutElement(const SVGLayoutState& state) m_display = state.display(); m_overflow = state.overflow(); m_visibility = state.visibility(); + m_pointer_events = state.pointer_events(); } void SVGElement::layoutChildren(SVGLayoutState& state) @@ -483,6 +512,31 @@ bool SVGElement::isHiddenElement() const } } +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) { @@ -622,7 +676,7 @@ SVGRootElement::SVGRootElement(Document* document) { } -SVGRootElement* SVGRootElement::updateLayout() +SVGRootElement* SVGRootElement::layoutIfNeeded() { if(needsLayout()) forceLayout(); diff --git a/vendor/lunasvg/source/svgelement.h b/vendor/lunasvg/source/svgelement.h index 6e898470fa1..7a9c6b1ed63 100644 --- a/vendor/lunasvg/source/svgelement.h +++ b/vendor/lunasvg/source/svgelement.h @@ -120,6 +120,8 @@ 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); @@ -162,6 +164,8 @@ class SVGElement : public SVGNode { SVGMaskElement* getMasker(const std::string_view& id) const; SVGPaintElement* getPainter(const std::string_view& id) const; + SVGElement* elementFromPoint(float x, float y); + template void transverse(T callback); @@ -187,6 +191,7 @@ class SVGElement : public SVGNode { 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; } @@ -204,6 +209,7 @@ class SVGElement : public SVGNode { 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; @@ -338,7 +344,7 @@ class SVGRootElement final : public SVGSVGElement { void setNeedsLayout() { m_intrinsicWidth = -1.f; } bool needsLayout() const { return m_intrinsicWidth == -1.f; } - SVGRootElement* updateLayout(); + SVGRootElement* layoutIfNeeded(); SVGElement* getElementById(const std::string_view& id) const; void addElementById(const std::string& id, SVGElement* element); diff --git a/vendor/lunasvg/source/svglayoutstate.cpp b/vendor/lunasvg/source/svglayoutstate.cpp index cf9e81abc18..e7a295b28dd 100644 --- a/vendor/lunasvg/source/svglayoutstate.cpp +++ b/vendor/lunasvg/source/svglayoutstate.cpp @@ -147,6 +147,13 @@ static LengthList parseDashArray(std::string_view 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)); @@ -202,11 +209,32 @@ static Overflow parseOverflow(const std::string_view& input) return parseEnumValue(input, entries, Overflow::Visible); } +static PointerEvents parsePointerEvents(const 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(const 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"}, @@ -282,6 +310,33 @@ static Direction parseDirection(const std::string_view& input) return parseEnumValue(input, entries, Direction::Ltr); } +static WritingMode parseWritingMode(const 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(const std::string_view& input) +{ + static const SVGEnumerationEntry entries[] = { + {TextOrientation::Mixed, "mixed"}, + {TextOrientation::Upright, "upright"} + }; + + return parseEnumValue(input, entries, TextOrientation::Mixed); +} + static TextAnchor parseTextAnchor(const std::string_view& input) { static const SVGEnumerationEntry entries[] = { @@ -360,6 +415,8 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e , 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()) @@ -372,9 +429,12 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e , 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()) @@ -416,6 +476,12 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e 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_shit = parseBaselineShift(input); break; @@ -458,9 +524,15 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e case PropertyID::Text_Anchor: m_text_anchor = parseTextAnchor(input); break; - case PropertyID::WhiteSpace: + 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; @@ -470,6 +542,9 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e 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; @@ -518,7 +593,10 @@ Font SVGLayoutState::font() const stripLeadingAndTrailingSpaces(family); } - face = fontFaceCache()->getFontFace(family, bold, italic); + std::string font_family(family); + if(!font_family.empty()) { + face = fontFaceCache()->getFontFace(font_family, bold, italic); + } } if(face.isNull()) diff --git a/vendor/lunasvg/source/svglayoutstate.h b/vendor/lunasvg/source/svglayoutstate.h index 34af91d9c6d..1775835dd5c 100644 --- a/vendor/lunasvg/source/svglayoutstate.h +++ b/vendor/lunasvg/source/svglayoutstate.h @@ -26,6 +26,9 @@ class SVGLayoutState { 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_shit() const { return m_baseline_shit; } const Length& stroke_width() const { return m_stroke_width; } const Length& stroke_dashoffset() const { return m_stroke_dashoffset; } @@ -45,11 +48,14 @@ class SVGLayoutState { 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; } @@ -78,6 +84,9 @@ class SVGLayoutState { 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_shit; Length m_stroke_width{1.f, LengthUnits::None}; Length m_stroke_dashoffset{0.f, LengthUnits::None}; @@ -97,11 +106,14 @@ class SVGLayoutState { 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; diff --git a/vendor/lunasvg/source/svgpaintelement.cpp b/vendor/lunasvg/source/svgpaintelement.cpp index 16c3e3db0bc..0c8f5d5a011 100644 --- a/vendor/lunasvg/source/svgpaintelement.cpp +++ b/vendor/lunasvg/source/svgpaintelement.cpp @@ -115,9 +115,13 @@ SVGLinearGradientAttributes SVGLinearGradientElement::collectGradientAttributes( static GradientStops buildGradientStops(const SVGGradientElement* element, float opacity) { GradientStops gradientStops; - for(const auto& child : element->children()) { - if(auto element = toSVGElement(child); element && element->id() == ElementID::Stop) { - auto stopElement = static_cast(element); + + 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)); } } diff --git a/vendor/lunasvg/source/svgproperty.cpp b/vendor/lunasvg/source/svgproperty.cpp index 8846d90647f..e9b6ea659e4 100644 --- a/vendor/lunasvg/source/svgproperty.cpp +++ b/vendor/lunasvg/source/svgproperty.cpp @@ -26,6 +26,7 @@ PropertyID propertyid(const std::string_view& name) {"height", PropertyID::Height}, {"href", PropertyID::Href}, {"id", PropertyID::Id}, + {"lengthAdjust", PropertyID::LengthAdjust}, {"markerHeight", PropertyID::MarkerHeight}, {"markerUnits", PropertyID::MarkerUnits}, {"markerWidth", PropertyID::MarkerWidth}, @@ -46,6 +47,7 @@ PropertyID propertyid(const std::string_view& name) {"ry", PropertyID::Ry}, {"spreadMethod", PropertyID::SpreadMethod}, {"style", PropertyID::Style}, + {"textLength", PropertyID::TextLength}, {"transform", PropertyID::Transform}, {"viewBox", PropertyID::ViewBox}, {"width", PropertyID::Width}, @@ -53,7 +55,7 @@ PropertyID propertyid(const std::string_view& name) {"x1", PropertyID::X1}, {"x2", PropertyID::X2}, {"xlink:href", PropertyID::Href}, - {"xml:space", PropertyID::WhiteSpace}, + {"xml:space", PropertyID::White_Space}, {"y", PropertyID::Y}, {"y1", PropertyID::Y1}, {"y2", PropertyID::Y2} @@ -86,6 +88,7 @@ PropertyID csspropertyid(const std::string_view& name) {"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}, @@ -93,6 +96,7 @@ PropertyID csspropertyid(const std::string_view& name) {"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}, @@ -104,8 +108,11 @@ PropertyID csspropertyid(const std::string_view& name) {"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::WhiteSpace} + {"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; }); @@ -160,6 +167,17 @@ bool SVGEnumeration::parse(std::string_view input) 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]) diff --git a/vendor/lunasvg/source/svgproperty.h b/vendor/lunasvg/source/svgproperty.h index 73163fd9646..59ee12d0fef 100644 --- a/vendor/lunasvg/source/svgproperty.h +++ b/vendor/lunasvg/source/svgproperty.h @@ -12,9 +12,9 @@ enum class PropertyID : uint8_t { Alignment_Baseline, Baseline_Shift, Class, - ClipPathUnits, Clip_Path, Clip_Rule, + ClipPathUnits, Color, Cx, Cy, @@ -38,16 +38,18 @@ enum class PropertyID : uint8_t { Height, Href, Id, - MarkerHeight, - MarkerUnits, - MarkerWidth, + LengthAdjust, + Letter_Spacing, Marker_End, Marker_Mid, Marker_Start, + MarkerHeight, + MarkerUnits, + MarkerWidth, Mask, + Mask_Type, MaskContentUnits, MaskUnits, - Mask_Type, Offset, Opacity, Orient, @@ -55,6 +57,7 @@ enum class PropertyID : uint8_t { PatternContentUnits, PatternTransform, PatternUnits, + Pointer_Events, Points, PreserveAspectRatio, R, @@ -76,11 +79,15 @@ enum class PropertyID : uint8_t { Stroke_Width, Style, Text_Anchor, + Text_Orientation, + TextLength, Transform, ViewBox, Visibility, - WhiteSpace, + White_Space, Width, + Word_Spacing, + Writing_Mode, X, X1, X2, @@ -154,6 +161,20 @@ enum class Overflow : uint8_t { Hidden }; +enum class PointerEvents : uint8_t { + None, + Auto, + Stroke, + Fill, + Painted, + Visible, + VisibleStroke, + VisibleFill, + VisiblePainted, + BoundingBox, + All +}; + enum class FontStyle : uint8_t { Normal, Italic @@ -205,6 +226,16 @@ enum class WhiteSpace : uint8_t { Preserve }; +enum class WritingMode : uint8_t { + Horizontal, + Vertical +}; + +enum class TextOrientation : uint8_t { + Mixed, + Upright +}; + enum class Direction : uint8_t { Ltr, Rtl @@ -225,6 +256,11 @@ enum class MarkerUnits : uint8_t { UserSpaceOnUse }; +enum class LengthAdjust : uint8_t { + Spacing, + SpacingAndGlyphs +}; + template using SVGEnumerationEntry = std::pair; diff --git a/vendor/lunasvg/source/svgtextelement.cpp b/vendor/lunasvg/source/svgtextelement.cpp index 34d358c4531..f62280b5e89 100644 --- a/vendor/lunasvg/source/svgtextelement.cpp +++ b/vendor/lunasvg/source/svgtextelement.cpp @@ -22,6 +22,9 @@ static AlignmentBaseline resolveDominantBaseline(const SVGTextPositioningElement { 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: @@ -131,6 +134,93 @@ static float calculateTextAnchorOffset(const SVGTextPositioningElement* element, 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) { @@ -150,34 +240,50 @@ void SVGTextFragmentsBuilder::build(const SVGTextElement* textElement) 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 = element->font().measureText(text); + 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); - m_x += fragment.width; }; + 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(m_characterPositions.count(m_characterOffset) > 0) { - characterPosition = m_characterPositions.at(m_characterOffset); + 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 = characterPosition.x || characterPosition.y || dx || dy || angle || angle != lastAngle; + auto shouldStartNewFragment = needsTextLengthSpacing || isVerticalText || applySpacingToNextCharacter + || characterPosition.x || characterPosition.y || dx || dy || angle || angle != lastAngle; if(shouldStartNewFragment && didStartTextFragment) { recordTextFragment(startOffset, textOffset); + applySpacingToNextCharacter = false; startOffset = textOffset; } @@ -185,14 +291,39 @@ void SVGTextFragmentsBuilder::build(const SVGTextElement* textElement) if(startsNewTextChunk || shouldStartNewFragment || !didStartTextFragment) { m_x = dx + characterPosition.x.value_or(m_x); m_y = dy + characterPosition.y.value_or(m_y); - fragment.x = m_x; - fragment.y = m_y - baselineOffset; + 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; } @@ -200,26 +331,6 @@ void SVGTextFragmentsBuilder::build(const SVGTextElement* textElement) recordTextFragment(startOffset, textOffset); } - auto handleTextChunk = [](auto begin, auto end) { - if(!needsTextAnchorAdjustment(begin->element)) - return; - float chunkWidth = 0.f; - const SVGTextFragment* lastFragment = nullptr; - for(auto it = begin; it != end; ++it) { - const SVGTextFragment& fragment = *it; - chunkWidth += fragment.width; - if(lastFragment) - chunkWidth += fragment.x - (lastFragment->x + lastFragment->width); - lastFragment = &fragment; - } - - auto chunkOffset = calculateTextAnchorOffset(begin->element, chunkWidth); - for(auto it = begin; it != end; ++it) { - SVGTextFragment& fragment = *it; - fragment.x += chunkOffset; - } - }; - if(m_fragments.empty()) return; auto it = m_fragments.begin(); @@ -267,6 +378,8 @@ void SVGTextFragmentsBuilder::handleText(const SVGTextNode* node) 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()) { @@ -338,12 +451,16 @@ SVGTextPositioningElement::SVGTextPositioningElement(Document* document, Element , 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) @@ -355,11 +472,16 @@ void SVGTextPositioningElement::layoutElement(const SVGLayoutState& 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_shit()); 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(); } @@ -412,7 +534,9 @@ void SVGTextElement::render(SVGRenderState& state) const std::u32string_view wholeText(m_text); for(const auto& fragment : m_fragments) { - auto transform = newState.currentTransform() * Transform::rotated(fragment.angle, fragment.x, fragment.y); + 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); @@ -440,8 +564,8 @@ Rect SVGTextElement::boundingBox(bool includeStroke) const 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); - auto fragmentRect = Rect(fragment.x, fragment.y - font.ascent(), fragment.width, fragment.element->font_size()); + 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)); diff --git a/vendor/lunasvg/source/svgtextelement.h b/vendor/lunasvg/source/svgtextelement.h index 8b0a0e24b8f..71b0f1121da 100644 --- a/vendor/lunasvg/source/svgtextelement.h +++ b/vendor/lunasvg/source/svgtextelement.h @@ -35,13 +35,15 @@ 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 angle = 0; float width = 0; - bool startsNewTextChunk = false; + float height = 0; + float angle = 0; }; using SVGTextFragmentList = std::vector; @@ -77,11 +79,19 @@ class SVGTextPositioningElement : public SVGGraphicsElement { 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; } @@ -99,16 +109,23 @@ class SVGTextPositioningElement : public SVGGraphicsElement { 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; }; From 3a613f2b1cb9fe3cc19182d5718ae2435d3c9851 Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:34:34 +0000 Subject: [PATCH 3/7] Update premake5.lua --- vendor/lunasvg/premake5.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/lunasvg/premake5.lua b/vendor/lunasvg/premake5.lua index a11fb592670..22e329b9801 100644 --- a/vendor/lunasvg/premake5.lua +++ b/vendor/lunasvg/premake5.lua @@ -31,9 +31,9 @@ project "lunasvg" } includedirs { - "plutovg", - "source", - "include" + "plutovg/include", + "include", + "source" } filter "system:windows" From 969dfec6c43c050ab5c02ceb242ef64a7dec0815 Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:39:30 +0000 Subject: [PATCH 4/7] Allow custom resource fonts Lua API: svgRegisterFont(string fontFamily, string path) svgUnregisterFont(string fontFamily) --- .../deathmatch/logic/CClientVectorGraphic.cpp | 4 + .../logic/CClientVectorGraphicDisplay.cpp | 4 + Client/mods/deathmatch/logic/CResource.cpp | 4 + .../mods/deathmatch/logic/CSVGFontManager.cpp | 119 ++++++++++++++++++ .../mods/deathmatch/logic/CSVGFontManager.h | 48 +++++++ .../logic/luadefs/CLuaVectorGraphicDefs.cpp | 31 ++++- .../logic/luadefs/CLuaVectorGraphicDefs.h | 4 + 7 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 Client/mods/deathmatch/logic/CSVGFontManager.cpp create mode 100644 Client/mods/deathmatch/logic/CSVGFontManager.h diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp index 2e3dcb675e5..089f8481f07 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp @@ -54,6 +54,10 @@ bool CClientVectorGraphic::SetDocument(CXMLNode* node) m_pXMLDocument = node; m_pSVGDocument = lunasvg::Document::loadFromData(node->ToString()); + // Check if LunaSVG successfully parsed the document + if (!m_pSVGDocument) + return false; + m_pVectorGraphicDisplay->Update(); return true; diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp index bcdd75ee3e6..29c08ebe045 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp @@ -92,6 +92,10 @@ void CClientVectorGraphicDisplay::UpdateTexture() if (!surface) return; + // Check for valid SVG dimensions to avoid division by zero + if (svgDocument->width() <= 0 || svgDocument->height() <= 0) + return; + // SVG has a predefined width and height. We need transform it to the requested size const Matrix transformationMatrix(pVectorGraphicItem->m_uiSizeX / svgDocument->width(), 0, 0, pVectorGraphicItem->m_uiSizeY / svgDocument->height(), 0, 0); diff --git a/Client/mods/deathmatch/logic/CResource.cpp b/Client/mods/deathmatch/logic/CResource.cpp index d1d9be1f8ea..8c0c724d0f2 100644 --- a/Client/mods/deathmatch/logic/CResource.cpp +++ b/Client/mods/deathmatch/logic/CResource.cpp @@ -13,6 +13,7 @@ #define DECLARE_PROFILER_SECTION_CResource #include "profiler/SharedUtil.Profiler.h" #include "CServerIdManager.h" +#include "CSVGFontManager.h" using namespace std; @@ -94,6 +95,9 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient CResource::~CResource() { + // Unregister SVG fonts registered by this resource + CSVGFontManager::GetSingleton().UnregisterResourceFonts(this); + // Remove refrences from requested models m_modelStreamer.ReleaseAll(); diff --git a/Client/mods/deathmatch/logic/CSVGFontManager.cpp b/Client/mods/deathmatch/logic/CSVGFontManager.cpp new file mode 100644 index 00000000000..3d6d496f2bb --- /dev/null +++ b/Client/mods/deathmatch/logic/CSVGFontManager.cpp @@ -0,0 +1,119 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CSVGFontManager.h" +#include + +CSVGFontManager& CSVGFontManager::GetSingleton() +{ + static CSVGFontManager instance; + return instance; +} + +void CSVGFontManager::FontDataDestroyCallback(void* pData) +{ + delete[] static_cast(pData); +} + +bool CSVGFontManager::RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource) +{ + if (strFontFamily.empty() || strFontPath.empty() || !pResource) + return false; + + if (m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end()) + return false; + + SString strAbsPath; + SString strMetaPath; + + CResource* pFileResource = pResource; + if (!g_pClientGame->GetResourceManager()->ParseResourcePathInput(strFontPath, pFileResource, &strAbsPath, &strMetaPath)) + return false; + + if (!FileExists(strAbsPath)) + return false; + + SString fontData; + if (!FileLoad(strAbsPath, fontData)) + return false; + + if (fontData.empty()) + return false; + + // Allocate memory for font data that will be owned by LunaSVG + size_t dataSize = fontData.size(); + char* pFontData = new char[dataSize]; + memcpy(pFontData, fontData.data(), dataSize); + + // Register font with LunaSVG using data API + // LunaSVG takes ownership of the data and will call our destroy callback when done + if (!lunasvg_add_font_face_from_data(strFontFamily.c_str(), false, false, pFontData, dataSize, FontDataDestroyCallback, pFontData)) + { + // Registration failed, clean up the memory ourselves + delete[] pFontData; + return false; + } + + // Store font info for tracking + SFontInfo fontInfo; + fontInfo.strOriginalPath = strFontPath; + fontInfo.strAbsolutePath = strAbsPath; + fontInfo.pOwnerResource = pResource; + + m_RegisteredFonts[strFontFamily] = std::move(fontInfo); + + return true; +} + +bool CSVGFontManager::UnregisterFont(const SString& strFontFamily) +{ + auto it = m_RegisteredFonts.find(strFontFamily); + if (it == m_RegisteredFonts.end()) + return false; + + // Remove from our tracking + // Note: The font data memory will be freed by LunaSVG via our destroy callback + m_RegisteredFonts.erase(it); + return true; +} + +bool CSVGFontManager::IsFontRegistered(const SString& strFontFamily) const +{ + return m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end(); +} + +void CSVGFontManager::UnregisterResourceFonts(CResource* pResource) +{ + if (!pResource) + return; + + // Collect fonts to remove + std::vector fontsToRemove; + for (const auto& pair : m_RegisteredFonts) + { + if (pair.second.pOwnerResource == pResource) + fontsToRemove.push_back(pair.first); + } + + // Remove collected fonts from our tracking + for (const auto& fontFamily : fontsToRemove) + m_RegisteredFonts.erase(fontFamily); +} + +std::vector CSVGFontManager::GetRegisteredFonts() const +{ + std::vector fonts; + fonts.reserve(m_RegisteredFonts.size()); + + for (const auto& pair : m_RegisteredFonts) + fonts.push_back(pair.first); + + return fonts; +} diff --git a/Client/mods/deathmatch/logic/CSVGFontManager.h b/Client/mods/deathmatch/logic/CSVGFontManager.h new file mode 100644 index 00000000000..64ab4128c47 --- /dev/null +++ b/Client/mods/deathmatch/logic/CSVGFontManager.h @@ -0,0 +1,48 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include +#include + +class CResource; + +class CSVGFontManager +{ +public: + struct SFontInfo + { + SString strOriginalPath; // Original resource path for debugging + SString strAbsolutePath; // Absolute path to font file + CResource* pOwnerResource; // Resource that registered this font + }; + + static CSVGFontManager& GetSingleton(); + + + bool RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource); + bool UnregisterFont(const SString& strFontFamily); + bool IsFontRegistered(const SString& strFontFamily) const; + void UnregisterResourceFonts(CResource* pResource); + std::vector GetRegisteredFonts() const; + +private: + CSVGFontManager() = default; + ~CSVGFontManager() = default; + + CSVGFontManager(const CSVGFontManager&) = delete; + CSVGFontManager& operator=(const CSVGFontManager&) = delete; + + static void FontDataDestroyCallback(void* pData); + + // Fonts we're currently tracking (owned by resources) + std::map m_RegisteredFonts; +}; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp index 66b209bdabe..1eaed5fb33f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "lua/CLuaFunctionParser.h" #include "CLuaVectorGraphicDefs.h" +#include "../CSVGFontManager.h" void CLuaVectorGraphicDefs::LoadFunctions() { @@ -20,7 +21,8 @@ void CLuaVectorGraphicDefs::LoadFunctions() {"svgGetSize", ArgumentParser}, {"svgSetSize", ArgumentParser}, {"svgSetUpdateCallback", ArgumentParser}, - + {"svgRegisterFont", ArgumentParser}, + {"svgUnregisterFont", ArgumentParser}, }; // Add functions @@ -271,3 +273,30 @@ std::variant CLuaVectorGraphicDefs::SVGGetUpdateCallback( return false; } + +bool CLuaVectorGraphicDefs::SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath) +{ + if (fontFamily.empty()) + throw std::invalid_argument("Font family name cannot be empty"); + + if (fontPath.empty()) + throw std::invalid_argument("Font path cannot be empty"); + + CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); + if (!pLuaMain) + return false; + + CResource* pResource = pLuaMain->GetResource(); + if (!pResource) + return false; + + return CSVGFontManager::GetSingleton().RegisterFont(fontFamily, fontPath, pResource); +} + +bool CLuaVectorGraphicDefs::SVGUnregisterFont(std::string fontFamily) +{ + if (fontFamily.empty()) + throw std::invalid_argument("Font family name cannot be empty"); + + return CSVGFontManager::GetSingleton().UnregisterFont(fontFamily); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h index 6d549d652c9..f55fae65077 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h @@ -26,6 +26,10 @@ class CLuaVectorGraphicDefs : public CLuaDefs static CLuaMultiReturn SVGGetSize(CClientVectorGraphic* vectorGraphic); static bool SVGSetSize(CClientVectorGraphic* vectorGraphic, CVector2D size, std::optional luaFunctionRef); + // Font management functions + static bool SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath); + static bool SVGUnregisterFont(std::string fontFamily); + private: static bool LoadFromData(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, std::string rawData); static bool LoadFromFile(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, CScriptFile* file, std::string path, CResource* parentResource); From 7c2999537a2b1cacd3af269dc995cd7fea677443 Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Thu, 8 Jan 2026 05:07:11 +0000 Subject: [PATCH 5/7] Revert "Allow custom resource fonts" This reverts commit 969dfec6c43c050ab5c02ceb242ef64a7dec0815. --- .../deathmatch/logic/CClientVectorGraphic.cpp | 4 - .../logic/CClientVectorGraphicDisplay.cpp | 4 - Client/mods/deathmatch/logic/CResource.cpp | 4 - .../mods/deathmatch/logic/CSVGFontManager.cpp | 119 ------------------ .../mods/deathmatch/logic/CSVGFontManager.h | 48 ------- .../logic/luadefs/CLuaVectorGraphicDefs.cpp | 31 +---- .../logic/luadefs/CLuaVectorGraphicDefs.h | 4 - 7 files changed, 1 insertion(+), 213 deletions(-) delete mode 100644 Client/mods/deathmatch/logic/CSVGFontManager.cpp delete mode 100644 Client/mods/deathmatch/logic/CSVGFontManager.h diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp index 089f8481f07..2e3dcb675e5 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp @@ -54,10 +54,6 @@ bool CClientVectorGraphic::SetDocument(CXMLNode* node) m_pXMLDocument = node; m_pSVGDocument = lunasvg::Document::loadFromData(node->ToString()); - // Check if LunaSVG successfully parsed the document - if (!m_pSVGDocument) - return false; - m_pVectorGraphicDisplay->Update(); return true; diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp index 29c08ebe045..bcdd75ee3e6 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp @@ -92,10 +92,6 @@ void CClientVectorGraphicDisplay::UpdateTexture() if (!surface) return; - // Check for valid SVG dimensions to avoid division by zero - if (svgDocument->width() <= 0 || svgDocument->height() <= 0) - return; - // SVG has a predefined width and height. We need transform it to the requested size const Matrix transformationMatrix(pVectorGraphicItem->m_uiSizeX / svgDocument->width(), 0, 0, pVectorGraphicItem->m_uiSizeY / svgDocument->height(), 0, 0); diff --git a/Client/mods/deathmatch/logic/CResource.cpp b/Client/mods/deathmatch/logic/CResource.cpp index 8c0c724d0f2..d1d9be1f8ea 100644 --- a/Client/mods/deathmatch/logic/CResource.cpp +++ b/Client/mods/deathmatch/logic/CResource.cpp @@ -13,7 +13,6 @@ #define DECLARE_PROFILER_SECTION_CResource #include "profiler/SharedUtil.Profiler.h" #include "CServerIdManager.h" -#include "CSVGFontManager.h" using namespace std; @@ -95,9 +94,6 @@ CResource::CResource(unsigned short usNetID, const char* szResourceName, CClient CResource::~CResource() { - // Unregister SVG fonts registered by this resource - CSVGFontManager::GetSingleton().UnregisterResourceFonts(this); - // Remove refrences from requested models m_modelStreamer.ReleaseAll(); diff --git a/Client/mods/deathmatch/logic/CSVGFontManager.cpp b/Client/mods/deathmatch/logic/CSVGFontManager.cpp deleted file mode 100644 index 3d6d496f2bb..00000000000 --- a/Client/mods/deathmatch/logic/CSVGFontManager.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/***************************************************************************** - * - * PROJECT: Multi Theft Auto - * LICENSE: See LICENSE in the top level directory - * - * Multi Theft Auto is available from https://www.multitheftauto.com/ - * - *****************************************************************************/ - -#include "StdInc.h" -#include "CSVGFontManager.h" -#include - -CSVGFontManager& CSVGFontManager::GetSingleton() -{ - static CSVGFontManager instance; - return instance; -} - -void CSVGFontManager::FontDataDestroyCallback(void* pData) -{ - delete[] static_cast(pData); -} - -bool CSVGFontManager::RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource) -{ - if (strFontFamily.empty() || strFontPath.empty() || !pResource) - return false; - - if (m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end()) - return false; - - SString strAbsPath; - SString strMetaPath; - - CResource* pFileResource = pResource; - if (!g_pClientGame->GetResourceManager()->ParseResourcePathInput(strFontPath, pFileResource, &strAbsPath, &strMetaPath)) - return false; - - if (!FileExists(strAbsPath)) - return false; - - SString fontData; - if (!FileLoad(strAbsPath, fontData)) - return false; - - if (fontData.empty()) - return false; - - // Allocate memory for font data that will be owned by LunaSVG - size_t dataSize = fontData.size(); - char* pFontData = new char[dataSize]; - memcpy(pFontData, fontData.data(), dataSize); - - // Register font with LunaSVG using data API - // LunaSVG takes ownership of the data and will call our destroy callback when done - if (!lunasvg_add_font_face_from_data(strFontFamily.c_str(), false, false, pFontData, dataSize, FontDataDestroyCallback, pFontData)) - { - // Registration failed, clean up the memory ourselves - delete[] pFontData; - return false; - } - - // Store font info for tracking - SFontInfo fontInfo; - fontInfo.strOriginalPath = strFontPath; - fontInfo.strAbsolutePath = strAbsPath; - fontInfo.pOwnerResource = pResource; - - m_RegisteredFonts[strFontFamily] = std::move(fontInfo); - - return true; -} - -bool CSVGFontManager::UnregisterFont(const SString& strFontFamily) -{ - auto it = m_RegisteredFonts.find(strFontFamily); - if (it == m_RegisteredFonts.end()) - return false; - - // Remove from our tracking - // Note: The font data memory will be freed by LunaSVG via our destroy callback - m_RegisteredFonts.erase(it); - return true; -} - -bool CSVGFontManager::IsFontRegistered(const SString& strFontFamily) const -{ - return m_RegisteredFonts.find(strFontFamily) != m_RegisteredFonts.end(); -} - -void CSVGFontManager::UnregisterResourceFonts(CResource* pResource) -{ - if (!pResource) - return; - - // Collect fonts to remove - std::vector fontsToRemove; - for (const auto& pair : m_RegisteredFonts) - { - if (pair.second.pOwnerResource == pResource) - fontsToRemove.push_back(pair.first); - } - - // Remove collected fonts from our tracking - for (const auto& fontFamily : fontsToRemove) - m_RegisteredFonts.erase(fontFamily); -} - -std::vector CSVGFontManager::GetRegisteredFonts() const -{ - std::vector fonts; - fonts.reserve(m_RegisteredFonts.size()); - - for (const auto& pair : m_RegisteredFonts) - fonts.push_back(pair.first); - - return fonts; -} diff --git a/Client/mods/deathmatch/logic/CSVGFontManager.h b/Client/mods/deathmatch/logic/CSVGFontManager.h deleted file mode 100644 index 64ab4128c47..00000000000 --- a/Client/mods/deathmatch/logic/CSVGFontManager.h +++ /dev/null @@ -1,48 +0,0 @@ -/***************************************************************************** - * - * PROJECT: Multi Theft Auto - * LICENSE: See LICENSE in the top level directory - * - * Multi Theft Auto is available from https://www.multitheftauto.com/ - * - *****************************************************************************/ - -#pragma once - -#include -#include -#include - -class CResource; - -class CSVGFontManager -{ -public: - struct SFontInfo - { - SString strOriginalPath; // Original resource path for debugging - SString strAbsolutePath; // Absolute path to font file - CResource* pOwnerResource; // Resource that registered this font - }; - - static CSVGFontManager& GetSingleton(); - - - bool RegisterFont(const SString& strFontFamily, const SString& strFontPath, CResource* pResource); - bool UnregisterFont(const SString& strFontFamily); - bool IsFontRegistered(const SString& strFontFamily) const; - void UnregisterResourceFonts(CResource* pResource); - std::vector GetRegisteredFonts() const; - -private: - CSVGFontManager() = default; - ~CSVGFontManager() = default; - - CSVGFontManager(const CSVGFontManager&) = delete; - CSVGFontManager& operator=(const CSVGFontManager&) = delete; - - static void FontDataDestroyCallback(void* pData); - - // Fonts we're currently tracking (owned by resources) - std::map m_RegisteredFonts; -}; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp index 1eaed5fb33f..66b209bdabe 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.cpp @@ -10,7 +10,6 @@ #include "StdInc.h" #include "lua/CLuaFunctionParser.h" #include "CLuaVectorGraphicDefs.h" -#include "../CSVGFontManager.h" void CLuaVectorGraphicDefs::LoadFunctions() { @@ -21,8 +20,7 @@ void CLuaVectorGraphicDefs::LoadFunctions() {"svgGetSize", ArgumentParser}, {"svgSetSize", ArgumentParser}, {"svgSetUpdateCallback", ArgumentParser}, - {"svgRegisterFont", ArgumentParser}, - {"svgUnregisterFont", ArgumentParser}, + }; // Add functions @@ -273,30 +271,3 @@ std::variant CLuaVectorGraphicDefs::SVGGetUpdateCallback( return false; } - -bool CLuaVectorGraphicDefs::SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath) -{ - if (fontFamily.empty()) - throw std::invalid_argument("Font family name cannot be empty"); - - if (fontPath.empty()) - throw std::invalid_argument("Font path cannot be empty"); - - CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); - if (!pLuaMain) - return false; - - CResource* pResource = pLuaMain->GetResource(); - if (!pResource) - return false; - - return CSVGFontManager::GetSingleton().RegisterFont(fontFamily, fontPath, pResource); -} - -bool CLuaVectorGraphicDefs::SVGUnregisterFont(std::string fontFamily) -{ - if (fontFamily.empty()) - throw std::invalid_argument("Font family name cannot be empty"); - - return CSVGFontManager::GetSingleton().UnregisterFont(fontFamily); -} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h index f55fae65077..6d549d652c9 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVectorGraphicDefs.h @@ -26,10 +26,6 @@ class CLuaVectorGraphicDefs : public CLuaDefs static CLuaMultiReturn SVGGetSize(CClientVectorGraphic* vectorGraphic); static bool SVGSetSize(CClientVectorGraphic* vectorGraphic, CVector2D size, std::optional luaFunctionRef); - // Font management functions - static bool SVGRegisterFont(lua_State* luaVM, std::string fontFamily, std::string fontPath); - static bool SVGUnregisterFont(std::string fontFamily); - private: static bool LoadFromData(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, std::string rawData); static bool LoadFromFile(lua_State* luaVM, CClientVectorGraphic* vectorGraphic, CScriptFile* file, std::string path, CResource* parentResource); From fbfd66c6c570ee49d9eb5e5770170c519859d9ef Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Thu, 8 Jan 2026 05:08:52 +0000 Subject: [PATCH 6/7] Re-introduce sanity checks --- Client/mods/deathmatch/logic/CClientVectorGraphic.cpp | 4 ++++ Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp index 2e3dcb675e5..089f8481f07 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphic.cpp @@ -54,6 +54,10 @@ bool CClientVectorGraphic::SetDocument(CXMLNode* node) m_pXMLDocument = node; m_pSVGDocument = lunasvg::Document::loadFromData(node->ToString()); + // Check if LunaSVG successfully parsed the document + if (!m_pSVGDocument) + return false; + m_pVectorGraphicDisplay->Update(); return true; diff --git a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp index bcdd75ee3e6..29c08ebe045 100644 --- a/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp +++ b/Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp @@ -92,6 +92,10 @@ void CClientVectorGraphicDisplay::UpdateTexture() if (!surface) return; + // Check for valid SVG dimensions to avoid division by zero + if (svgDocument->width() <= 0 || svgDocument->height() <= 0) + return; + // SVG has a predefined width and height. We need transform it to the requested size const Matrix transformationMatrix(pVectorGraphicItem->m_uiSizeX / svgDocument->width(), 0, 0, pVectorGraphicItem->m_uiSizeY / svgDocument->height(), 0, 0); From d2328fabcbc7e80734cce08cded744eabc6383b9 Mon Sep 17 00:00:00 2001 From: lopsi <40902730+Lpsd@users.noreply.github.com> Date: Wed, 14 Jan 2026 06:08:28 +0000 Subject: [PATCH 7/7] Update lunasvg vendor to 0dd60d1 --- vendor/lunasvg/include/lunasvg.h | 2 +- vendor/lunasvg/plutovg/include/plutovg.h | 2 +- vendor/lunasvg/plutovg/source/plutovg-blend.c | 102 ++++++++++++------ vendor/lunasvg/plutovg/source/plutovg-font.c | 7 +- .../lunasvg/plutovg/source/plutovg-ft-math.c | 3 - .../plutovg/source/plutovg-ft-raster.c | 3 - .../lunasvg/plutovg/source/plutovg-ft-types.h | 3 + vendor/lunasvg/source/svglayoutstate.cpp | 2 +- vendor/lunasvg/source/svglayoutstate.h | 4 +- vendor/lunasvg/source/svgpaintelement.cpp | 3 +- vendor/lunasvg/source/svgparser.cpp | 18 +++- vendor/lunasvg/source/svgtextelement.cpp | 2 +- 12 files changed, 101 insertions(+), 50 deletions(-) diff --git a/vendor/lunasvg/include/lunasvg.h b/vendor/lunasvg/include/lunasvg.h index d869eeeee9d..cf871adb9f6 100644 --- a/vendor/lunasvg/include/lunasvg.h +++ b/vendor/lunasvg/include/lunasvg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Samuel Ugochukwu + * 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 diff --git a/vendor/lunasvg/plutovg/include/plutovg.h b/vendor/lunasvg/plutovg/include/plutovg.h index 4d0bf20d0d4..c34d0c08946 100644 --- a/vendor/lunasvg/plutovg/include/plutovg.h +++ b/vendor/lunasvg/plutovg/include/plutovg.h @@ -51,7 +51,7 @@ extern "C" { #define PLUTOVG_VERSION_MAJOR 1 #define PLUTOVG_VERSION_MINOR 3 -#define PLUTOVG_VERSION_MICRO 1 +#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) diff --git a/vendor/lunasvg/plutovg/source/plutovg-blend.c b/vendor/lunasvg/plutovg/source/plutovg-blend.c index e36b79dae79..892c0b8847f 100644 --- a/vendor/lunasvg/plutovg/source/plutovg-blend.c +++ b/vendor/lunasvg/plutovg/source/plutovg-blend.c @@ -55,11 +55,12 @@ static inline uint32_t premultiply_color_with_opacity(const plutovg_color_t* col return (alpha << 24) | (pr << 16) | (pg << 8) | (pb); } -static inline uint32_t INTERPOLATE_PIXEL(uint32_t x, uint32_t a, uint32_t y, uint32_t b) +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; @@ -67,11 +68,24 @@ static inline uint32_t INTERPOLATE_PIXEL(uint32_t x, uint32_t a, uint32_t y, uin 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; @@ -346,7 +360,7 @@ static void composition_solid_source_in(uint32_t* dest, int length, uint32_t col uint32_t cia = 255 - const_alpha; for(int i = 0; i < length; i++) { uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(color, plutovg_alpha(d), d, cia); + dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(d), d, cia); } } } @@ -372,7 +386,7 @@ static void composition_solid_source_out(uint32_t* dest, int length, uint32_t co uint32_t cia = 255 - const_alpha; for(int i = 0; i < length; i++) { uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(color, plutovg_alpha(~d), d, cia); + dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(~d), d, cia); } } } @@ -394,7 +408,7 @@ static void composition_solid_source_atop(uint32_t* dest, int length, uint32_t c uint32_t sia = plutovg_alpha(~color); for(int i = 0; i < length; i++) { uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(color, plutovg_alpha(d), d, sia); + dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(d), d, sia); } } @@ -408,7 +422,7 @@ static void composition_solid_destination_atop(uint32_t* dest, int length, uint3 for(int i = 0; i < length; i++) { uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(d, a, color, plutovg_alpha(~d)); + dest[i] = INTERPOLATE_PIXEL_255(d, a, color, plutovg_alpha(~d)); } } @@ -419,7 +433,7 @@ static void composition_solid_xor(uint32_t* dest, int length, uint32_t color, ui uint32_t sia = plutovg_alpha(~color); for(int i = 0; i < length; i++) { uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(color, plutovg_alpha(~d), d, sia); + dest[i] = INTERPOLATE_PIXEL_255(color, plutovg_alpha(~d), d, sia); } } @@ -459,7 +473,7 @@ static void composition_source(uint32_t* dest, int length, const uint32_t* src, } else { uint32_t ialpha = 255 - const_alpha; for(int i = 0; i < length; i++) { - dest[i] = INTERPOLATE_PIXEL(src[i], const_alpha, dest[i], ialpha); + dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha); } } } @@ -475,7 +489,7 @@ static void composition_source_over(uint32_t* dest, int length, const uint32_t* uint32_t s = src[i]; if(s >= 0xff000000) { dest[i] = s; - } else if (s != 0) { + } else if(s != 0) { dest[i] = s + BYTE_MUL(dest[i], plutovg_alpha(~s)); } } @@ -514,7 +528,7 @@ static void composition_source_in(uint32_t* dest, int length, const uint32_t* sr 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(s, plutovg_alpha(d), d, cia); + dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(d), d, cia); } } } @@ -545,7 +559,7 @@ static void composition_source_out(uint32_t* dest, int length, const uint32_t* s 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(s, plutovg_alpha(~d), d, cia); + dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(~d), d, cia); } } } @@ -571,13 +585,13 @@ static void composition_source_atop(uint32_t* dest, int length, const uint32_t* for(int i = 0; i < length; i++) { uint32_t s = src[i]; uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(s, plutovg_alpha(d), d, plutovg_alpha(~s)); + 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(s, plutovg_alpha(d), d, plutovg_alpha(~s)); + dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(d), d, plutovg_alpha(~s)); } } } @@ -588,7 +602,7 @@ static void composition_destination_atop(uint32_t* dest, int length, const uint3 for(int i = 0; i < length; i++) { uint32_t s = src[i]; uint32_t d = dest[i]; - dest[i] = INTERPOLATE_PIXEL(d, plutovg_alpha(s), s, plutovg_alpha(~d)); + dest[i] = INTERPOLATE_PIXEL_255(d, plutovg_alpha(s), s, plutovg_alpha(~d)); } } else { uint32_t cia = 255 - const_alpha; @@ -596,7 +610,7 @@ static void composition_destination_atop(uint32_t* dest, int length, const uint3 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(d, a, s, plutovg_alpha(~d)); + dest[i] = INTERPOLATE_PIXEL_255(d, a, s, plutovg_alpha(~d)); } } } @@ -607,13 +621,13 @@ static void composition_xor(uint32_t* dest, int length, const uint32_t* src, uin for(int i = 0; i < length; i++) { uint32_t d = dest[i]; uint32_t s = src[i]; - dest[i] = INTERPOLATE_PIXEL(s, plutovg_alpha(~d), d, plutovg_alpha(~s)); + 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(s, plutovg_alpha(~d), d, plutovg_alpha(~s)); + dest[i] = INTERPOLATE_PIXEL_255(s, plutovg_alpha(~d), d, plutovg_alpha(~s)); } } } @@ -852,6 +866,16 @@ static void blend_untransformed_tiled_argb(plutovg_surface_t* surface, plutovg_o } } +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_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]; @@ -859,7 +883,6 @@ static void blend_transformed_tiled_argb(plutovg_surface_t* surface, plutovg_ope 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); @@ -868,13 +891,15 @@ static void blend_transformed_tiled_argb(plutovg_surface_t* surface, plutovg_ope 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); + 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; @@ -882,21 +907,30 @@ static void blend_transformed_tiled_argb(plutovg_surface_t* surface, plutovg_ope 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; + while (b < end) { + int x1 = (fx >> 16) % image_width; + int y1 = (fy >> 16) % image_height; - assert(px >= 0 && px < image_width); - assert(py >= 0 && py < image_height); + if(x1 < 0) x1 += image_width; + if(y1 < 0) y1 += image_height; - *b = image_bits[y_offset + px]; - x += fdx; - y += fdy; + 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; } @@ -965,7 +999,7 @@ static void plutovg_blend_gradient(plutovg_canvas_t* canvas, const plutovg_gradi t = (fpos - curr->offset) * delta; dist = (uint32_t)(255 * t); idist = 255 - dist; - data.colortable[pos] = INTERPOLATE_PIXEL(curr_color, idist, next_color, dist); + data.colortable[pos] = INTERPOLATE_PIXEL_255(curr_color, idist, next_color, dist); ++pos; fpos += incr; } diff --git a/vendor/lunasvg/plutovg/source/plutovg-font.c b/vendor/lunasvg/plutovg/source/plutovg-font.c index 3dae6f9c8ee..0ad89b1ac4b 100644 --- a/vendor/lunasvg/plutovg/source/plutovg-font.c +++ b/vendor/lunasvg/plutovg/source/plutovg-font.c @@ -125,7 +125,7 @@ typedef CRITICAL_SECTION plutovg_mutex_t; typedef mtx_t plutovg_mutex_t; -#define plutovg_mutex_init(mutex) mtx_init(mutex, mtx_recursive) +#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) @@ -696,7 +696,12 @@ plutovg_font_face_t* plutovg_font_face_cache_get(plutovg_font_face_cache_t* cach #include #include #include + +#ifdef __linux__ +#include +#else #include +#endif #include #include diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-math.c b/vendor/lunasvg/plutovg/source/plutovg-ft-math.c index 5d6089075f3..c4a1af8d79a 100644 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-math.c +++ b/vendor/lunasvg/plutovg/source/plutovg-ft-math.c @@ -46,9 +46,6 @@ static inline int clz(unsigned int x) { #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) -#define PVG_FT_BEGIN_STMNT do { -#define PVG_FT_END_STMNT } while (0) - /* transfer sign leaving a positive number */ #define PVG_FT_MOVE_SIGN(x, s) \ PVG_FT_BEGIN_STMNT \ diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c index 4f2f073a62c..d746b789e93 100644 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c +++ b/vendor/lunasvg/plutovg/source/plutovg-ft-raster.c @@ -58,9 +58,6 @@ #include "plutovg-ft-raster.h" #include "plutovg-ft-math.h" -#define PVG_FT_BEGIN_STMNT do { -#define PVG_FT_END_STMNT } while ( 0 ) - #include #define pvg_ft_setjmp setjmp diff --git a/vendor/lunasvg/plutovg/source/plutovg-ft-types.h b/vendor/lunasvg/plutovg/source/plutovg-ft-types.h index 06580d12ce2..4b3db9003b2 100644 --- a/vendor/lunasvg/plutovg/source/plutovg-ft-types.h +++ b/vendor/lunasvg/plutovg/source/plutovg-ft-types.h @@ -170,4 +170,7 @@ typedef unsigned int PVG_FT_UInt32; #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/source/svglayoutstate.cpp b/vendor/lunasvg/source/svglayoutstate.cpp index e7a295b28dd..24d583b67f3 100644 --- a/vendor/lunasvg/source/svglayoutstate.cpp +++ b/vendor/lunasvg/source/svglayoutstate.cpp @@ -483,7 +483,7 @@ SVGLayoutState::SVGLayoutState(const SVGLayoutState& parent, const SVGElement* e m_word_spacing = parseLengthOrNormal(input); break; case PropertyID::Baseline_Shift: - m_baseline_shit = parseBaselineShift(input); + m_baseline_shift = parseBaselineShift(input); break; case PropertyID::Stroke_Width: m_stroke_width = parseLength(input, LengthNegativeMode::Forbid, Length(1.f, LengthUnits::None)); diff --git a/vendor/lunasvg/source/svglayoutstate.h b/vendor/lunasvg/source/svglayoutstate.h index 1775835dd5c..4d08ad9ab9b 100644 --- a/vendor/lunasvg/source/svglayoutstate.h +++ b/vendor/lunasvg/source/svglayoutstate.h @@ -29,7 +29,7 @@ class SVGLayoutState { const Length& letter_spacing() const { return m_letter_spacing; } const Length& word_spacing() const { return m_word_spacing; } - const BaselineShift& baseline_shit() const { return m_baseline_shit; } + 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; } @@ -87,7 +87,7 @@ class SVGLayoutState { Length m_letter_spacing{0.f, LengthUnits::None}; Length m_word_spacing{0.f, LengthUnits::None}; - BaselineShift m_baseline_shit; + BaselineShift m_baseline_shift; Length m_stroke_width{1.f, LengthUnits::None}; Length m_stroke_dashoffset{0.f, LengthUnits::None}; LengthList m_stroke_dasharray; diff --git a/vendor/lunasvg/source/svgpaintelement.cpp b/vendor/lunasvg/source/svgpaintelement.cpp index 0c8f5d5a011..9432d6edf5f 100644 --- a/vendor/lunasvg/source/svgpaintelement.cpp +++ b/vendor/lunasvg/source/svgpaintelement.cpp @@ -2,6 +2,7 @@ #include "svglayoutstate.h" #include "svgrenderstate.h" +#include #include namespace lunasvg { @@ -308,7 +309,7 @@ bool SVGPatternElement::applyPaint(SVGRenderState& state, float opacity) const auto patternTransform = attributes.patternTransform(); patternTransform.translate(patternRect.x, patternRect.y); - patternTransform.scale(1.f / xScale, 1.f / yScale); + patternTransform.scale(patternRect.w / patternImage->width(), patternRect.h / patternImage->height()); state->setTexture(*patternImage, TextureType::Tiled, opacity, patternTransform); return true; } diff --git a/vendor/lunasvg/source/svgparser.cpp b/vendor/lunasvg/source/svgparser.cpp index 399fc9b2e63..fa465887a97 100644 --- a/vendor/lunasvg/source/svgparser.cpp +++ b/vendor/lunasvg/source/svgparser.cpp @@ -214,7 +214,7 @@ static bool matchPseudoClassSelector(const PseudoClassSelector& selector, const while(sibling) { if(sibling->id() == element->id()) return false; - sibling = element->previousElement(); + sibling = sibling->previousElement(); } return true; @@ -225,7 +225,7 @@ static bool matchPseudoClassSelector(const PseudoClassSelector& selector, const while(sibling) { if(sibling->id() == element->id()) return false; - sibling = element->nextElement(); + sibling = sibling->nextElement(); } return true; @@ -571,6 +571,9 @@ static RuleDataList parseStyleSheet(std::string_view input) 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()); @@ -738,6 +741,17 @@ bool Document::parse(const char* data, size_t length) }; 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('<')); diff --git a/vendor/lunasvg/source/svgtextelement.cpp b/vendor/lunasvg/source/svgtextelement.cpp index f62280b5e89..6d43e57dd78 100644 --- a/vendor/lunasvg/source/svgtextelement.cpp +++ b/vendor/lunasvg/source/svgtextelement.cpp @@ -475,7 +475,7 @@ void SVGTextPositioningElement::layoutElement(const SVGLayoutState& state) 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_shit()); + 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();