From 00e4f7678da1bfe783dbe595145093a4a5028336 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Mon, 28 Sep 2026 16:44:52 +1300 Subject: [PATCH] feat(font): port shadPS4's sceFont and sceFontFt on FreeType Ported from shadPS4 (GPL-2.0-or-later) with system fonts removed, stubs made to throw, and sceFontCloseFont implemented. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ --- core/libs/CMakeLists.txt | 8 + core/libs/SceTypes.hpp | 201 ----- .../libs/prx/libSceFont.native/CMakeLists.txt | 2 + core/libs/prx/libSceFont.native/Export.cpp | 520 ------------- core/libs/prx/libSceFont/CMakeLists.txt | 2 + core/libs/prx/libSceFont/Export.cpp | 520 ------------- core/libs/prx/libSceFont/FontLibrary.cmake | 23 + .../prx/libSceFont/include/FontDriver.hpp | 360 +++++++++ .../prx/libSceFont/include/FontFreeType.hpp | 151 ++++ .../prx/libSceFont/include/FontInternal.hpp | 112 +++ .../libs/prx/libSceFont/include/FontTypes.hpp | 432 +++++++++++ core/libs/prx/libSceFont/src/Layout.cpp | 522 +++++++++++++ core/libs/prx/libSceFont/src/Library.cpp | 378 ++++++++++ core/libs/prx/libSceFont/src/Open.cpp | 509 +++++++++++++ core/libs/prx/libSceFont/src/Render.cpp | 449 +++++++++++ core/libs/prx/libSceFont/src/State.cpp | 433 +++++++++++ core/libs/prx/libSceFont/src/Style.cpp | 303 ++++++++ core/libs/prx/libSceFont/src/Text.cpp | 633 ++++++++++++++++ .../libs/prx/libSceFont/src/Unimplemented.cpp | 477 ++++++++++++ core/libs/prx/libSceFontFt/CMakeLists.txt | 15 + core/libs/prx/libSceFontFt/Export.cpp | 118 ++- .../prx/libSceFontFt/include/FontFtDriver.hpp | 16 + core/libs/prx/libSceFontFt/src/Driver.cpp | 711 ++++++++++++++++++ core/libs/tests/GuestFont.cpp | 75 ++ 24 files changed, 5718 insertions(+), 1252 deletions(-) create mode 100644 core/libs/prx/libSceFont.native/CMakeLists.txt delete mode 100644 core/libs/prx/libSceFont.native/Export.cpp create mode 100644 core/libs/prx/libSceFont/CMakeLists.txt delete mode 100644 core/libs/prx/libSceFont/Export.cpp create mode 100644 core/libs/prx/libSceFont/FontLibrary.cmake create mode 100644 core/libs/prx/libSceFont/include/FontDriver.hpp create mode 100644 core/libs/prx/libSceFont/include/FontFreeType.hpp create mode 100644 core/libs/prx/libSceFont/include/FontInternal.hpp create mode 100644 core/libs/prx/libSceFont/include/FontTypes.hpp create mode 100644 core/libs/prx/libSceFont/src/Layout.cpp create mode 100644 core/libs/prx/libSceFont/src/Library.cpp create mode 100644 core/libs/prx/libSceFont/src/Open.cpp create mode 100644 core/libs/prx/libSceFont/src/Render.cpp create mode 100644 core/libs/prx/libSceFont/src/State.cpp create mode 100644 core/libs/prx/libSceFont/src/Style.cpp create mode 100644 core/libs/prx/libSceFont/src/Text.cpp create mode 100644 core/libs/prx/libSceFont/src/Unimplemented.cpp create mode 100644 core/libs/prx/libSceFontFt/CMakeLists.txt create mode 100644 core/libs/prx/libSceFontFt/include/FontFtDriver.hpp create mode 100644 core/libs/prx/libSceFontFt/src/Driver.cpp create mode 100644 core/libs/tests/GuestFont.cpp diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index b0572b5bf..c5bc6746d 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -310,6 +310,14 @@ if(BUILD_TESTING) set_tests_properties(guest_rtc PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_executable(guest_font_tests tests/GuestFont.cpp) + target_include_directories(guest_font_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_font_tests PRIVATE libSceFont libSceFontFt libc) + add_test(NAME guest_font COMMAND guest_font_tests) + if(WIN32) + set_tests_properties(guest_font PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_executable(mspace_tests tests/Mspace.cpp) target_include_directories(mspace_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(mspace_tests PRIVATE libc) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 51b0a9caf..2328a171c 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1150,207 +1150,6 @@ struct PositionAndForm { std::uint32_t height; }; -using FontHandle = void*; -using FontLibrary = void*; -using FontLibrarySelection = void*; -using FontRenderer = void*; -using FontRendererSelection = void*; - -using FontMemoryDestroyCallback = void (*)(void*); - -struct FontMemoryInterface { - void* (*malloc_func)(std::size_t); - void (*free_func)(void*); -}; - -struct FontMemory { - std::uint16_t type; - std::uint16_t attr; - std::uint32_t size; - void* address; - void* mspace_object; - const FontMemoryInterface* mem_interface; - FontMemoryDestroyCallback destroy_callback; - void* destroy_object; - void* user_object; - void* parent_object; -}; - -using FontOpenDetail = void; - -struct FontHorizontalLayout { - float base_line_y; - float line_height; - float effect_height; -}; - -struct FontVerticalLayout { - float base_line_x; - float line_width; - float effect_width; -}; - -struct FontGlyphMetrics { - float width; - float height; - struct { - float bearing_x; - float bearing_y; - float advance; - } horizontal; - struct { - float bearing_x; - float bearing_y; - float advance; - } vertical; -}; - -struct FontRenderSurface { - void* buffer; - std::int32_t width_byte; - std::int8_t pixel_size_byte; - std::uint8_t system_ext0; - std::uint8_t system_ext1; - std::uint8_t system_ext2; - std::int32_t width; - std::int32_t height; - struct { - std::uint32_t x0; - std::uint32_t y0; - std::uint32_t x1; - std::uint32_t y1; - } scissor; - std::uint32_t system_use[22]; -}; - -struct FontRenderCharacter { std::uint8_t reserved[128]; }; - -struct FontTransImage { - std::uint8_t* address; - std::uint32_t width_byte; - std::uint32_t image_width; - std::uint32_t image_height; -}; - -struct FontSurfaceImage { - std::uint8_t* address; - std::uint32_t width_byte; - std::uint8_t pixel_size_byte; - std::uint8_t pixel_format; -}; - -struct FontGlyphImageMetrics { - float bearing_x; - float bearing_y; - float advance; - float stride; - std::uint32_t width; - std::uint32_t height; -}; - -struct FontRenderResult { - const FontTransImage* trans_image; - FontSurfaceImage surface_image; - struct { - std::uint32_t x; - std::uint32_t y; - std::uint32_t w; - std::uint32_t h; - } update_rect; - FontGlyphImageMetrics image_metrics; -}; - -using FontCreateStringOrdersFunction = void (*)(void*, void*); - -struct FontCreateStringDetail { - std::uint16_t detail_id; - std::uint8_t detail_type; - std::uint8_t detections; - std::uint32_t orders_option; - FontHandle default_font; - struct { - FontCreateStringOrdersFunction function; - void* object; - } orders; -}; - -struct FontGenerateGlyphDetail { std::uint8_t reserved[64]; }; - -struct FontString { void* system_use[32]; }; - -struct FontWriting { void* system_use[32]; }; - -struct FontWritingExtent { - float top; - float bottom; - float left; - float right; -}; - -struct FontWritingMetrics { - float advance_x; - float advance_y; - FontWritingExtent extent; -}; - -struct FontWritingStep { - float x; - float y; - float advance_x; - float advance_y; - FontHandle font; - struct { - std::uint32_t character_count : 8; - std::uint32_t invisible_glyph : 1; - } profile; - std::uint32_t glyph_code; - struct { - float x; - float y; - } positioning; - FontGlyphMetrics glyph_metrics; -}; - -struct FontWritingLine { std::uint8_t reserved[128]; }; - -struct FontWritingLineStep { - float x; - float y; - float advance_x; - float advance_y; - float spacing_progress; - void* writing_orderer; - struct { - float x; - float y; - } adjusting; - FontWritingMetrics metrics; -}; - -using FontTextParseFunction = int (*)(void*, std::uint32_t, void*); - -struct FontTextSource { - std::uint64_t system_use0; - const void* start; - const void* end; - const void* current; - FontTextParseFunction text_parser; - void* text_object; - FontHandle default_font; - void* system_use[5]; -}; - -struct FontTextCharacter { std::uint8_t reserved[64]; }; - -union FontTextCodes { - struct { - void* order; - std::uint32_t code; - std::uint32_t reserved; - } text; - void* system_reserved[8]; -}; - struct NetEtherAddr { std::uint8_t data[6]; }; union NetEpollData { diff --git a/core/libs/prx/libSceFont.native/CMakeLists.txt b/core/libs/prx/libSceFont.native/CMakeLists.txt new file mode 100644 index 000000000..af4d2df01 --- /dev/null +++ b/core/libs/prx/libSceFont.native/CMakeLists.txt @@ -0,0 +1,2 @@ +include(${CMAKE_CURRENT_SOURCE_DIR}/../libSceFont/FontLibrary.cmake) +add_sce_font_library(libSceFont.native) diff --git a/core/libs/prx/libSceFont.native/Export.cpp b/core/libs/prx/libSceFont.native/Export.cpp deleted file mode 100644 index 33f3d0fa4..000000000 --- a/core/libs/prx/libSceFont.native/Export.cpp +++ /dev/null @@ -1,520 +0,0 @@ -#include -#include -#include "SceTypes.hpp" -#include "prx/libc/include/General.hpp" - -extern "C" { - -int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, uint32_t size) { - (void)library; - (void)buffer; - (void)size; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontBindRenderer(FontHandle font_handle, FontRenderer renderer) { - (void)font_handle; - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetBidiLevel(FontTextCharacter text_character, int* bidi_level) { - (void)text_character; - (void)bidi_level; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetSyllableStringState(FontTextCharacter text_character, int* syllable_string_state) { - (void)text_character; - (void)syllable_string_state; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetTextFontCode(FontTextCharacter text_character, FontHandle* font_handle, uint32_t* text_code) { - (void)text_character; - (void)font_handle; - (void)text_code; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetTextOrder(FontTextCharacter text_character, void** text_order) { - (void)text_character; - (void)text_order; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontTextCharacter APS5_VABI sceFontCharacterRefersTextBack(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCharacter APS5_VABI sceFontCharacterRefersTextNext(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(FontTextCharacter text_character, FontTextCharacter term_character, FontTextCodes* text_codes) { - (void)text_character; - (void)term_character; - (void)text_codes; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontCloseFont(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, FontLibrarySelection selection, FontLibrary* library) { - (void)memory; - (void)selection; - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, FontLibrarySelection selection, uint64_t edition, FontLibrary* library) { - (void)memory; - (void)selection; - (void)edition; - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, FontRendererSelection selection, uint64_t edition, FontRenderer* renderer) { - (void)memory; - (void)selection; - (void)edition; - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateString(const FontMemory* memory, FontTextSource* font_text_source, const FontCreateStringDetail* string_detail, FontString* font_string) { - (void)memory; - (void)font_text_source; - (void)string_detail; - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateWritingLine(const FontMemory* memory, int writing_form, const void* writing_line_detail, FontWritingLine* writing_line) { - (void)memory; - (void)writing_form; - (void)writing_line_detail; - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDefineAttribute(FontHandle font_handle, int attribute, int* old_attribute) { - (void)font_handle; - (void)attribute; - (void)old_attribute; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, void** font_glyph) { - (void)memory; - (void)font_glyph; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyLibrary(FontLibrary* library) { - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyRenderer(FontRenderer* renderer) { - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyString(FontString* font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyWritingLine(FontWritingLine* writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGenerateCharGlyph(FontHandle font_handle, uint32_t code, const FontGenerateGlyphDetail* detail, void** font_glyph) { - (void)font_handle; - (void)code; - (void)detail; - (void)font_glyph; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) { - (void)font_handle; - (void)code; - (void)metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetHorizontalLayout(FontHandle font_handle, FontHorizontalLayout* layout) { - (void)font_handle; - (void)layout; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) { - (void)font_handle; - (void)code; - (void)metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetVerticalLayout(FontHandle font_handle, FontVerticalLayout* layout) { - (void)font_handle; - (void)layout; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGlyphDefineAttribute(void* font_glyph, int attribute, int* old_attribute) { - (void)font_glyph; - (void)attribute; - (void)old_attribute; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontMemoryInit(FontMemory* font_memory, void* address, uint32_t size_byte, const FontMemoryInterface* mem_interface, void* mspace_object, FontMemoryDestroyCallback destroy_callback, void* destroy_object) { - (void)font_memory; - (void)address; - (void)size_byte; - (void)mem_interface; - (void)mspace_object; - (void)destroy_callback; - (void)destroy_object; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontMemoryTerm(FontMemory* font_memory) { - (void)font_memory; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontInstance(FontHandle font_handle, void* setup_font, FontHandle* out_font_handle) { - (void)font_handle; - (void)setup_font; - (void)out_font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* font_address, uint32_t font_size, const FontOpenDetail* detail, FontHandle* handle) { - (void)library; - (void)font_address; - (void)font_size; - (void)detail; - (void)handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontSet(FontLibrary library, uint32_t font_set_type, uint32_t open_mode, const FontOpenDetail* detail, FontHandle* handle) { - (void)library; - (void)font_set_type; - (void)open_mode; - (void)detail; - (void)handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontRebindRenderer(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle font_handle, uint32_t code, FontRenderSurface* surf, float x, float y, FontGlyphMetrics* metrics, FontRenderResult* result) { - (void)font_handle; - (void)code; - (void)surf; - (void)x; - (void)y; - (void)metrics; - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* surf, void* buffer, int buf_width_byte, int pixel_size_byte, int width, int height) { - (void)surf; - (void)buffer; - (void)buf_width_byte; - (void)pixel_size_byte; - (void)width; - (void)height; - NotImplemented_nid_no_patch(__func__); -} - -void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* surf, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1) { - (void)surf; - (void)x0; - (void)y0; - (void)x1; - (void)y1; - NotImplemented_nid_no_patch(__func__); -} - -int APS5_VABI sceFontSetEffectSlant(FontHandle font_handle, float slant_ratio) { - (void)font_handle; - (void)slant_ratio; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) { - (void)font_handle; - (void)weight_x_scale; - (void)weight_y_scale; - (void)mode; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetScalePixel(FontHandle font_handle, float w, float h) { - (void)font_handle; - (void)w; - (void)h; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle font_handle, float slant_ratio) { - (void)font_handle; - (void)slant_ratio; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) { - (void)font_handle; - (void)weight_x_scale; - (void)weight_y_scale; - (void)mode; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderScalePixel(FontHandle font_handle, float w, float h) { - (void)font_handle; - (void)w; - (void)h; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -void* APS5_VABI sceFontStringGetTerminateOrder(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontStringGetWritingForm(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontRenderCharacter APS5_VABI sceFontStringRefersRenderCharacters(FontString font_string, FontTextCharacter start_character, FontTextCharacter last_character, uint32_t* character_count) { - (void)font_string; - (void)start_character; - (void)last_character; - (void)character_count; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCharacter APS5_VABI sceFontStringRefersTextCharacters(FontString font_string, uint32_t* character_count) { - (void)font_string; - (void)character_count; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, uint32_t font_max, uint32_t formats) { - (void)library; - (void)font_max; - (void)formats; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) { - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontTextCodes* APS5_VABI sceFontTextCodesStepBack(FontTextCodes* text_codes_step) { - (void)text_codes_step; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes* text_codes_step) { - (void)text_codes_step; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontTextSourceInit(FontTextSource* font_text_source, const void* text_address, uint32_t text_size_byte, FontTextParseFunction text_parser, void* text_object) { - (void)font_text_source; - (void)text_address; - (void)text_size_byte; - (void)text_parser; - (void)text_object; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceRewind(FontTextSource* font_text_source) { - (void)font_text_source; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* font_text_source, FontHandle default_font) { - (void)font_text_source; - (void)default_font; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* font_text_source, int writing_form) { - (void)font_text_source; - (void)writing_form; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontUnbindRenderer(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* font_writing, FontWritingMetrics* writing_metrics) { - (void)font_writing; - (void)writing_metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingInit(FontWriting* font_writing, FontString font_string, FontRenderCharacter font_character) { - (void)font_writing; - (void)font_string; - (void)font_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineClear(FontWritingLine writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineGetOrderingSpace(FontWritingLine writing_line, float* head_space, float* inline_space, float* tail_space, float* advance_space) { - (void)writing_line; - (void)head_space; - (void)inline_space; - (void)tail_space; - (void)advance_space; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineGetRenderMetrics(FontWritingLine writing_line, FontWritingMetrics* writing_metrics) { - (void)writing_line; - (void)writing_metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(FontWritingLine writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontWritingLineWritesOrder(FontWritingLine writing_line, uint64_t writing_attribute, const FontWritingMetrics* writing_metrics, void* writing_orderer) { - (void)writing_line; - (void)writing_attribute; - (void)writing_metrics; - (void)writing_orderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* font_writing) { - (void)font_writing; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -FontTextCharacter APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* font_writing, const void** letter_step) { - (void)font_writing; - (void)letter_step; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* font_writing, int mask) { - (void)font_writing; - (void)mask; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -} diff --git a/core/libs/prx/libSceFont/CMakeLists.txt b/core/libs/prx/libSceFont/CMakeLists.txt new file mode 100644 index 000000000..ccbfe95e4 --- /dev/null +++ b/core/libs/prx/libSceFont/CMakeLists.txt @@ -0,0 +1,2 @@ +include(${CMAKE_CURRENT_SOURCE_DIR}/FontLibrary.cmake) +add_sce_font_library(libSceFont) diff --git a/core/libs/prx/libSceFont/Export.cpp b/core/libs/prx/libSceFont/Export.cpp deleted file mode 100644 index 33f3d0fa4..000000000 --- a/core/libs/prx/libSceFont/Export.cpp +++ /dev/null @@ -1,520 +0,0 @@ -#include -#include -#include "SceTypes.hpp" -#include "prx/libc/include/General.hpp" - -extern "C" { - -int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, uint32_t size) { - (void)library; - (void)buffer; - (void)size; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontBindRenderer(FontHandle font_handle, FontRenderer renderer) { - (void)font_handle; - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetBidiLevel(FontTextCharacter text_character, int* bidi_level) { - (void)text_character; - (void)bidi_level; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetSyllableStringState(FontTextCharacter text_character, int* syllable_string_state) { - (void)text_character; - (void)syllable_string_state; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetTextFontCode(FontTextCharacter text_character, FontHandle* font_handle, uint32_t* text_code) { - (void)text_character; - (void)font_handle; - (void)text_code; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCharacterGetTextOrder(FontTextCharacter text_character, void** text_order) { - (void)text_character; - (void)text_order; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontTextCharacter APS5_VABI sceFontCharacterRefersTextBack(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCharacter APS5_VABI sceFontCharacterRefersTextNext(FontTextCharacter text_character) { - (void)text_character; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(FontTextCharacter text_character, FontTextCharacter term_character, FontTextCodes* text_codes) { - (void)text_character; - (void)term_character; - (void)text_codes; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontCloseFont(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, FontLibrarySelection selection, FontLibrary* library) { - (void)memory; - (void)selection; - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, FontLibrarySelection selection, uint64_t edition, FontLibrary* library) { - (void)memory; - (void)selection; - (void)edition; - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, FontRendererSelection selection, uint64_t edition, FontRenderer* renderer) { - (void)memory; - (void)selection; - (void)edition; - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateString(const FontMemory* memory, FontTextSource* font_text_source, const FontCreateStringDetail* string_detail, FontString* font_string) { - (void)memory; - (void)font_text_source; - (void)string_detail; - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontCreateWritingLine(const FontMemory* memory, int writing_form, const void* writing_line_detail, FontWritingLine* writing_line) { - (void)memory; - (void)writing_form; - (void)writing_line_detail; - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDefineAttribute(FontHandle font_handle, int attribute, int* old_attribute) { - (void)font_handle; - (void)attribute; - (void)old_attribute; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, void** font_glyph) { - (void)memory; - (void)font_glyph; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyLibrary(FontLibrary* library) { - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyRenderer(FontRenderer* renderer) { - (void)renderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyString(FontString* font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontDestroyWritingLine(FontWritingLine* writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGenerateCharGlyph(FontHandle font_handle, uint32_t code, const FontGenerateGlyphDetail* detail, void** font_glyph) { - (void)font_handle; - (void)code; - (void)detail; - (void)font_glyph; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) { - (void)font_handle; - (void)code; - (void)metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetHorizontalLayout(FontHandle font_handle, FontHorizontalLayout* layout) { - (void)font_handle; - (void)layout; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) { - (void)font_handle; - (void)code; - (void)metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGetVerticalLayout(FontHandle font_handle, FontVerticalLayout* layout) { - (void)font_handle; - (void)layout; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGlyphDefineAttribute(void* font_glyph, int attribute, int* old_attribute) { - (void)font_glyph; - (void)attribute; - (void)old_attribute; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontMemoryInit(FontMemory* font_memory, void* address, uint32_t size_byte, const FontMemoryInterface* mem_interface, void* mspace_object, FontMemoryDestroyCallback destroy_callback, void* destroy_object) { - (void)font_memory; - (void)address; - (void)size_byte; - (void)mem_interface; - (void)mspace_object; - (void)destroy_callback; - (void)destroy_object; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontMemoryTerm(FontMemory* font_memory) { - (void)font_memory; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontInstance(FontHandle font_handle, void* setup_font, FontHandle* out_font_handle) { - (void)font_handle; - (void)setup_font; - (void)out_font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* font_address, uint32_t font_size, const FontOpenDetail* detail, FontHandle* handle) { - (void)library; - (void)font_address; - (void)font_size; - (void)detail; - (void)handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontOpenFontSet(FontLibrary library, uint32_t font_set_type, uint32_t open_mode, const FontOpenDetail* detail, FontHandle* handle) { - (void)library; - (void)font_set_type; - (void)open_mode; - (void)detail; - (void)handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontRebindRenderer(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle font_handle, uint32_t code, FontRenderSurface* surf, float x, float y, FontGlyphMetrics* metrics, FontRenderResult* result) { - (void)font_handle; - (void)code; - (void)surf; - (void)x; - (void)y; - (void)metrics; - (void)result; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* surf, void* buffer, int buf_width_byte, int pixel_size_byte, int width, int height) { - (void)surf; - (void)buffer; - (void)buf_width_byte; - (void)pixel_size_byte; - (void)width; - (void)height; - NotImplemented_nid_no_patch(__func__); -} - -void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* surf, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1) { - (void)surf; - (void)x0; - (void)y0; - (void)x1; - (void)y1; - NotImplemented_nid_no_patch(__func__); -} - -int APS5_VABI sceFontSetEffectSlant(FontHandle font_handle, float slant_ratio) { - (void)font_handle; - (void)slant_ratio; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) { - (void)font_handle; - (void)weight_x_scale; - (void)weight_y_scale; - (void)mode; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetScalePixel(FontHandle font_handle, float w, float h) { - (void)font_handle; - (void)w; - (void)h; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle font_handle, float slant_ratio) { - (void)font_handle; - (void)slant_ratio; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) { - (void)font_handle; - (void)weight_x_scale; - (void)weight_y_scale; - (void)mode; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSetupRenderScalePixel(FontHandle font_handle, float w, float h) { - (void)font_handle; - (void)w; - (void)h; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -void* APS5_VABI sceFontStringGetTerminateOrder(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontStringGetWritingForm(FontString font_string) { - (void)font_string; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontRenderCharacter APS5_VABI sceFontStringRefersRenderCharacters(FontString font_string, FontTextCharacter start_character, FontTextCharacter last_character, uint32_t* character_count) { - (void)font_string; - (void)start_character; - (void)last_character; - (void)character_count; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -FontTextCharacter APS5_VABI sceFontStringRefersTextCharacters(FontString font_string, uint32_t* character_count) { - (void)font_string; - (void)character_count; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, uint32_t font_max, uint32_t formats) { - (void)library; - (void)font_max; - (void)formats; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) { - (void)library; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontTextCodes* APS5_VABI sceFontTextCodesStepBack(FontTextCodes* text_codes_step) { - (void)text_codes_step; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes* text_codes_step) { - (void)text_codes_step; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontTextSourceInit(FontTextSource* font_text_source, const void* text_address, uint32_t text_size_byte, FontTextParseFunction text_parser, void* text_object) { - (void)font_text_source; - (void)text_address; - (void)text_size_byte; - (void)text_parser; - (void)text_object; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceRewind(FontTextSource* font_text_source) { - (void)font_text_source; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* font_text_source, FontHandle default_font) { - (void)font_text_source; - (void)default_font; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* font_text_source, int writing_form) { - (void)font_text_source; - (void)writing_form; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontUnbindRenderer(FontHandle font_handle) { - (void)font_handle; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* font_writing, FontWritingMetrics* writing_metrics) { - (void)font_writing; - (void)writing_metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingInit(FontWriting* font_writing, FontString font_string, FontRenderCharacter font_character) { - (void)font_writing; - (void)font_string; - (void)font_character; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineClear(FontWritingLine writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineGetOrderingSpace(FontWritingLine writing_line, float* head_space, float* inline_space, float* tail_space, float* advance_space) { - (void)writing_line; - (void)head_space; - (void)inline_space; - (void)tail_space; - (void)advance_space; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontWritingLineGetRenderMetrics(FontWritingLine writing_line, FontWritingMetrics* writing_metrics) { - (void)writing_line; - (void)writing_metrics; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(FontWritingLine writing_line) { - (void)writing_line; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -int APS5_VABI sceFontWritingLineWritesOrder(FontWritingLine writing_line, uint64_t writing_attribute, const FontWritingMetrics* writing_metrics, void* writing_orderer) { - (void)writing_line; - (void)writing_attribute; - (void)writing_metrics; - (void)writing_orderer; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* font_writing) { - (void)font_writing; - NotImplemented_nid_no_patch(__func__); - return nullptr; -} - -FontTextCharacter APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* font_writing, const void** letter_step) { - (void)font_writing; - (void)letter_step; - NotImplemented_nid_no_patch(__func__); - return {}; -} - -int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* font_writing, int mask) { - (void)font_writing; - (void)mask; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -} diff --git a/core/libs/prx/libSceFont/FontLibrary.cmake b/core/libs/prx/libSceFont/FontLibrary.cmake new file mode 100644 index 000000000..36f76410a --- /dev/null +++ b/core/libs/prx/libSceFont/FontLibrary.cmake @@ -0,0 +1,23 @@ +find_package(Freetype REQUIRED) + +function(add_sce_font_library target) + set(fontDir ${CMAKE_CURRENT_FUNCTION_LIST_DIR}) + add_library(${target} SHARED EXCLUDE_FROM_ALL + ${fontDir}/src/Layout.cpp + ${fontDir}/src/Library.cpp + ${fontDir}/src/Open.cpp + ${fontDir}/src/Render.cpp + ${fontDir}/src/State.cpp + ${fontDir}/src/Style.cpp + ${fontDir}/src/Text.cpp + ${fontDir}/src/Unimplemented.cpp + ) + target_include_directories(${target} PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(${target} PRIVATE Freetype::Freetype libc) + set_target_properties(${target} PROPERTIES + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + ) + configure_windows_unwind(${target}) +endfunction() diff --git a/core/libs/prx/libSceFont/include/FontDriver.hpp b/core/libs/prx/libSceFont/include/FontDriver.hpp new file mode 100644 index 000000000..e58ae256e --- /dev/null +++ b/core/libs/prx/libSceFont/include/FontDriver.hpp @@ -0,0 +1,360 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTDRIVER_HPP +#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTDRIVER_HPP + +#include +#include +#include +#include + +#include "prx/libSceFont/include/FontTypes.hpp" + +namespace Font { + +constexpr std::uint16_t MEMORY_MAGIC = 0x0F00; +constexpr std::uint16_t LIBRARY_MAGIC = 0x0F01; +constexpr std::uint16_t HANDLE_MAGIC = 0x0F02; +constexpr std::uint16_t GLYPH_MAGIC = 0x0F03; +constexpr std::uint16_t RENDERER_MAGIC = 0x0F07; +constexpr std::uint16_t STYLE_FRAME_MAGIC = 0x0F09; + +constexpr std::uint32_t LOCK_BIT = 0x80000000u; +constexpr std::uint32_t OPEN_BIT = 0x40000000u; +constexpr std::uint32_t COUNT_MASK = 0x0FFFFFFFu; + +constexpr std::size_t HORIZONTAL_LAYOUT_SIZE = 0x30; +constexpr std::size_t HORIZONTAL_LINE_ADVANCE = 0x00; +constexpr std::size_t HORIZONTAL_BASELINE = 0x04; +constexpr std::size_t HORIZONTAL_X_BOUND_LO = 0x08; +constexpr std::size_t HORIZONTAL_X_BOUND_HI = 0x0C; +constexpr std::size_t HORIZONTAL_MAX_ADVANCE_WIDTH = 0x10; +constexpr std::size_t HORIZONTAL_CARET_RISE_ADJUST = 0x14; +constexpr std::size_t HORIZONTAL_EFFECT_HEIGHT = 0x18; +constexpr std::size_t HORIZONTAL_HALF_EFFECT_WIDTH = 0x1C; +constexpr std::size_t HORIZONTAL_LEFT_ADJUST = 0x20; + +constexpr std::size_t VERTICAL_LAYOUT_SIZE = 0x20; +constexpr std::size_t VERTICAL_COLUMN_ADVANCE = 0x00; +constexpr std::size_t VERTICAL_BASELINE_OFFSET_X = 0x04; +constexpr std::size_t VERTICAL_METRICS_0X08 = 0x08; +constexpr std::size_t VERTICAL_METRICS_0X0C = 0x0C; +constexpr std::size_t VERTICAL_ADVANCE_HEIGHT = 0x10; +constexpr std::size_t VERTICAL_DECORATION_SPAN = 0x14; +constexpr std::size_t VERTICAL_DECORATION_0X08 = 0x18; +constexpr std::size_t VERTICAL_DECORATION_0X0C = 0x1C; + +inline float LoadFloat(const std::uint8_t* base, std::size_t offset) { + float value; + std::memcpy(&value, base + offset, sizeof(value)); + return value; +} + +inline void StoreFloat(std::uint8_t* base, std::size_t offset, float value) { + std::memcpy(base + offset, &value, sizeof(value)); +} + +struct StyleStateBlock { + std::uint32_t dpi_x; + std::uint32_t dpi_y; + std::uint32_t scale_unit; + std::uint32_t reserved_0x0c; + float scale_w; + float scale_h; + float effect_weight_x; + float effect_weight_y; + float slant_ratio; + float reserved_0x24; +}; + +struct CachedStyle { + StyleStateBlock state; + std::uint32_t reserved_0x28; + std::uint32_t layout_cache_state; + std::uint32_t cache_flags_and_direction; + std::uint32_t cache_lock_word; + std::uint8_t layout_cache_bytes[0x20]; + std::uint32_t reserved_0x58; + std::uint32_t cached_scalar_bits; +}; + +struct FontHandleOpenInfo { + std::uint32_t unique_id_packed; + std::uint32_t ctx_entry_index; + std::uint32_t sub_font_index; + std::uint32_t fontset_flags; + const void* fontset_record; + std::uint64_t reserved_0x18; +}; + +struct FontHandleNative { + std::uint16_t magic; + std::uint16_t flags; + std::uint32_t lock_word; + FontHandleOpenInfo open_info; + FontLibrary library; + void* renderer; + std::uint8_t reserved_0x38[0x08]; + StyleStateBlock style; + CachedStyle cached_style; + std::uint16_t metricA; + std::uint16_t metricB; + std::uint8_t reserved_0xcc[0x1C]; + std::uint8_t reserved_0xe8[0x08]; + FontHandle prevFont; + FontHandle nextFont; +}; + +struct FontObj { + std::uint32_t refcount; + std::uint32_t reserved_0x04; + std::uint32_t reserved_0x08; + std::uint32_t sub_font_index; + FontObj* prev; + FontObj* next; + std::uint8_t reserved_0x20[0x08]; + void* open_ctx_0x28; + void* ft_face; + FontHandle font_handle; + std::int32_t shift_units_x; + std::int32_t shift_units_y; + std::uint64_t layout_seed_pair; + float scale_x_0x50; + float scale_y_0x54; + void* ft_ctx_0x58; + std::uint8_t reserved_0x60[0x04]; + std::int32_t cached_glyph_index_0x64; + std::uint64_t cached_units_x_0x68; + std::uint64_t cached_units_y_0x70; + std::int64_t shift_cache_x; + std::int64_t shift_cache_y; + std::array layout_seed_vec; + std::array layout_scale_vec; + std::uint8_t reserved_0xA8[0x130 - 0xA8]; + std::uint32_t glyph_cfg_word_0x130; + std::uint8_t glyph_cfg_mode_0x134; + std::uint8_t glyph_cfg_byte_0x135; + std::uint8_t glyph_cfg_byte_0x136; + std::uint8_t reserved_0x137[0x200 - 0x137]; +}; + +struct FontCtxHeader { + std::uint32_t lock_word; + std::uint32_t max_entries; + void* base; + std::uint8_t reserved_0x10[0x10]; +}; + +struct FontCtxEntry { + std::uint32_t reserved_0x00; + std::uint32_t active; + std::uint64_t font_address; + std::uint32_t unique_id; + std::uint32_t lock_mode1; + std::uint32_t lock_mode3; + std::uint32_t lock_mode2; + void* obj_mode1; + void* obj_mode3; + void* obj_mode2; + std::uint8_t reserved_0x38[0x08]; +}; + +struct SysDriver; + +struct FontLibNative { + std::uint16_t magic; + std::uint16_t reserved0; + std::uint32_t lock_word; + std::uint32_t flags; + std::uint32_t feature_word; + std::uint32_t mem_kind; + std::uint32_t region_size; + void* region_base; + void* alloc_ctx; + const FontMemoryInterface* iface; + std::uint8_t reserved_0x30[0x20]; + FontAllocFunction alloc_fn; + FontFreeFunction dealloc_fn; + FontReallocFunction realloc_fn; + FontCallocFunction calloc_fn; + std::uint8_t reserved_0x70[0x10]; + const SysDriver* sys_driver; + void* fontset_registry; + std::uint8_t reserved_0x90[0x10]; + void* sysfonts_ctx; + void* external_fonts_ctx; + std::uint32_t* device_cache_buf; + std::uint8_t reserved_0xb8[0x04]; + std::uint32_t workspace_size; + void* workspace; + void* sysfont_desc_ptr; + std::uint64_t sysfont_flags; + FontHandle* list_head_ptr; + FontHandle list_head; + std::uint8_t reserved_0xe8[0x18]; +}; + +using DriverPixelResolutionFunction = std::uint32_t (APS5_VABI*)(); +using DriverInitFunction = int (APS5_VABI*)(const FontMemory* memory, FontLibNative* library); +using DriverTermFunction = int (APS5_VABI*)(FontLibNative* library); +using DriverSupportFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t formats); +using DriverOpenFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t mode, const void* fontAddress, std::uint32_t fontSize, std::uint32_t subFontIndex, std::uint32_t uniqueWord, FontObj** inoutFontObj); +using DriverCloseFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t flags); +using DriverScaleFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint16_t* outUnitsPerEm, float* outScale); +using DriverMetricFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t metricId, std::uint16_t* outMetric); +using DriverGlyphsCountFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* outCount); +using DriverGlyphIndexFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t codepoint, std::uint32_t* outGlyphIndex); +using DriverSetCharWithDpiFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t dpiX, std::uint32_t dpiY, float scaleX, float scaleY, float* outScaleX, float* outScaleY); +using DriverSetCharDefaultDpiFunction = int (APS5_VABI*)(FontObj* fontObj, float scaleX, float scaleY, float* outScaleX, float* outScaleY); +using DriverComputeLayoutFunction = int (APS5_VABI*)(FontObj* fontObj, const StyleStateBlock* style, std::uint8_t* outWords); +using DriverLoadGlyphCachedFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t glyphIndex, std::int32_t mode, std::uint64_t* outWords); +using DriverGetGlyphMetricsFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* optParam2, std::uint8_t mode, std::uint8_t* outParams, FontGlyphMetrics* outMetrics); +using DriverApplyGlyphAdjustFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t p2, std::uint32_t glyphIndex, std::int32_t p4, std::int32_t p5, std::uint32_t* inoutGlyphIndex); +using DriverConfigureGlyphFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* inParams, std::int32_t mode, std::uint32_t* inoutState); + +struct SysDriver { + std::uint32_t magic; + std::uint32_t reserved; + void* reserved_ptr1; + DriverPixelResolutionFunction pixel_resolution; + DriverInitFunction init; + DriverTermFunction term; + DriverSupportFunction support_formats; + std::uint8_t reserved_0x30[0x08]; + DriverOpenFunction open; + DriverCloseFunction close; + std::uint8_t reserved_0x48[0x08]; + DriverScaleFunction scale; + std::uint8_t reserved_0x58[0x08]; + DriverMetricFunction metric; + DriverGlyphsCountFunction glyphs_count; + std::uint8_t reserved_0x70[0x08]; + DriverGlyphIndexFunction glyph_index; + DriverSetCharWithDpiFunction set_char_with_dpi; + DriverSetCharDefaultDpiFunction set_char_default_dpi; + std::uint8_t reserved_0x90[0x10]; + DriverComputeLayoutFunction compute_layout; + DriverLoadGlyphCachedFunction load_glyph_cached; + std::uint8_t reserved_0xb0[0x08]; + DriverGetGlyphMetricsFunction get_glyph_metrics; + std::uint8_t reserved_0xc0[0x20]; + DriverApplyGlyphAdjustFunction apply_glyph_adjust; + std::uint8_t reserved_0xe8[0x20]; + DriverConfigureGlyphFunction configure_glyph; + std::uint8_t reserved_0x110[0x08]; + DriverComputeLayoutFunction compute_layout_alt; +}; + +struct RendererNative { + std::uint16_t magic; + std::uint16_t reserved02; + std::uint8_t reserved_04[0x08]; + std::uint8_t feature_byte_0x0c; + std::uint8_t reserved_0d[0x03]; + std::uint32_t mem_kind; + std::uint32_t region_size; + void* region_base; + void* alloc_ctx; + const FontMemoryInterface* mem_iface; + std::uint8_t reserved_30[0x20]; + FontAllocFunction alloc_fn; + FontFreeFunction free_fn; + FontReallocFunction realloc_fn; + FontCallocFunction calloc_fn; + std::uint8_t reserved_70[0x10]; + void* selection; + std::uint8_t reserved_88[0x08]; + std::uint64_t outline_magic_0x90; + void* workspace; + std::uint64_t workspace_size; + std::uint32_t reserved_a8; + std::uint32_t outline_policy_flag; +}; + +struct RendererFtBackend { + void* renderer_header_0x10; + std::uintptr_t unknown_0x08; + std::uintptr_t unknown_0x10; + std::uintptr_t unknown_0x18; + void* initialized_marker; + std::uint8_t reserved_0x28[0x20]; +}; + +struct RendererFt { + RendererNative base; + std::uint8_t reserved_0x0B0[0x70]; + RendererFtBackend ft_backend; +}; + +using RendererCreateFunction = int (APS5_VABI*)(RendererNative* renderer); +using RendererDestroyFunction = int (APS5_VABI*)(RendererNative* renderer); +using RendererQueryFunction = std::uint64_t (APS5_VABI*)(RendererNative* renderer, std::uint8_t* params, std::int64_t* outPtr, std::uint8_t* outVector); + +struct RendererSelection { + std::uint32_t magic; + std::uint32_t size; + RendererCreateFunction create_fn; + RendererDestroyFunction destroy_fn; + RendererQueryFunction query_fn; +}; + +static_assert(sizeof(StyleStateBlock) == 0x28); +static_assert(offsetof(StyleStateBlock, scale_w) == 0x10); +static_assert(offsetof(StyleStateBlock, slant_ratio) == 0x20); +static_assert(sizeof(CachedStyle) == 0x60); +static_assert(offsetof(CachedStyle, layout_cache_state) == 0x2C); +static_assert(offsetof(CachedStyle, cache_lock_word) == 0x34); +static_assert(offsetof(CachedStyle, layout_cache_bytes) == 0x38); +static_assert(offsetof(CachedStyle, cached_scalar_bits) == 0x5C); +static_assert(sizeof(FontHandleOpenInfo) == 0x20); +static_assert(sizeof(FontHandleNative) == 0x100); +static_assert(offsetof(FontHandleNative, open_info) == 0x08); +static_assert(offsetof(FontHandleNative, library) == 0x28); +static_assert(offsetof(FontHandleNative, renderer) == 0x30); +static_assert(offsetof(FontHandleNative, style) == 0x40); +static_assert(offsetof(FontHandleNative, cached_style) == 0x68); +static_assert(offsetof(FontHandleNative, metricA) == 0xC8); +static_assert(offsetof(FontHandleNative, prevFont) == 0xF0); +static_assert(sizeof(FontObj) == 0x200); +static_assert(offsetof(FontObj, ft_face) == 0x30); +static_assert(offsetof(FontObj, shift_cache_x) == 0x78); +static_assert(offsetof(FontObj, layout_scale_vec) == 0x98); +static_assert(offsetof(FontObj, glyph_cfg_word_0x130) == 0x130); +static_assert(sizeof(FontCtxHeader) == 0x20); +static_assert(sizeof(FontCtxEntry) == 0x40); +static_assert(offsetof(FontCtxEntry, obj_mode2) == 0x30); +static_assert(sizeof(FontLibNative) == 0x100); +static_assert(offsetof(FontLibNative, alloc_ctx) == 0x20); +static_assert(offsetof(FontLibNative, iface) == 0x28); +static_assert(offsetof(FontLibNative, alloc_fn) == 0x50); +static_assert(offsetof(FontLibNative, sys_driver) == 0x80); +static_assert(offsetof(FontLibNative, fontset_registry) == 0x88); +static_assert(offsetof(FontLibNative, sysfonts_ctx) == 0xA0); +static_assert(offsetof(FontLibNative, external_fonts_ctx) == 0xA8); +static_assert(offsetof(FontLibNative, device_cache_buf) == 0xB0); +static_assert(offsetof(FontLibNative, workspace) == 0xC0); +static_assert(offsetof(FontLibNative, sysfont_flags) == 0xD0); +static_assert(offsetof(FontLibNative, list_head_ptr) == 0xD8); +static_assert(offsetof(FontLibNative, list_head) == 0xE0); +static_assert(sizeof(SysDriver) == 0x120); +static_assert(offsetof(SysDriver, pixel_resolution) == 0x10); +static_assert(offsetof(SysDriver, open) == 0x38); +static_assert(offsetof(SysDriver, scale) == 0x50); +static_assert(offsetof(SysDriver, glyph_index) == 0x78); +static_assert(offsetof(SysDriver, compute_layout) == 0xA0); +static_assert(offsetof(SysDriver, get_glyph_metrics) == 0xB8); +static_assert(offsetof(SysDriver, apply_glyph_adjust) == 0xE0); +static_assert(offsetof(SysDriver, configure_glyph) == 0x108); +static_assert(offsetof(SysDriver, compute_layout_alt) == 0x118); +static_assert(sizeof(RendererNative) == 0xB0); +static_assert(offsetof(RendererNative, alloc_fn) == 0x50); +static_assert(offsetof(RendererNative, selection) == 0x80); +static_assert(offsetof(RendererNative, outline_policy_flag) == 0xAC); +static_assert(sizeof(RendererFtBackend) == 0x48); +static_assert(sizeof(RendererFt) == 0x168); +static_assert(offsetof(RendererFt, ft_backend) == 0x120); + +} + +#endif diff --git a/core/libs/prx/libSceFont/include/FontFreeType.hpp b/core/libs/prx/libSceFont/include/FontFreeType.hpp new file mode 100644 index 000000000..eeef92b9f --- /dev/null +++ b/core/libs/prx/libSceFont/include/FontFreeType.hpp @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTFREETYPE_HPP +#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTFREETYPE_HPP + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +namespace Font { + +constexpr float ONE_OVER_64 = 1.0f / 64.0f; + +inline std::int32_t TruncateFloatToInt(float value) { + if (!std::isfinite(value) || value > 2147483647.0f || value < -2147483648.0f) return std::numeric_limits::min(); + return static_cast(value); +} + +inline std::int32_t RoundMulFixed(std::int64_t value, std::int64_t fixed16) { + const std::int64_t product = value * fixed16; + const std::int64_t signAdjust = ((~value) >> 63) * -0x10000LL; + const std::int64_t base = signAdjust + product; + std::int64_t rounded = base - 0x8000LL; + if (rounded < 0) rounded = base + 0x7FFFLL; + return static_cast(static_cast(rounded) >> 16); +} + +inline std::int32_t TruncFixed(std::int64_t fixed16) { + std::int64_t value = fixed16; + if (fixed16 < 0) value = fixed16 + 0xFFFFLL; + return static_cast(value >> 16); +} + +inline std::int64_t TruncMulUnits(std::int64_t fixed16, std::uint16_t units) { + const std::int64_t product = fixed16 * static_cast(units); + std::int64_t rounded = product; + if (product < 0) rounded = product + 0xFFFFLL; + return static_cast(static_cast(rounded >> 16)); +} + +inline float FixedMulUnitsToF26Dot6(std::int64_t fixed16, std::uint16_t unitsPerEm) { + const std::int64_t product = fixed16 * static_cast(unitsPerEm); + std::int64_t rounded = product + 0xFFFF; + if (product >= 0) rounded = product; + return static_cast(rounded >> 16) * ONE_OVER_64; +} + +inline int FloorToInt(float value) { + int result = static_cast(std::trunc(value)); + if (static_cast(result) > value) --result; + return result; +} + +inline int CeilToInt(float value) { + int result = static_cast(std::trunc(value)); + if (static_cast(result) < value) ++result; + return result; +} + +inline FT_UInt ResolveGlyphIndexWithFallback(FT_Face face, std::uint32_t codepoint) { + if (!face) return 0; + const auto code = static_cast(codepoint); + const FT_CharMap original = face->charmap; + FT_UInt glyphIndex = FT_Get_Char_Index(face, code); + if (glyphIndex != 0 || !face->charmaps || face->num_charmaps <= 1) return glyphIndex; + for (int i = 0; i < face->num_charmaps; ++i) { + const FT_CharMap charmap = face->charmaps[i]; + if (!charmap || charmap == original) continue; + if (FT_Set_Charmap(face, charmap) != 0) continue; + glyphIndex = FT_Get_Char_Index(face, code); + if (glyphIndex != 0) break; + } + if (original && face->charmap != original) FT_Set_Charmap(face, original); + return glyphIndex; +} + +inline FT_F26Dot6 ClampPpem(FT_F26Dot6 value) { + constexpr FT_F26Dot6 maxPpem = static_cast(65535 * 64); + return std::clamp(value, static_cast(-maxPpem), maxPpem); +} + +inline FT_Error SetCharSizeCompat(FT_Face face, FT_F26Dot6 charW, FT_F26Dot6 charH, std::uint32_t dpiX, std::uint32_t dpiY, FT_F26Dot6* usedW = nullptr, FT_F26Dot6* usedH = nullptr) { + const auto writeUsed = [&](FT_F26Dot6 w, FT_F26Dot6 h) { + if (usedW) *usedW = w; + if (usedH) *usedH = h; + }; + if (!face) { + writeUsed(charW, charH); + return FT_Err_Invalid_Face_Handle; + } + FT_Error error = FT_Set_Char_Size(face, charW, charH, dpiX, dpiY); + if (error == 0) { + writeUsed(charW, charH); + return 0; + } + const FT_F26Dot6 clampedW = ClampPpem(charW); + const FT_F26Dot6 clampedH = ClampPpem(charH); + if (clampedW != charW || clampedH != charH) { + error = FT_Set_Char_Size(face, clampedW, clampedH, dpiX, dpiY); + if (error == 0) { + writeUsed(clampedW, clampedH); + return 0; + } + } + if (face->num_fixed_sizes <= 0 || !face->available_sizes) { + writeUsed(charW, charH); + return error; + } + const double requestW = static_cast(std::llabs(static_cast(charW))) / 64.0; + const double requestH = static_cast(std::llabs(static_cast(charH))) / 64.0; + const double targetW = requestW > 0.0 ? requestW : requestH; + const double targetH = requestH > 0.0 ? requestH : requestW; + if (targetW <= 0.0 || targetH <= 0.0) { + writeUsed(charW, charH); + return error; + } + int bestIndex = -1; + double bestScore = std::numeric_limits::infinity(); + for (int i = 0; i < face->num_fixed_sizes; ++i) { + const FT_Bitmap_Size& strike = face->available_sizes[i]; + const double strikeW = std::max(1.0, static_cast(strike.x_ppem) / 64.0); + const double strikeH = std::max(1.0, static_cast(strike.y_ppem) / 64.0); + const double score = std::abs(strikeW - targetW) / targetW + std::abs(strikeH - targetH) / targetH; + if (score < bestScore) { + bestScore = score; + bestIndex = i; + } + } + if (bestIndex < 0) { + writeUsed(charW, charH); + return error; + } + error = FT_Select_Size(face, bestIndex); + if (error == 0) { + const FT_Bitmap_Size& strike = face->available_sizes[bestIndex]; + writeUsed(strike.x_ppem, strike.y_ppem); + return 0; + } + writeUsed(charW, charH); + return error; +} + +} + +#endif diff --git a/core/libs/prx/libSceFont/include/FontInternal.hpp b/core/libs/prx/libSceFont/include/FontInternal.hpp new file mode 100644 index 000000000..426a21bee --- /dev/null +++ b/core/libs/prx/libSceFont/include/FontInternal.hpp @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTINTERNAL_HPP +#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTINTERNAL_HPP + +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "prx/libSceFont/include/FontDriver.hpp" + +namespace Font { + +constexpr std::uint8_t STYLE_FRAME_FLAG_SCALE = 0x01; +constexpr std::uint8_t STYLE_FRAME_FLAG_SLANT = 0x02; +constexpr std::uint8_t STYLE_FRAME_FLAG_WEIGHT = 0x04; +constexpr float POINTS_PER_INCH = 72.0f; + +struct FontState { + std::vector faceData; + FT_Face face = nullptr; + float scaleW = 16.0f; + float scaleH = 16.0f; + + FontState() = default; + FontState(const FontState&) = delete; + FontState& operator=(const FontState&) = delete; + ~FontState(); +}; + +struct GeneratedGlyph { + FontGlyphOpaque glyph{}; + FontGlyphMetrics metrics{}; + FontGlyphMetricsHorizontal metricsHorizontal{}; + FontGlyphMetricsHorizontalX metricsHorizontalX{}; + FontGlyphMetricsHorizontalAdvance metricsHorizontalAdvance{}; + std::uint32_t codepoint = 0; + FontHandle owner = nullptr; + FontGlyphOutline outline{}; + std::vector outlinePoints; + std::vector outlineTags; + std::vector outlineContours; + bool metricsInitialized = false; + bool outlineInitialized = false; +}; + +struct RenderSurfaceSystemUse { + FontStyleFrame* styleframe; + float catchedScale; +}; + +void Backoff(); +std::uint32_t AcquireWordLock(std::uint32_t& word); +FontHandleNative* GetNativeFont(FontHandle handle); +bool AcquireLibraryLock(FontLibNative* library, std::uint32_t& previous); +void ReleaseLibraryLock(FontLibNative* library, std::uint32_t previous); +bool AcquireFontLock(FontHandleNative* font, std::uint32_t& previous); +void ReleaseFontLock(FontHandleNative* font, std::uint32_t previous); +bool AcquireCachedStyleLock(FontHandleNative* font, std::uint32_t& previous); +void ReleaseCachedStyleLock(FontHandleNative* font, std::uint32_t previous); +void LinkFontToLibrary(FontLibNative* library, FontHandle handle); +void UnlinkFontFromLibrary(FontLibNative* library, FontHandle handle); + +std::uint32_t* EntryLockWord(FontCtxEntry* entry, std::uint32_t modeLow); +void** EntryObjectSlot(FontCtxEntry* entry, std::uint32_t modeLow); +FontCtxEntry* AcquireFontCtxEntry(void* ctx, std::uint32_t index, std::uint32_t modeLow, FontObj** outObject, std::uint32_t* outLockWord); +void ReleaseFontCtxEntryLock(FontCtxEntry* entry, std::uint32_t modeLow, std::uint32_t lockWord); +FontObj* FindSubFont(FontObj* head, std::uint32_t subFontIndex); +void ReleaseFontObjectsForHandle(FontHandleNative* font); + +FontState* TryGetState(FontHandle handle); +FontState& ResetState(FontHandle handle); +void RemoveState(FontHandle handle); +void LoadStateFace(FontState& state, std::vector bytes, std::uint32_t subFontIndex); + +void TrackGeneratedGlyph(FontGlyph glyph); +bool ForgetGeneratedGlyph(FontGlyph glyph); +GeneratedGlyph* TryGetGeneratedGlyph(FontGlyph glyph); +void PopulateGlyphMetricVariants(GeneratedGlyph& glyph); +void BuildBoundingOutline(GeneratedGlyph& glyph); +bool BuildTrueOutline(GeneratedGlyph& glyph); + +int StyleStateSetScalePixel(StyleStateBlock* style, float w, float h); +int StyleStateSetScalePoint(StyleStateBlock* style, float w, float h); +int StyleStateGetScalePixel(const StyleStateBlock* style, float* w, float* h); +int StyleStateGetScalePoint(const StyleStateBlock* style, float* w, float* h); +int StyleStateSetDpi(StyleStateBlock* style, std::uint32_t hDpi, std::uint32_t vDpi); +int StyleStateSetSlantRatio(StyleStateBlock* style, float slantRatio); +int StyleStateGetSlantRatio(const StyleStateBlock* style, float* slantRatio); +int StyleStateSetWeightScale(StyleStateBlock* style, float weightXScale, float weightYScale); +int StyleStateGetWeightScale(const StyleStateBlock* style, float* weightXScale, float* weightYScale, std::uint32_t* mode); +bool ValidStyleFrame(const FontStyleFrame* frame); + +std::uint8_t CachedStyleCacheFlags(const CachedStyle& style); +void CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags); +void CachedStyleSetDirectionWord(CachedStyle& style, std::uint16_t word); +float CachedStyleGetScalar(const CachedStyle& style); +void CachedStyleSetScalar(CachedStyle& style, float value); + +void ClearRenderOutputs(FontGlyphMetrics* metrics, FontRenderOutput* result); +int ComputeHorizontalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords); +int ComputeVerticalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords); +int GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMetrics* metrics, bool useCachedStyle); +int RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result); + +} + +#endif diff --git a/core/libs/prx/libSceFont/include/FontTypes.hpp b/core/libs/prx/libSceFont/include/FontTypes.hpp new file mode 100644 index 000000000..d65cb4b6c --- /dev/null +++ b/core/libs/prx/libSceFont/include/FontTypes.hpp @@ -0,0 +1,432 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTTYPES_HPP +#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTTYPES_HPP + +#include +#include + +#include "prx/libc/include/General.hpp" + +constexpr int SCE_FONT_OK = 0; +constexpr int SCE_FONT_ERROR_FATAL = static_cast(0x80460001); +constexpr int SCE_FONT_ERROR_INVALID_PARAMETER = static_cast(0x80460002); +constexpr int SCE_FONT_ERROR_INVALID_MEMORY = static_cast(0x80460003); +constexpr int SCE_FONT_ERROR_INVALID_LIBRARY = static_cast(0x80460004); +constexpr int SCE_FONT_ERROR_INVALID_FONT_HANDLE = static_cast(0x80460005); +constexpr int SCE_FONT_ERROR_INVALID_GLYPH = static_cast(0x80460006); +constexpr int SCE_FONT_ERROR_INVALID_RENDERER = static_cast(0x80460007); +constexpr int SCE_FONT_ERROR_INVALID_TEXT_SOURCE = static_cast(0x80460008); +constexpr int SCE_FONT_ERROR_INVALID_STRING = static_cast(0x80460009); +constexpr int SCE_FONT_ERROR_INVALID_WRITING = static_cast(0x8046000A); +constexpr int SCE_FONT_ERROR_INVALID_WORDS = static_cast(0x8046000B); +constexpr int SCE_FONT_ERROR_ALLOCATION_FAILED = static_cast(0x80460010); +constexpr int SCE_FONT_ERROR_FS_OPEN_FAILED = static_cast(0x80460011); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_LIBRARY = static_cast(0x80460018); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_FORMAT = static_cast(0x80460019); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_FUNCTION = static_cast(0x80460020); +constexpr int SCE_FONT_ERROR_ALREADY_SPECIFIED = static_cast(0x80460021); +constexpr int SCE_FONT_ERROR_ALREADY_ATTACHED = static_cast(0x80460022); +constexpr int SCE_FONT_ERROR_ALREADY_OPENED = static_cast(0x80460023); +constexpr int SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER = static_cast(0x80460025); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_FONTSET = static_cast(0x80460031); +constexpr int SCE_FONT_ERROR_FONT_OPEN_MAX = static_cast(0x80460033); +constexpr int SCE_FONT_ERROR_FONT_OPEN_FAILED = static_cast(0x80460036); +constexpr int SCE_FONT_ERROR_FONT_CLOSE_FAILED = static_cast(0x80460037); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY = static_cast(0x80460040); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_CODE = static_cast(0x80460041); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_GLYPH = static_cast(0x80460042); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_SCRIPT = static_cast(0x80460043); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_LANGUAGE = static_cast(0x80460044); +constexpr int SCE_FONT_ERROR_NO_SUPPORT_SURFACE = static_cast(0x80460050); +constexpr int SCE_FONT_ERROR_UNSET_PARAMETER = static_cast(0x80460058); +constexpr int SCE_FONT_ERROR_FUNCTIONAL_LIMIT = static_cast(0x8046005C); +constexpr int SCE_FONT_ERROR_ALREADY_BOUND_RENDERER = static_cast(0x80460060); +constexpr int SCE_FONT_ERROR_NOT_BOUND_RENDERER = static_cast(0x80460061); +constexpr int SCE_FONT_ERROR_RENDERER_ALLOCATION_FAILED = static_cast(0x80460063); +constexpr int SCE_FONT_ERROR_RENDERER_ALLOCATION_LIMITED = static_cast(0x80460064); +constexpr int SCE_FONT_ERROR_RENDERER_RENDER_FAILED = static_cast(0x80460065); + +struct FontHandleOpaque { + std::uint32_t reserved[64]; +}; + +using FontHandle = FontHandleOpaque*; +using FontLibrary = void*; +using FontRenderer = void*; + +struct FontMemory; + +using FontAllocFunction = void* (APS5_VABI*)(void* object, std::uint32_t size); +using FontFreeFunction = void (APS5_VABI*)(void* object, void* p); +using FontReallocFunction = void* (APS5_VABI*)(void* object, void* p, std::uint32_t newSize); +using FontCallocFunction = void* (APS5_VABI*)(void* object, std::uint32_t nBlock, std::uint32_t size); +using FontMspaceCreateFunction = void* (APS5_VABI*)(void* parent, const char* name, void* address, std::uint32_t size, std::uint32_t attr); +using FontMspaceDestroyFunction = void (APS5_VABI*)(void* parent, void* mspace); +using FontMemoryDestroyFunction = void (APS5_VABI*)(FontMemory* fontMemory, void* object, void* destroyArg); + +struct FontMemoryInterface { + FontAllocFunction alloc; + FontFreeFunction dealloc; + FontReallocFunction realloc_fn; + FontCallocFunction calloc_fn; + FontMspaceCreateFunction mspace_create; + FontMspaceDestroyFunction mspace_destroy; +}; + +struct FontMemory { + std::uint16_t mem_kind; + std::uint16_t attr_bits; + std::uint32_t region_size; + void* region_base; + void* mspace_handle; + const FontMemoryInterface* iface; + FontMemoryDestroyFunction on_destroy; + void* destroy_ctx; + void* some_ctx1; + void* some_ctx2; +}; + +struct FontOpenDetail { + std::uint16_t tag; + std::uint16_t pad16; + std::uint32_t flags; + std::uint32_t subfont_index; + std::int32_t unique_id; + const void* reserved_ptr2; + const void* reserved_ptr1; +}; + +struct FontGlyphMetrics { + float width; + float height; + struct { + float bearingX; + float bearingY; + float advance; + } Horizontal; + struct { + float bearingX; + float bearingY; + float advance; + } Vertical; +}; + +struct FontGlyphMetricsHorizontal { + float width; + float height; + struct { + float bearing_x; + float bearing_y; + float advance; + } horizontal; +}; + +struct FontGlyphMetricsHorizontalX { + float width; + struct { + float bearing_x; + float advance; + } horizontal; +}; + +struct FontGlyphMetricsHorizontalAdvance { + struct { + float advance; + } horizontal; +}; + +struct FontKerning { + float offsetX; + float offsetY; + float positionX; + float positionY; +}; + +struct FontGlyphImageMetrics { + float bearingX; + float bearingY; + float advance; + float stride; + std::uint32_t width; + std::uint32_t height; +}; + +struct FontGenerateGlyphDetail { + std::uint16_t id; + std::uint16_t res0; + std::uint16_t form_options; + std::uint8_t glyph_form; + std::uint8_t metrics_form; + const FontMemory* mem; + void* res1; + void* res2; +}; + +struct FontGlyphOutlinePoint { + float x; + float y; +}; + +struct FontGlyphOutline { + std::int16_t contours_cnt; + std::int16_t points_cnt; + std::uint32_t outline_flags; + FontGlyphOutlinePoint* points_ptr; + std::uint8_t* tags_ptr; + std::uint16_t* contour_end_idx; +}; + +struct FontGlyphOpaque { + std::uint16_t magic; + std::uint16_t flags; + std::uint8_t glyph_form; + std::uint8_t metrics_form; + std::uint16_t em_size; + std::uint16_t baseline; + std::uint16_t height_px; + std::uint16_t origin_x; + std::uint16_t origin_y; + float scale_x; + float base_scale; + const FontMemory* memory; +}; + +using FontGlyph = FontGlyphOpaque*; + +struct FontResultStage { + std::uint8_t* p_00; + std::uint32_t u32_08; + std::uint32_t u32_0C; + std::uint32_t u32_10; +}; + +struct FontSurfaceImage { + std::uint8_t* address; + std::uint32_t widthByte; + std::uint8_t pixelSizeByte; + std::uint8_t pixelFormat; + std::uint16_t pad16; +}; + +struct FontRenderOutput { + const FontResultStage* stage; + FontSurfaceImage SurfaceImage; + struct { + std::uint32_t x; + std::uint32_t y; + std::uint32_t w; + std::uint32_t h; + } UpdateRect; + FontGlyphImageMetrics ImageMetrics; +}; + +struct FontTextCharacter { + FontTextCharacter* prev; + FontTextCharacter* next; + void* textOrder; + FontHandle font; + void* shapeEntry; + std::uint32_t characterCode; + std::uint8_t reserved_0x2c[4]; + std::uint8_t clusterSpan; + std::uint8_t clusterIndex; + std::uint8_t clusterKind; + std::int8_t synthetic; + std::uint8_t reserved_0x34[4]; + std::uint64_t flags; + std::uint8_t reserved_0x40[136]; +}; + +struct FontRenderSurface { + void* buffer; + std::int32_t widthByte; + std::int8_t pixelSizeByte; + std::uint8_t pad0; + std::uint8_t styleFlag; + std::uint8_t pad2; + std::int32_t width; + std::int32_t height; + std::uint32_t sc_x0; + std::uint32_t sc_y0; + std::uint32_t sc_x1; + std::uint32_t sc_y1; + std::uint64_t reserved_q[11]; +}; + +struct FontStyleFrame { + std::uint16_t magic; + std::uint8_t flags1; + std::uint8_t flags2; + std::uint32_t hDpi; + std::uint32_t vDpi; + std::uint32_t scaleUnit; + float baseScale; + float scalePixelW; + float scalePixelH; + float effectWeightX; + float effectWeightY; + float slantRatio; + std::uint32_t reserved_0x28; + std::uint32_t layout_cache_state; + std::uint32_t cache_flags_and_direction; + std::uint32_t cache_lock_word; + std::uint8_t layout_cache_bytes[0x20]; + std::uint32_t reserved_0x58; + std::uint32_t cached_scalar_bits; +}; + +struct FontTextSource; + +union FontTextParseResult { + void* reserved[4]; + struct { + FontHandle font; + std::uint32_t code; + } FontCode; + struct { + void* reserved; + std::uint32_t terminateCode; + } Terminate; + struct { + void* reserved; + std::int32_t errorCode; + } Error; +}; + +using FontTextParseFunction = std::int32_t (APS5_VABI*)(FontTextSource* fontTextSource, void** order, FontTextParseResult* result); + +struct FontTextSource { + std::uint64_t systemUse0; + const void* start; + const void* end; + const void* current; + FontTextParseFunction textParser; + void* textObject; + FontHandle defaultFont; + void* systemUse[5]; +}; + +struct FontCreateStringDetail { + std::uint16_t detailId; + std::uint8_t detailType; + std::uint8_t detections; + std::uint32_t ordersOption; + FontHandle defaultFont; + union { + struct { + void* function; + void* object; + } Callback; + } Orders; +}; + +struct FontStringOpaque; +using FontString = FontStringOpaque*; + +struct FontWriting { + void* systemUse[32]; +}; + +struct FontWritingProfile { + std::uint32_t characterCount : 8; + std::uint32_t invisibleGlyph : 1; + std::uint32_t reserved : 23; +}; + +struct FontWritingStep { + float x; + float y; + float advanceX; + float advanceY; + FontHandle font; + FontWritingProfile Profile; + std::uint32_t glyphCode; + struct { + float x; + float y; + } Positioning; + FontGlyphMetrics GlyphMetrics; +}; + +struct FontWritingLetterStep { + float x; + float y; + float advanceX; + float advanceY; + struct { + std::uint32_t textsCount; + std::uint32_t textsIndex; + std::uint32_t glyphsCount; + std::uint32_t glyphsIndex; + std::uint8_t baseComponentsCount; + std::uint8_t baseComponentsIndex; + std::uint8_t baseFocusCount; + std::uint8_t baseFocusIndex; + std::uint8_t oppositeDirection; + std::uint8_t characterTextCount; + std::uint8_t baseTextCount; + std::uint8_t markTextCount; + std::uint32_t marksTextCount; + std::uint32_t marksTextNumber; + std::uint32_t reserved[3]; + std::int32_t textLetterOffset; + } Components; +}; + +struct FontWritingExtent { + float top; + float bottom; + float left; + float right; +}; + +struct FontWritingMetrics { + float advanceX; + float advanceY; + FontWritingExtent Extent; +}; + +struct FontHorizontalLayout { + float baselineOffset; + float lineAdvance; + float decorationExtent; +}; + +struct FontVerticalLayout { + float baselineOffsetX; + float columnAdvance; + float decorationSpan; +}; + +static_assert(sizeof(FontHandleOpaque) == 0x100); +static_assert(sizeof(FontMemory) == 0x40); +static_assert(sizeof(FontMemoryInterface) == 0x30); +static_assert(sizeof(FontOpenDetail) == 0x20); +static_assert(sizeof(FontGlyphMetrics) == 0x20); +static_assert(sizeof(FontKerning) == 0x10); +static_assert(sizeof(FontGlyphImageMetrics) == 0x18); +static_assert(sizeof(FontGenerateGlyphDetail) == 0x20); +static_assert(sizeof(FontResultStage) == 0x18); +static_assert(sizeof(FontSurfaceImage) == 0x10); +static_assert(sizeof(FontRenderOutput) == 0x40); +static_assert(sizeof(FontTextCharacter) == 0xC8); +static_assert(offsetof(FontTextCharacter, flags) == 0x38); +static_assert(sizeof(FontRenderSurface) == 0x80); +static_assert(sizeof(FontStyleFrame) == 0x60); +static_assert(offsetof(FontStyleFrame, scalePixelW) == 0x14); +static_assert(offsetof(FontStyleFrame, cache_lock_word) == 0x34); +static_assert(offsetof(FontStyleFrame, reserved_0x58) == 0x58); +static_assert(sizeof(FontTextSource) == 0x60); +static_assert(offsetof(FontTextSource, textParser) == 0x20); +static_assert(offsetof(FontTextSource, defaultFont) == 0x30); +static_assert(offsetof(FontTextSource, systemUse) == 0x38); +static_assert(sizeof(FontCreateStringDetail) == 0x20); +static_assert(sizeof(FontWriting) == 0x100); +static_assert(sizeof(FontWritingProfile) == sizeof(std::uint32_t)); +static_assert(sizeof(FontWritingStep) == 0x48); +static_assert(sizeof(FontWritingLetterStep) == 0x40); +static_assert(sizeof(FontWritingMetrics) == 0x18); + +#endif diff --git a/core/libs/prx/libSceFont/src/Layout.cpp b/core/libs/prx/libSceFont/src/Layout.cpp new file mode 100644 index 000000000..7b76c46d0 --- /dev/null +++ b/core/libs/prx/libSceFont/src/Layout.cpp @@ -0,0 +1,522 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +#include "prx/libSceFont/include/FontFreeType.hpp" +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +struct FloatPair { + float lo = 0.0f; + float hi = 0.0f; +}; + +bool OrderedLess(float a, float b) { + return !std::isnan(a) && !std::isnan(b) && a < b; +} + +float MaxSs(float a, float b) { + if (std::isnan(a) || std::isnan(b)) return b; + if (a > b) return a; + if (a < b) return b; + if (a == 0.0f && b == 0.0f && std::signbit(a) != std::signbit(b)) return std::signbit(a) ? b : a; + return a; +} + +float FlipSign(float value) { + return std::bit_cast(std::bit_cast(value) ^ 0x80000000u); +} + +void UpdateShiftCache(FontObj* obj) { + obj->shift_cache_x = obj->shift_units_x; + obj->shift_cache_y = obj->shift_units_y; + const auto seedLow = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const auto seedHigh = static_cast(static_cast(obj->layout_seed_pair >> 32)); + obj->layout_seed_vec[0] = static_cast(static_cast(seedLow)); + obj->layout_seed_vec[1] = static_cast(static_cast(seedHigh)); + obj->layout_scale_vec[0] = 0x10000; + obj->layout_scale_vec[1] = 0x10000; +} + +int SetDriverCharSize(const SysDriver* driver, FontObj* obj, const StyleStateBlock* style, float& scaleX, float& scaleY) { + if (style->scale_unit == 0) return driver->set_char_default_dpi(obj, scaleX, scaleY, &scaleX, &scaleY); + return driver->set_char_with_dpi(obj, style->dpi_x, style->dpi_y, scaleX, scaleY, &scaleX, &scaleY); +} + +void ClearHorizontalLayout(std::uint8_t* outWords) { + if (!outWords) return; + std::memset(outWords, 0, 0x10); + std::memset(outWords + 0x18, 0, 0x0C); +} + +void ClearVerticalLayout(std::uint8_t* outWords) { + if (!outWords) return; + std::memset(outWords, 0, 0x10); + std::memset(outWords + 0x14, 0, 0x0C); +} + +const StyleStateBlock* ResolveSurfaceStyleState(const StyleStateBlock* cachedState, const FontRenderSurface* surface, StyleStateBlock& temporary) { + const auto* systemUse = reinterpret_cast(surface->reserved_q); + if ((surface->styleFlag & 0x1) == 0 || !ValidStyleFrame(systemUse->styleframe)) return cachedState; + const FontStyleFrame* frame = systemUse->styleframe; + temporary = *cachedState; + if ((frame->flags1 & STYLE_FRAME_FLAG_SCALE) != 0) { + temporary.scale_unit = frame->scaleUnit; + temporary.scale_w = frame->scalePixelW; + temporary.scale_h = frame->scalePixelH; + temporary.dpi_x = frame->hDpi ? frame->hDpi : 0x48; + temporary.dpi_y = frame->vDpi ? frame->vDpi : 0x48; + } else { + const std::uint32_t wantX = frame->hDpi == 0 ? temporary.dpi_x : frame->hDpi; + const std::uint32_t wantY = frame->vDpi == 0 ? temporary.dpi_y : frame->vDpi; + temporary.dpi_x = wantX != 0 ? 0x48 : 0; + temporary.dpi_y = wantY != 0 ? 0x48 : 0; + } + if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) temporary.slant_ratio = frame->slantRatio; + if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) { + temporary.effect_weight_x = frame->effectWeightX; + temporary.effect_weight_y = frame->effectWeightY; + } + return &temporary; +} + +void FillImageMetrics(FontRenderOutput* result, const FontGlyphMetrics* metrics, float x, float y) { + const float leftF = x + metrics->Horizontal.bearingX; + const float topF = y + metrics->Horizontal.bearingY; + const float rightF = leftF + metrics->width; + const float bottomF = topF - metrics->height; + const int left = FloorToInt(leftF); + const int top = CeilToInt(topF); + const int right = CeilToInt(rightF); + const int bottom = FloorToInt(bottomF); + const float advanceF = x + metrics->Horizontal.advance; + const int advance = CeilToInt(advanceF); + result->ImageMetrics.bearingX = static_cast(left) - x; + result->ImageMetrics.bearingY = static_cast(top) - y; + result->ImageMetrics.advance = static_cast(advance) - x; + int stride = advance; + if (advanceF != static_cast(advance)) { + const int candidate = static_cast((static_cast(right) - rightF) + advanceF); + stride = right + 1; + if (advanceF <= rightF) stride = candidate; + if (stride < candidate) stride = candidate; + } + result->ImageMetrics.stride = static_cast(stride) - x; + result->ImageMetrics.width = static_cast(std::max(0, right - left)); + result->ImageMetrics.height = static_cast(std::max(0, top - bottom)); +} + +int RenderGlyphIndexToSurface(FontObj& obj, std::uint32_t glyphIndex, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + ClearRenderOutputs(metrics, result); + if (!surface->buffer || surface->width <= 0 || surface->height <= 0 || surface->widthByte <= 0 || surface->pixelSizeByte <= 0) return SCE_FONT_ERROR_NO_SUPPORT_SURFACE; + const int bytesPerPixel = surface->pixelSizeByte; + if (bytesPerPixel != 1 && bytesPerPixel != 4) return SCE_FONT_ERROR_NO_SUPPORT_SURFACE; + const auto face = static_cast(obj.ft_face); + if (!face || !face->size) return SCE_FONT_ERROR_NO_SUPPORT_GLYPH; + FT_Vector delta{}; + delta.x = static_cast(static_cast((x - std::floor(x)) * 64.0f)); + delta.y = static_cast(-static_cast((y - std::floor(y)) * 64.0f)); + if (obj.shift_units_x != 0 || obj.shift_units_y != 0) { + delta.x += static_cast(RoundMulFixed(obj.shift_units_x, static_cast(face->size->metrics.x_scale))); + delta.y += static_cast(RoundMulFixed(obj.shift_units_y, static_cast(face->size->metrics.y_scale))); + } + FT_Set_Transform(face, nullptr, &delta); + const FT_Error error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT | FT_LOAD_RENDER); + FT_Set_Transform(face, nullptr, nullptr); + if (error != 0 || !face->glyph) return SCE_FONT_ERROR_NO_SUPPORT_GLYPH; + const FT_GlyphSlot slot = face->glyph; + const int glyphW = static_cast(slot->bitmap.width); + const int glyphH = static_cast(slot->bitmap.rows); + metrics->width = static_cast(slot->metrics.width) / 64.0f; + metrics->height = static_cast(slot->metrics.height) / 64.0f; + metrics->Horizontal.bearingX = static_cast(slot->metrics.horiBearingX) / 64.0f; + metrics->Horizontal.bearingY = static_cast(slot->metrics.horiBearingY) / 64.0f; + metrics->Horizontal.advance = static_cast(slot->metrics.horiAdvance) / 64.0f; + std::vector bitmap(static_cast(glyphW) * static_cast(glyphH)); + if (glyphW > 0 && glyphH > 0) { + const int pitch = slot->bitmap.pitch; + const auto* source = slot->bitmap.buffer; + for (int row = 0; row < glyphH; ++row) { + const std::uint8_t* sourceRow = source + static_cast(row) * pitch; + std::uint8_t* targetRow = bitmap.data() + static_cast(row) * static_cast(glyphW); + if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + for (int column = 0; column < glyphW; ++column) targetRow[column] = (sourceRow[column >> 3] & (0x80u >> (column & 7))) ? 0xFF : 0x00; + } else { + std::memcpy(targetRow, sourceRow, static_cast(glyphW)); + } + } + } + const int destX = static_cast(std::floor(x)) + slot->bitmap_left; + const int destY = static_cast(std::floor(y)) - slot->bitmap_top; + const int clipX0 = std::clamp(static_cast(surface->sc_x0), 0, surface->width); + const int clipY0 = std::clamp(static_cast(surface->sc_y0), 0, surface->height); + const int clipX1 = std::clamp(static_cast(surface->sc_x1), 0, surface->width); + const int clipY1 = std::clamp(static_cast(surface->sc_y1), 0, surface->height); + int updateX = destX; + int updateY = destY; + int updateW = 0; + int updateH = 0; + if (glyphW > 0 && glyphH > 0 && clipX1 > clipX0 && clipY1 > clipY0) { + auto* targetBase = static_cast(surface->buffer); + const int startRow = std::max(destY, clipY0); + const int endRow = std::min(destY + glyphH, clipY1); + const int startColumn = std::max(destX, clipX0); + const int endColumn = std::min(destX + glyphW, clipX1); + updateX = startColumn; + updateY = startRow; + updateW = std::max(0, endColumn - startColumn); + updateH = std::max(0, endRow - startRow); + for (int row = startRow; row < endRow; ++row) { + const std::uint8_t* sourceRow = bitmap.data() + static_cast(row - destY) * static_cast(glyphW); + std::uint8_t* targetRow = targetBase + static_cast(row) * static_cast(surface->widthByte); + for (int column = startColumn; column < endColumn; ++column) { + const std::uint8_t coverage = sourceRow[column - destX]; + std::memset(targetRow + static_cast(column) * static_cast(bytesPerPixel), coverage, static_cast(bytesPerPixel)); + } + } + } + result->stage = nullptr; + result->SurfaceImage.widthByte = static_cast(surface->widthByte); + result->SurfaceImage.pixelSizeByte = static_cast(surface->pixelSizeByte); + result->SurfaceImage.pixelFormat = 0; + result->SurfaceImage.pad16 = 0; + result->UpdateRect.x = static_cast(std::max(updateX, 0)); + result->UpdateRect.y = static_cast(std::max(updateY, 0)); + result->UpdateRect.w = static_cast(std::max(updateW, 0)); + result->UpdateRect.h = static_cast(std::max(updateH, 0)); + result->SurfaceImage.address = static_cast(surface->buffer) + static_cast(result->UpdateRect.y) * static_cast(surface->widthByte) + static_cast(result->UpdateRect.x) * static_cast(bytesPerPixel); + FillImageMetrics(result, metrics, x, y); + return SCE_FONT_OK; +} + +} + +int Font::ComputeHorizontalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords) { + ClearHorizontalLayout(outWords); + const auto fail = [&] { + ClearHorizontalLayout(outWords); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + }; + auto* font = GetNativeFont(handle); + if (!font) return fail(); + auto* library = static_cast(font->library); + if (!library || !library->external_fonts_ctx) return fail(); + const SysDriver* driver = library->sys_driver; + if (!driver || !driver->set_char_with_dpi || !driver->set_char_default_dpi || !driver->compute_layout || !style) return fail(); + const std::uint32_t modeLow = font->flags & 0x0Fu; + const std::uint32_t fontId = font->open_info.ctx_entry_index; + float baselineMax = 0.0f; + float deltaMax = 0.0f; + float effectForBaseline = 0.0f; + float effectForDelta = 0.0f; + FloatPair accumulatedAdjust{}; + FloatPair accumulatedBounds{}; + if (static_cast(fontId) >= 0) { + FontObj* head = nullptr; + std::uint32_t lockWord = 0; + auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord); + if (!entry) return fail(); + FontObj* match = FindSubFont(head, font->open_info.sub_font_index); + int rc = SCE_FONT_ERROR_FATAL; + std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {}; + if ((lockWord & OPEN_BIT) != 0 && (lockWord & COUNT_MASK) != 0 && match) { + float scaleX = style->scale_w; + float scaleY = style->scale_h; + match->font_handle = handle; + match->shift_units_x = 0; + match->shift_units_y = 0; + rc = SetDriverCharSize(driver, match, style, scaleX, scaleY); + if (rc == SCE_FONT_OK) { + UpdateShiftCache(match); + rc = driver->compute_layout(match, style, layout); + } + } + ReleaseFontCtxEntryLock(entry, modeLow, lockWord); + if (rc != SCE_FONT_OK) return fail(); + const float lineAdvance = LoadFloat(layout, HORIZONTAL_LINE_ADVANCE); + const float baseline = LoadFloat(layout, HORIZONTAL_BASELINE); + const float effectHeight = LoadFloat(layout, HORIZONTAL_EFFECT_HEIGHT); + const FloatPair bounds{LoadFloat(layout, HORIZONTAL_X_BOUND_LO), LoadFloat(layout, HORIZONTAL_X_BOUND_HI)}; + const FloatPair adjust{LoadFloat(layout, HORIZONTAL_HALF_EFFECT_WIDTH), LoadFloat(layout, HORIZONTAL_LEFT_ADJUST)}; + const FloatPair previousAdjust = accumulatedAdjust; + const FloatPair previousBounds = accumulatedBounds; + const bool baselineUpdate = OrderedLess(baselineMax, baseline); + baselineMax = MaxSs(baseline, baselineMax); + if (baselineUpdate) effectForBaseline = effectHeight; + const float delta = lineAdvance - baseline; + const bool deltaUpdate = OrderedLess(deltaMax, delta); + deltaMax = MaxSs(delta, deltaMax); + if (deltaUpdate) effectForDelta = effectHeight; + const FloatPair difference{bounds.lo - adjust.hi, bounds.hi - adjust.lo}; + const FloatPair sum{previousAdjust.lo + previousBounds.lo, previousAdjust.hi + previousBounds.hi}; + if (OrderedLess(difference.lo, sum.lo)) { + accumulatedBounds.lo = bounds.lo; + accumulatedAdjust.lo = FlipSign(adjust.hi); + } + if (OrderedLess(difference.hi, sum.hi)) { + accumulatedBounds.hi = bounds.hi; + accumulatedAdjust.hi = adjust.lo; + } + } + if (!outWords) return fail(); + StoreFloat(outWords, HORIZONTAL_BASELINE, baselineMax); + StoreFloat(outWords, HORIZONTAL_LINE_ADVANCE, baselineMax + deltaMax); + StoreFloat(outWords, HORIZONTAL_X_BOUND_LO, accumulatedBounds.lo); + StoreFloat(outWords, HORIZONTAL_X_BOUND_HI, accumulatedBounds.hi); + StoreFloat(outWords, HORIZONTAL_EFFECT_HEIGHT, MaxSs(effectForBaseline, effectForDelta)); + StoreFloat(outWords, HORIZONTAL_LEFT_ADJUST, accumulatedAdjust.lo); + StoreFloat(outWords, HORIZONTAL_HALF_EFFECT_WIDTH, accumulatedAdjust.hi); + return SCE_FONT_OK; +} + +int Font::ComputeVerticalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords) { + const auto fail = [&](int rc) { + ClearVerticalLayout(outWords); + return rc; + }; + auto* font = GetNativeFont(handle); + auto* library = font ? static_cast(font->library) : nullptr; + if (!library || !library->external_fonts_ctx || !library->sys_driver) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + const SysDriver* driver = library->sys_driver; + const std::uint32_t modeLow = font->flags & 0x0Fu; + const std::uint32_t fontId = font->open_info.ctx_entry_index; + float accNegOffsetMax = 0.0f; + float accSumMax = 0.0f; + float spanForNegOffset = 0.0f; + float spanForSum = 0.0f; + float accMetrics08Max = 0.0f; + float accMetrics0CMax = 0.0f; + float accDiffMin = 0.0f; + float accTempMin = 0.0f; + if (static_cast(fontId) >= 0) { + FontObj* head = nullptr; + std::uint32_t lockWord = 0; + auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord); + FontObj* obj = FindSubFont(head, font->open_info.sub_font_index); + if (!entry || !obj) { + if (entry) ReleaseFontCtxEntryLock(entry, modeLow, lockWord); + return fail(SCE_FONT_ERROR_FATAL); + } + int rc = SCE_FONT_ERROR_FATAL; + if ((lockWord & OPEN_BIT) != 0 && (lockWord & COUNT_MASK) != 0) { + float scaleX = style ? style->scale_w : 0.0f; + float scaleY = style ? style->scale_h : 0.0f; + obj->font_handle = handle; + obj->shift_units_x = 0; + obj->shift_units_y = 0; + int callRc = SCE_FONT_ERROR_FATAL; + if (style && style->scale_unit == 0) { + if (driver->set_char_default_dpi) callRc = driver->set_char_default_dpi(obj, scaleX, scaleY, &scaleX, &scaleY); + } else if (driver->set_char_with_dpi) { + callRc = driver->set_char_with_dpi(obj, style ? style->dpi_x : 0u, style ? style->dpi_y : 0u, scaleX, scaleY, &scaleX, &scaleY); + } + std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {}; + if (callRc == SCE_FONT_OK) { + UpdateShiftCache(obj); + callRc = driver->compute_layout_alt ? driver->compute_layout_alt(obj, style, layout) : SCE_FONT_ERROR_FATAL; + } + if (callRc == SCE_FONT_OK) { + const float metrics00 = LoadFloat(layout, VERTICAL_COLUMN_ADVANCE); + const float offsetCandidate = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X); + const float metrics08 = LoadFloat(layout, VERTICAL_METRICS_0X08); + const float metrics0C = LoadFloat(layout, VERTICAL_METRICS_0X0C); + const float spanCandidate = LoadFloat(layout, VERTICAL_DECORATION_SPAN); + const float extra08 = LoadFloat(layout, VERTICAL_DECORATION_0X08); + const float extra0C = LoadFloat(layout, VERTICAL_DECORATION_0X0C); + const float negOffset = -offsetCandidate; + const float sum = offsetCandidate + metrics00; + if (accNegOffsetMax < negOffset) spanForNegOffset = spanCandidate; + accNegOffsetMax = std::max(accNegOffsetMax, negOffset); + if (accSumMax < sum) spanForSum = spanCandidate; + accSumMax = std::max(accSumMax, sum); + accDiffMin = std::min(extra08 - offsetCandidate, accDiffMin); + accMetrics08Max = std::max(metrics08, accMetrics08Max); + accTempMin = std::min(sum + extra0C, accDiffMin); + accMetrics0CMax = std::max(metrics0C, accMetrics0CMax); + rc = SCE_FONT_OK; + } else { + rc = callRc; + } + } + ReleaseFontCtxEntryLock(entry, modeLow, lockWord); + if (rc != SCE_FONT_OK) return fail(rc); + } + if (outWords) { + StoreFloat(outWords, VERTICAL_COLUMN_ADVANCE, accNegOffsetMax + accSumMax); + StoreFloat(outWords, VERTICAL_BASELINE_OFFSET_X, -accNegOffsetMax); + StoreFloat(outWords, VERTICAL_METRICS_0X08, accMetrics08Max); + StoreFloat(outWords, VERTICAL_METRICS_0X0C, accMetrics0CMax); + StoreFloat(outWords, VERTICAL_DECORATION_SPAN, std::max(spanForSum, spanForNegOffset)); + StoreFloat(outWords, VERTICAL_DECORATION_0X08, accTempMin - accNegOffsetMax); + StoreFloat(outWords, VERTICAL_DECORATION_0X0C, -accSumMax); + } + return SCE_FONT_OK; +} + +int Font::GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMetrics* metrics, bool useCachedStyle) { + const auto clearMetrics = [&] { + if (metrics) *metrics = {}; + }; + auto* font = GetNativeFont(handle); + if (!font || font->magic != HANDLE_MAGIC) { + clearMetrics(); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { + clearMetrics(); + return SCE_FONT_ERROR_NO_SUPPORT_CODE; + } + if (!metrics) return SCE_FONT_ERROR_INVALID_PARAMETER; + std::uint32_t previousFontLock = 0; + if (!AcquireFontLock(font, previousFontLock)) { + clearMetrics(); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + std::uint32_t previousCachedLock = 0; + if (useCachedStyle && !AcquireCachedStyleLock(font, previousCachedLock)) { + ReleaseFontLock(font, previousFontLock); + clearMetrics(); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + const auto finish = [&](int rc) { + if (useCachedStyle) ReleaseCachedStyleLock(font, previousCachedLock); + ReleaseFontLock(font, previousFontLock); + if (rc != SCE_FONT_OK) clearMetrics(); + return rc; + }; + FontState* state = TryGetState(handle); + if (!state) return finish(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + if (useCachedStyle && !font->renderer) return finish(SCE_FONT_ERROR_NOT_BOUND_RENDERER); + const StyleStateBlock* style = useCachedStyle ? &font->cached_style.state : &font->style; + float scaleW = 0.0f; + float scaleH = 0.0f; + const int scaleRc = StyleStateGetScalePixel(style, &scaleW, &scaleH); + if (scaleRc != SCE_FONT_OK) return finish(scaleRc); + const FT_Face face = state->face; + if (!face) return finish(SCE_FONT_ERROR_NO_SUPPORT_FUNCTION); + const FT_UInt glyphIndex = ResolveGlyphIndexWithFallback(face, code); + if (glyphIndex == 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH); + const auto charW = static_cast(static_cast(scaleW * 64.0f)); + const auto charH = static_cast(static_cast(scaleH * 64.0f)); + FT_F26Dot6 usedW = charW; + FT_F26Dot6 usedH = charH; + if (SetCharSizeCompat(face, charW, charH, 72, 72, &usedW, &usedH) != 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH); + FT_Set_Transform(face, nullptr, nullptr); + if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT) != 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH); + const auto ratio = [](FT_F26Dot6 requested, FT_F26Dot6 applied) { + if (requested == 0 || applied == 0) return 1.0f; + const double value = static_cast(std::llabs(static_cast(requested))) / static_cast(std::llabs(static_cast(applied))); + return std::isfinite(value) && value > 0.0 ? static_cast(value) : 1.0f; + }; + const float ratioX = ratio(charW, usedW); + const float ratioY = ratio(charH, usedH); + const FT_GlyphSlot slot = face->glyph; + metrics->width = static_cast(slot->metrics.width) / 64.0f * ratioX; + metrics->height = static_cast(slot->metrics.height) / 64.0f * ratioY; + metrics->Horizontal.bearingX = static_cast(slot->metrics.horiBearingX) / 64.0f * ratioX; + metrics->Horizontal.bearingY = static_cast(slot->metrics.horiBearingY) / 64.0f * ratioY; + metrics->Horizontal.advance = static_cast(slot->metrics.horiAdvance) / 64.0f * ratioX; + metrics->Vertical.bearingX = 0.0f; + metrics->Vertical.bearingY = 0.0f; + metrics->Vertical.advance = 0.0f; + return finish(SCE_FONT_OK); +} + +int Font::RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + const auto fail = [&](int rc) { + ClearRenderOutputs(metrics, result); + return rc; + }; + if (!handle) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + if (code == 0) return fail(SCE_FONT_ERROR_NO_SUPPORT_CODE); + if (!surface || !metrics || !result) return fail(SCE_FONT_ERROR_INVALID_PARAMETER); + auto* font = GetNativeFont(handle); + if (font->magic != HANDLE_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + if (!font->renderer) return fail(SCE_FONT_ERROR_NOT_BOUND_RENDERER); + auto* library = static_cast(font->library); + if (!library || library->magic != LIBRARY_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + const SysDriver* driver = library->sys_driver; + if (!driver || !driver->glyph_index || !driver->set_char_with_dpi || !driver->set_char_default_dpi) return fail(SCE_FONT_ERROR_FATAL); + const std::uint32_t modeLow = font->flags & 0x0Fu; + StyleStateBlock surfaceStyle{}; + const StyleStateBlock* style = ResolveSurfaceStyleState(&font->cached_style.state, surface, surfaceStyle); + FontObj* obj = nullptr; + std::uint32_t lockWord = 0; + auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, font->open_info.ctx_entry_index, modeLow, &obj, &lockWord); + obj = FindSubFont(obj, font->open_info.sub_font_index); + if (!entry || !obj) { + if (entry) ReleaseFontCtxEntryLock(entry, modeLow, lockWord); + return fail(SCE_FONT_ERROR_FATAL); + } + const auto finish = [&](int rc) { + ReleaseFontCtxEntryLock(entry, modeLow, lockWord); + if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result); + return rc; + }; + if ((lockWord & OPEN_BIT) == 0 || (lockWord & COUNT_MASK) == 0) return finish(SCE_FONT_ERROR_FATAL); + obj->reserved_0x04 = static_cast(font->flags >> 15); + obj->font_handle = handle; + obj->shift_units_x = 0; + obj->shift_units_y = 0; + obj->layout_seed_pair = 0; + StyleStateGetScalePixel(style, &obj->scale_x_0x50, &obj->scale_y_0x54); + float outScaleX = 0.0f; + float outScaleY = 0.0f; + int rc; + if (style->scale_unit == 0) { + rc = driver->set_char_default_dpi(obj, style->scale_w, style->scale_h, &outScaleX, &outScaleY); + } else { + rc = driver->set_char_with_dpi(obj, style->dpi_x ? style->dpi_x : 0x48, style->dpi_y ? style->dpi_y : 0x48, style->scale_w, style->scale_h, &outScaleX, &outScaleY); + } + if (rc != SCE_FONT_OK) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH); + std::uint32_t glyphIndex = 0; + rc = driver->glyph_index(obj, code, &glyphIndex); + if (rc != SCE_FONT_OK || glyphIndex == 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH); + return finish(RenderGlyphIndexToSurface(*obj, glyphIndex, surface, x, y, metrics, result)); +} + +bool Font::BuildTrueOutline(GeneratedGlyph& glyph) { + FontState* state = TryGetState(glyph.owner); + if (!state || !state->face) return false; + const auto* font = GetNativeFont(glyph.owner); + if (font->magic != HANDLE_MAGIC) return false; + const FT_Face face = state->face; + const FT_UInt glyphIndex = FT_Get_Char_Index(face, glyph.codepoint); + if (glyphIndex == 0) return false; + const auto charW = static_cast(static_cast(glyph.glyph.scale_x * 64.0f)); + const auto charH = static_cast(static_cast(glyph.glyph.base_scale * 64.0f)); + if (FT_Set_Char_Size(face, charW, charH, 72, 72) != 0) return false; + FT_Set_Transform(face, nullptr, nullptr); + if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP) != 0) return false; + const FT_GlyphSlot slot = face->glyph; + if (!slot || slot->format != FT_GLYPH_FORMAT_OUTLINE) return false; + const FT_Outline& outline = slot->outline; + if (outline.n_points <= 0 || outline.n_contours <= 0 || !outline.points || !outline.tags || !outline.contours) return false; + glyph.outlinePoints.clear(); + glyph.outlineTags.clear(); + glyph.outlineContours.clear(); + for (int i = 0; i < outline.n_points; ++i) { + glyph.outlinePoints.push_back({static_cast(outline.points[i].x) / 64.0f, static_cast(outline.points[i].y) / 64.0f}); + glyph.outlineTags.push_back(FT_CURVE_TAG(outline.tags[i]) == FT_CURVE_TAG_ON ? 1 : 0); + } + for (int i = 0; i < outline.n_contours; ++i) glyph.outlineContours.push_back(static_cast(outline.contours[i])); + glyph.outline.points_ptr = glyph.outlinePoints.data(); + glyph.outline.tags_ptr = glyph.outlineTags.data(); + glyph.outline.contour_end_idx = glyph.outlineContours.data(); + glyph.outline.points_cnt = static_cast(glyph.outlinePoints.size()); + glyph.outline.contours_cnt = static_cast(glyph.outlineContours.size()); + glyph.outlineInitialized = true; + return true; +} diff --git a/core/libs/prx/libSceFont/src/Library.cpp b/core/libs/prx/libSceFont/src/Library.cpp new file mode 100644 index 000000000..46b421dcb --- /dev/null +++ b/core/libs/prx/libSceFont/src/Library.cpp @@ -0,0 +1,378 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +std::uint32_t EffectiveEdition(std::uint64_t edition) { + return static_cast(edition >> 32); +} + +std::uint32_t* AcquireDeviceCache(FontLibNative* library) { + auto* const busy = reinterpret_cast(std::numeric_limits::max()); + std::atomic_ref slot(library->device_cache_buf); + for (;;) { + std::uint32_t* current = slot.load(std::memory_order_acquire); + if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current; + Backoff(); + } +} + +void ReleaseDeviceCache(FontLibNative* library, std::uint32_t* cache) { + std::atomic_ref(library->device_cache_buf).store(cache, std::memory_order_release); +} + +void* AcquireRendererSelection(RendererNative* renderer) { + auto* const busy = reinterpret_cast(std::numeric_limits::max()); + std::atomic_ref slot(renderer->selection); + for (;;) { + void* current = slot.load(std::memory_order_acquire); + if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current; + Backoff(); + } +} + +void* CreateFontContext(FontLibNative* library, std::uint32_t entries, std::uint32_t size) { + void* ctx = library->iface->alloc(library->alloc_ctx, size); + if (!ctx) return nullptr; + std::memset(ctx, 0, size); + auto* header = static_cast(ctx); + header->lock_word = 0; + header->max_entries = entries; + header->base = entries != 0 ? static_cast(ctx) + sizeof(FontCtxHeader) : nullptr; + return ctx; +} + +int SupportFonts(FontLibrary library, void* FontLibNative::*slot, std::uint32_t entries, std::uint32_t size, std::uint32_t formats) { + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + std::uint32_t previous = 0; + if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY; + const auto finish = [&](int rc) { + ReleaseLibraryLock(lib, previous); + return rc; + }; + if (lib->*slot) return finish(SCE_FONT_ERROR_ALREADY_SPECIFIED); + void* ctx = CreateFontContext(lib, entries, size); + if (!ctx) return finish(SCE_FONT_ERROR_ALLOCATION_FAILED); + if (!lib->sys_driver || !lib->sys_driver->support_formats) { + lib->iface->dealloc(lib->alloc_ctx, ctx); + return finish(SCE_FONT_ERROR_INVALID_LIBRARY); + } + const int rc = lib->sys_driver->support_formats(lib, formats); + if (rc != SCE_FONT_OK) { + lib->iface->dealloc(lib->alloc_ctx, ctx); + return finish(rc); + } + lib->*slot = ctx; + return finish(SCE_FONT_OK); +} + +} + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontMemoryInit(FontMemory* memory, void* regionAddress, std::uint32_t regionSize, const FontMemoryInterface* iface, void* mspaceObject, FontMemoryDestroyFunction destroyCallback, void* destroyObject) { + if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!iface) { + memory->mem_kind = 0; + if (!regionAddress || regionSize == 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + } + memory->mem_kind = MEMORY_MAGIC; + memory->attr_bits = 0; + memory->region_size = regionSize; + memory->region_base = regionAddress; + memory->mspace_handle = mspaceObject; + memory->iface = iface; + memory->on_destroy = destroyCallback; + memory->destroy_ctx = destroyObject; + memory->some_ctx1 = nullptr; + memory->some_ctx2 = mspaceObject; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontMemoryTerm(FontMemory* memory) { + if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (memory->mem_kind != MEMORY_MAGIC) return SCE_FONT_ERROR_INVALID_MEMORY; + if (static_cast(memory->attr_bits & 0xFF) < 0) { + if (memory->iface && memory->iface->mspace_destroy) memory->iface->mspace_destroy(memory->some_ctx2, memory->mspace_handle); + std::memset(memory, 0, sizeof(*memory)); + return SCE_FONT_OK; + } + if (memory->on_destroy) { + memory->mem_kind = 0; + memory->on_destroy(memory, memory->mspace_handle, memory->destroy_ctx); + return SCE_FONT_OK; + } + std::memset(memory, 0, sizeof(*memory)); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, const void* createParams, std::uint64_t edition, FontLibrary* pLibrary) { + if (pLibrary) *pLibrary = nullptr; + if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (memory->mem_kind != MEMORY_MAGIC || !memory->iface || !memory->iface->alloc || !memory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY; + if (!createParams || !pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER; + const auto allocFn = memory->iface->alloc; + const auto freeFn = memory->iface->dealloc; + auto* lib = static_cast(allocFn(memory->mspace_handle, sizeof(FontLibNative))); + if (!lib) return SCE_FONT_ERROR_ALLOCATION_FAILED; + void* workspace = allocFn(memory->mspace_handle, 0x4000); + if (!workspace) { + freeFn(memory->mspace_handle, lib); + return SCE_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(lib, 0, sizeof(FontLibNative)); + lib->mem_kind = MEMORY_MAGIC; + lib->region_size = memory->region_size; + lib->region_base = memory->region_base; + lib->alloc_ctx = memory->mspace_handle; + lib->iface = memory->iface; + lib->alloc_fn = memory->iface->alloc; + lib->dealloc_fn = memory->iface->dealloc; + lib->realloc_fn = memory->iface->realloc_fn; + lib->calloc_fn = memory->iface->calloc_fn; + lib->sys_driver = static_cast(createParams); + lib->workspace_size = 0x4000; + lib->workspace = workspace; + lib->list_head_ptr = &lib->list_head; + lib->list_head = nullptr; + const auto initFn = lib->sys_driver->init; + const int rc = initFn ? initFn(memory, lib) : SCE_FONT_ERROR_INVALID_PARAMETER; + if (rc != SCE_FONT_OK) { + freeFn(memory->mspace_handle, workspace); + freeFn(memory->mspace_handle, lib); + return rc; + } + const std::uint32_t version = EffectiveEdition(edition); + if (version > 0x92FFFFu) { + if (version < 0x1500000u) { + lib->feature_word |= 1u | 2u | 4u; + } else if (version < 0x2000000u) { + lib->feature_word |= 2u | 4u; + } else if (version <= 0x2000070u) { + lib->feature_word |= 4u; + } + } + lib->magic = LIBRARY_MAGIC; + *pLibrary = lib; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, const void* createParams, FontLibrary* pLibrary) { + return sceFontCreateLibraryWithEdition(memory, createParams, 0, pLibrary); +} + +int APS5_VABI sceFontDestroyLibrary(FontLibrary* pLibrary) { + if (!pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* lib = static_cast(*pLibrary); + if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + if (lib->sys_driver && lib->sys_driver->term) lib->sys_driver->term(lib); + const auto freeFn = lib->iface->dealloc; + void* allocCtx = lib->alloc_ctx; + if ((lib->flags & 1u) != 0 && lib->device_cache_buf) freeFn(allocCtx, lib->device_cache_buf); + if (lib->external_fonts_ctx) freeFn(allocCtx, lib->external_fonts_ctx); + if (lib->sysfonts_ctx) freeFn(allocCtx, lib->sysfonts_ctx); + if (lib->workspace) freeFn(allocCtx, lib->workspace); + lib->magic = 0; + freeFn(allocCtx, lib); + *pLibrary = nullptr; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, const void* createParams, std::uint64_t edition, FontRenderer* pRenderer) { + if (!memory) { + if (pRenderer) *pRenderer = nullptr; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + int rc = SCE_FONT_ERROR_INVALID_MEMORY; + if (memory->mem_kind == MEMORY_MAGIC && memory->iface && memory->iface->alloc && memory->iface->dealloc) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + if (createParams && pRenderer) { + const auto allocFn = memory->iface->alloc; + const auto freeFn = memory->iface->dealloc; + const auto* selection = static_cast(createParams); + void* rendererMemory = allocFn(memory->mspace_handle, selection->size); + void* workspace = allocFn(memory->mspace_handle, 0x4000); + rc = SCE_FONT_ERROR_ALLOCATION_FAILED; + if (rendererMemory && workspace) { + auto* renderer = static_cast(rendererMemory); + std::memset(renderer, 0, offsetof(RendererNative, mem_kind)); + renderer->magic = RENDERER_MAGIC; + renderer->mem_kind = MEMORY_MAGIC; + renderer->region_size = memory->region_size; + renderer->region_base = memory->region_base; + renderer->alloc_ctx = memory->mspace_handle; + renderer->mem_iface = memory->iface; + renderer->alloc_fn = memory->iface->alloc; + renderer->free_fn = memory->iface->dealloc; + renderer->realloc_fn = memory->iface->realloc_fn; + renderer->calloc_fn = memory->iface->calloc_fn; + renderer->selection = const_cast(createParams); + renderer->outline_magic_0x90 = 0x400000000000ull; + renderer->workspace = workspace; + renderer->workspace_size = 0x4000; + renderer->reserved_a8 = 0; + renderer->outline_policy_flag = 0; + rc = selection->create_fn ? selection->create_fn(renderer) : SCE_FONT_ERROR_FATAL; + if (rc == SCE_FONT_OK) { + if (EffectiveEdition(edition) - 0x930000u < 0x1BD0000u) { + renderer->feature_byte_0x0c |= 1u; + renderer->outline_policy_flag = 1; + } + *pRenderer = renderer; + return SCE_FONT_OK; + } + } + if (workspace) freeFn(memory->mspace_handle, workspace); + if (rendererMemory) freeFn(memory->mspace_handle, rendererMemory); + } + } + if (pRenderer) *pRenderer = nullptr; + return rc; +} + +int APS5_VABI sceFontCreateRenderer(const FontMemory* memory, const void* createParams, FontRenderer* pRenderer) { + return sceFontCreateRendererWithEdition(memory, createParams, 0, pRenderer); +} + +int APS5_VABI sceFontDestroyRenderer(FontRenderer* pRenderer) { + if (!pRenderer) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* renderer = static_cast(*pRenderer); + if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER; + const auto* selection = static_cast(AcquireRendererSelection(renderer)); + int rc = SCE_FONT_ERROR_FATAL; + if (selection && selection->destroy_fn) rc = selection->destroy_fn(renderer); + renderer->selection = nullptr; + const auto freeFn = renderer->free_fn; + void* allocCtx = renderer->alloc_ctx; + if (renderer->workspace) freeFn(allocCtx, renderer->workspace); + freeFn(allocCtx, renderer); + *pRenderer = nullptr; + return rc; +} + +int APS5_VABI sceFontRendererGetOutlineBufferSize(FontRenderer fontRenderer, std::uint32_t* size) { + if (!size) return SCE_FONT_ERROR_INVALID_PARAMETER; + *size = 0; + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER; + *size = static_cast(renderer->workspace_size); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontRendererResetOutlineBuffer(FontRenderer fontRenderer) { + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER; + if (renderer->workspace && renderer->workspace_size) std::memset(renderer->workspace, 0, static_cast(renderer->workspace_size)); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontRendererSetOutlineBufferPolicy(FontRenderer fontRenderer, std::uint64_t bufferPolicy, std::uint32_t basalSize, std::uint32_t limitSize) { + (void)bufferPolicy; + auto* renderer = static_cast(fontRenderer); + if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER; + if (limitSize != 0 && basalSize > limitSize) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!renderer->alloc_fn || !renderer->free_fn || !renderer->alloc_ctx) return SCE_FONT_ERROR_INVALID_MEMORY; + auto desiredSize = std::max(static_cast(renderer->workspace_size), static_cast(basalSize)); + if (limitSize != 0) desiredSize = std::min(desiredSize, static_cast(limitSize)); + if (desiredSize == 0) desiredSize = 0x4000; + if (!renderer->workspace || renderer->workspace_size != desiredSize) { + void* workspace = renderer->alloc_fn(renderer->alloc_ctx, static_cast(desiredSize)); + if (!workspace) return SCE_FONT_ERROR_ALLOCATION_FAILED; + if (renderer->workspace) renderer->free_fn(renderer->alloc_ctx, renderer->workspace); + renderer->workspace = workspace; + renderer->workspace_size = desiredSize; + } + return SCE_FONT_OK; +} + +int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, std::uint32_t size) { + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + std::uint32_t previous = 0; + if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY; + std::uint32_t* current = AcquireDeviceCache(lib); + std::uint32_t* stored = current; + int rc; + if (current) { + rc = SCE_FONT_ERROR_ALREADY_ATTACHED; + } else if (size < 0x1020) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } else { + auto* header = static_cast(buffer); + std::uint32_t owned = 0; + if (!header) { + header = static_cast(lib->iface->alloc(lib->alloc_ctx, size)); + owned = 1; + } + if (!header) { + rc = SCE_FONT_ERROR_ALLOCATION_FAILED; + } else { + const std::uint32_t pageCount = (size - 0x1000) >> 12; + header[0] = size; + header[1] = pageCount; + header[2] = 0; + header[3] = pageCount; + const std::uint64_t pageInfo = 0x0FF800001000ull; + std::memcpy(header + 4, &pageInfo, sizeof(pageInfo)); + if (size - 0x1000 > 0x0FFF) { + lib->flags |= owned; + stored = header; + rc = SCE_FONT_OK; + } else { + if (owned) lib->iface->dealloc(lib->alloc_ctx, header); + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } + } + } + ReleaseDeviceCache(lib, stored); + ReleaseLibraryLock(lib, previous); + return rc; +} + +int APS5_VABI sceFontClearDeviceCache(FontLibrary library) { + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + std::uint32_t previous = 0; + if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY; + std::uint32_t* current = AcquireDeviceCache(lib); + int rc = SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER; + if (current) { + current[3] = current[1]; + current[2] = 0; + rc = SCE_FONT_OK; + } + ReleaseDeviceCache(lib, current); + ReleaseLibraryLock(lib, previous); + return rc; +} + +int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, std::uint32_t fontMax, std::uint32_t formats) { + return SupportFonts(library, &FontLibNative::external_fonts_ctx, fontMax, (fontMax << 6) | 0x20u, formats); +} + +int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) { + return SupportFonts(library, &FontLibNative::sysfonts_ctx, 0x40, 0x1020, 0x52); +} + +int APS5_VABI sceFontSetFontsOpenMode(FontLibrary library, std::uint32_t openMode) { + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + if (openMode > 2) return SCE_FONT_ERROR_INVALID_PARAMETER; + return SCE_FONT_OK; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/Open.cpp b/core/libs/prx/libSceFont/src/Open.cpp new file mode 100644 index 000000000..b92f7ebf9 --- /dev/null +++ b/core/libs/prx/libSceFont/src/Open.cpp @@ -0,0 +1,509 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +constexpr std::array SYSTEM_FONT_SET_TYPES = { + 0x18070043u, 0x18070044u, 0x18070045u, 0x18070047u, 0x18070053u, 0x18070054u, + 0x18070055u, 0x18070057u, 0x180700C3u, 0x180700C4u, 0x180700C5u, 0x180700C7u, + 0x18070444u, 0x18070447u, 0x18070454u, 0x18070457u, 0x180704C4u, 0x180704C7u, + 0x18071053u, 0x18071054u, 0x18071055u, 0x18071057u, 0x18071454u, 0x18071457u, + 0x18072444u, 0x18072447u, 0x180724C4u, 0x180724C7u, 0x18073454u, 0x18073457u, + 0x180734D4u, 0x180734D7u, 0x18078044u, 0x180780C4u, 0x18079054u, 0x1807A044u, + 0x1807A0C4u, 0x1807A444u, 0x1807A4C4u, 0x1807AC44u, 0x1807ACC4u, 0x1807B054u, + 0x1807B0D4u, 0x1807B454u, 0x1807B4D4u, 0x1807BC54u, 0x1807BCD4u, 0x18080444u, + 0x18080447u, 0x18080454u, 0x18080457u, 0x180804C4u, 0x180804C7u, 0x18081454u, + 0x18081457u, 0x18082444u, 0x18082447u, 0x180824C4u, 0x180824C7u, 0x18083454u, + 0x18083457u, 0x180834D4u, 0x180834D7u, 0x1808A444u, 0x1808A4C4u, 0x1808B454u, + 0x1808B4D4u, 0x180C8044u, 0x180C80C4u, 0x180C9054u, 0x180CA044u, 0x180CA0C4u, + 0x180CAC44u, 0x180CACC4u, 0x180CB054u, 0x180CB0D4u, 0x180CBC54u, 0x180CBCD4u, + 0x18170043u, 0x18170044u, 0x18170045u, 0x18170047u, 0x18170444u, 0x18170447u, + 0x18370044u, 0x18370047u, 0x18370444u, 0x18370447u, +}; + +constexpr std::uint16_t HANDLE_OWNED = 0x10; + +struct OpenRequest { + FontLibNative* library; + std::uint32_t modeLow; + std::uint32_t driverMode; + const void* source; + std::uint32_t sourceSize; + bool matchAddress; + std::uint32_t subFontIndex; + std::int32_t uniqueId; +}; + +FontHandle PrepareHandle(FontLibNative* library, FontHandle existing) { + if (existing) { + GetNativeFont(existing)->flags = 0; + RemoveState(existing); + return existing; + } + auto handle = static_cast(library->iface->alloc(library->alloc_ctx, sizeof(FontHandleOpaque))); + if (!handle) return nullptr; + std::memset(handle, 0, sizeof(FontHandleOpaque)); + GetNativeFont(handle)->flags = HANDLE_OWNED; + return handle; +} + +void DiscardHandle(FontLibNative* library, FontHandle handle) { + auto* font = GetNativeFont(handle); + font->magic = 0; + font->open_info = {}; + font->library = library; + const std::uint16_t previousFlags = font->flags; + font->flags = 0; + RemoveState(handle); + if ((previousFlags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, handle); +} + +void InitializeOpenedHandle(FontHandleNative* font, FontLibNative* library, FontObj* obj, std::uint32_t modeLow, std::uint32_t packedId, std::uint32_t entryIndex, std::uint32_t subFontIndex) { + const SysDriver* driver = library->sys_driver; + font->magic = HANDLE_MAGIC; + font->flags = static_cast(font->flags | modeLow); + font->open_info = {}; + font->open_info.unique_id_packed = packedId; + font->open_info.ctx_entry_index = entryIndex; + font->open_info.sub_font_index = subFontIndex; + font->library = library; + font->renderer = nullptr; + std::memset(font->reserved_0x38, 0, sizeof(font->reserved_0x38)); + std::memset(&font->cached_style, 0, sizeof(font->cached_style)); + std::memset(font->reserved_0xcc, 0, sizeof(font->reserved_0xcc)); + std::memset(font->reserved_0xe8, 0, sizeof(font->reserved_0xe8)); + font->prevFont = nullptr; + font->nextFont = nullptr; + std::uint16_t metric[2] = {}; + driver->metric(obj, 0x0E00, metric); + font->metricA = metric[0]; + driver->metric(obj, 0xEA00, metric); + font->metricB = metric[0]; + float scale = 0.0f; + driver->scale(obj, metric, &scale); + font->style = {}; + font->style.dpi_x = 0x48; + font->style.dpi_y = 0x48; + font->style.scale_w = scale; + font->style.scale_h = scale; +} + +void SetOpenFlags(FontLibNative* library, std::uint32_t modeLow) { + const std::uint32_t flags = library->flags; + const std::uint32_t firstBit = modeLow == 2 ? 0x100000u : 0x200000u; + const std::uint32_t secondBit = modeLow == 2 ? 0x10000u : 0x20000u; + if ((flags & firstBit) != 0) library->sysfont_flags |= 1; + if ((flags & secondBit) != 0) library->sysfont_flags |= 2; +} + +std::uint32_t ClaimExternalEntry(FontCtxHeader* header, const OpenRequest& request) { + AcquireWordLock(header->lock_word); + auto* entries = static_cast(header->base); + std::uint32_t entryIndex = 0xFFFFFFFFu; + if (header->max_entries != 0 && entries) { + const auto uniqueId = static_cast(request.uniqueId); + const auto address = request.matchAddress ? reinterpret_cast(request.source) : 0; + std::uint32_t firstFree = 0xFFFFFFFFu; + for (std::uint32_t i = 0; i < header->max_entries; ++i) { + const auto& entry = entries[i]; + if ((entry.active != 0 && entry.unique_id == uniqueId) || (request.matchAddress && entry.font_address == address)) { + entryIndex = i; + break; + } + if (firstFree == 0xFFFFFFFFu && entry.active == 0) firstFree = i; + } + if (entryIndex == 0xFFFFFFFFu && firstFree != 0xFFFFFFFFu) { + entryIndex = firstFree; + auto& entry = entries[entryIndex]; + entry = {}; + entry.active = 1; + entry.font_address = address; + entry.unique_id = uniqueId != 0xFFFFFFFFu ? uniqueId : (entryIndex ^ 0x80000000u); + } + } + std::atomic_ref(header->lock_word).store(0, std::memory_order_release); + return entryIndex; +} + +int OpenExternalFont(const OpenRequest& request, std::uint32_t libraryLock, FontHandle* pFontHandle) { + FontLibNative* library = request.library; + FontHandle handle = PrepareHandle(library, *pFontHandle); + if (!handle) { + ReleaseLibraryLock(library, libraryLock); + *pFontHandle = nullptr; + return SCE_FONT_ERROR_ALLOCATION_FAILED; + } + const auto fail = [&](int rc) { + DiscardHandle(library, handle); + ReleaseLibraryLock(library, libraryLock); + *pFontHandle = nullptr; + return rc; + }; + auto* header = static_cast(library->external_fonts_ctx); + const std::uint32_t entryIndex = ClaimExternalEntry(header, request); + if (entryIndex == 0xFFFFFFFFu) return fail(SCE_FONT_ERROR_FONT_OPEN_MAX); + const std::uint32_t packedId = request.uniqueId == -1 ? entryIndex + 0x40000000u : (static_cast(request.uniqueId) | 0x80000000u); + FontObj* fontObj = nullptr; + std::uint32_t lockWord = 0; + auto* entry = AcquireFontCtxEntry(header, entryIndex, request.modeLow, &fontObj, &lockWord); + if (!entry) return fail(SCE_FONT_ERROR_FONT_OPEN_MAX); + const SysDriver* driver = library->sys_driver; + if (!driver->open || !driver->metric || !driver->scale) { + ReleaseFontCtxEntryLock(entry, request.modeLow, lockWord); + return fail(SCE_FONT_ERROR_INVALID_LIBRARY); + } + int rc = SCE_FONT_OK; + bool needOpen = (lockWord & OPEN_BIT) == 0; + if (!needOpen) { + rc = SCE_FONT_ERROR_FONT_OPEN_MAX; + if ((lockWord & COUNT_MASK) != COUNT_MASK) { + if (FontObj* node = FindSubFont(fontObj, request.subFontIndex)) { + ++node->refcount; + fontObj = node; + rc = SCE_FONT_OK; + } else { + needOpen = true; + } + } + } + if (needOpen) { + SetOpenFlags(library, request.modeLow); + rc = driver->open(library, request.driverMode, request.source, request.sourceSize, request.subFontIndex, packedId, &fontObj); + library->sysfont_flags = 0; + if (rc == SCE_FONT_OK) { + *EntryObjectSlot(entry, request.modeLow) = fontObj; + lockWord |= OPEN_BIT; + } + } + if (rc == SCE_FONT_OK) { + InitializeOpenedHandle(GetNativeFont(handle), library, fontObj, request.modeLow, packedId, entryIndex, request.subFontIndex); + ++lockWord; + *pFontHandle = handle; + } + ReleaseFontCtxEntryLock(entry, request.modeLow, lockWord); + if (rc != SCE_FONT_OK) return fail(rc); + if ((GetNativeFont(handle)->flags & HANDLE_OWNED) != 0) LinkFontToLibrary(library, handle); + ReleaseLibraryLock(library, libraryLock); + return SCE_FONT_OK; +} + +int BeginOpen(FontLibrary library, FontHandle* pFontHandle, std::uint32_t& libraryLock) { + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC || !AcquireLibraryLock(lib, libraryLock)) { + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } + int rc = SCE_FONT_OK; + if (!lib->fontset_registry || !lib->sys_driver || !lib->iface || !lib->iface->alloc || !lib->iface->dealloc) { + rc = SCE_FONT_ERROR_INVALID_LIBRARY; + } else if (!lib->external_fonts_ctx) { + rc = SCE_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + if (rc != SCE_FONT_OK) { + ReleaseLibraryLock(lib, libraryLock); + if (pFontHandle) *pFontHandle = nullptr; + } + return rc; +} + +bool ReadFileBytes(const std::filesystem::path& path, std::vector& bytes) { + std::ifstream file(path, std::ios::binary); + if (!file) return false; + bytes.assign(std::istreambuf_iterator(file), std::istreambuf_iterator()); + return !file.bad(); +} + +void CopyStyleToCache(FontHandleNative* font) { + std::memcpy(&font->cached_style.state, &font->style, sizeof(StyleStateBlock)); +} + +} + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* fontAddress, std::uint32_t fontSize, const FontOpenDetail* openDetail, FontHandle* pFontHandle) { + std::uint32_t libraryLock = 0; + const int beginRc = BeginOpen(library, pFontHandle, libraryLock); + if (beginRc != SCE_FONT_OK) return beginRc; + auto* lib = static_cast(library); + const std::uint32_t subFontIndex = openDetail ? openDetail->subfont_index : 0u; + const std::int32_t uniqueId = openDetail ? openDetail->unique_id : -1; + if (!fontAddress || fontSize == 0 || uniqueId < -1 || !pFontHandle) { + ReleaseLibraryLock(lib, libraryLock); + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + const OpenRequest request{lib, 1, 1, fontAddress, fontSize, true, subFontIndex, uniqueId}; + const int rc = OpenExternalFont(request, libraryLock, pFontHandle); + if (rc != SCE_FONT_OK) return rc; + const auto* bytes = static_cast(fontAddress); + LoadStateFace(ResetState(*pFontHandle), std::vector(bytes, bytes + fontSize), subFontIndex); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontOpenFontFile(FontLibrary library, const char* path, std::uint32_t openMode, const FontOpenDetail* openDetail, FontHandle* pFontHandle) { + if (!library) { + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } + if (!pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER; + const std::filesystem::path hostPath = path ? ResolvePath_nid_no_patch(path) : std::filesystem::path{}; + const std::string hostPathString = hostPath.string(); + std::uint32_t libraryLock = 0; + const int beginRc = BeginOpen(library, pFontHandle, libraryLock); + if (beginRc != SCE_FONT_OK) return beginRc; + auto* lib = static_cast(library); + const std::uint32_t subFontIndex = openDetail ? openDetail->subfont_index : 0u; + const std::int32_t uniqueId = openDetail ? openDetail->unique_id : -1; + const std::uint32_t modeLow = openMode & 0x0Fu; + if (!path || uniqueId < -1 || modeLow < 1 || modeLow > 3 || hostPathString.empty()) { + ReleaseLibraryLock(lib, libraryLock); + *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + const OpenRequest request{lib, modeLow, modeLow + 4, hostPathString.c_str(), 0, false, subFontIndex, uniqueId}; + const int rc = OpenExternalFont(request, libraryLock, pFontHandle); + if (rc != SCE_FONT_OK) return rc; + std::vector bytes; + FontState& state = ResetState(*pFontHandle); + if (ReadFileBytes(hostPath, bytes)) LoadStateFace(state, std::move(bytes), subFontIndex); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontOpenFontInstance(FontHandle fontHandle, FontHandle setupFont, FontHandle* pFontHandle) { + if (!fontHandle) { + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (!setupFont && !pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* source = GetNativeFont(fontHandle); + std::uint32_t sourceLock = 0; + if (source->magic != HANDLE_MAGIC || !AcquireFontLock(source, sourceLock)) { + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + const auto fail = [&](int rc) { + ReleaseFontLock(source, sourceLock); + if (pFontHandle) *pFontHandle = nullptr; + return rc; + }; + auto* library = static_cast(source->library); + if (!library || library->magic != LIBRARY_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + const std::uint32_t modeLow = source->flags & 0x0Fu; + const std::uint32_t subFontIndex = source->open_info.sub_font_index; + const std::uint32_t entryIndex = source->open_info.ctx_entry_index; + auto* header = static_cast(library->external_fonts_ctx); + if (!header) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + FontHandle target = setupFont; + bool owned = false; + if (!target) { + if (!library->iface || !library->iface->alloc || !library->iface->dealloc) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + target = static_cast(library->iface->alloc(library->alloc_ctx, sizeof(FontHandleOpaque))); + if (!target) return fail(SCE_FONT_ERROR_ALLOCATION_FAILED); + owned = true; + } else { + RemoveState(target); + } + std::memcpy(target, fontHandle, sizeof(FontHandleOpaque)); + auto* font = GetNativeFont(target); + font->lock_word = 0; + font->cached_style.cache_lock_word = 0; + font->flags = static_cast((font->flags & 0xFF0Fu) | (owned ? HANDLE_OWNED : 0u)); + auto* entries = static_cast(header->base); + if (!entries || header->max_entries < 1) { + font->magic = 0; + if (owned) library->iface->dealloc(library->alloc_ctx, target); + return fail(SCE_FONT_ERROR_INVALID_PARAMETER); + } + int rc = SCE_FONT_OK; + if (static_cast(entryIndex) >= 0) { + if (entryIndex >= header->max_entries || modeLow < 1 || modeLow > 3) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } else { + FontCtxEntry* entry = entries + entryIndex; + std::uint32_t* lockWord = EntryLockWord(entry, modeLow); + const std::uint32_t previous = AcquireWordLock(*lockWord); + FontObj* node = FindSubFont(static_cast(*EntryObjectSlot(entry, modeLow)), subFontIndex); + std::uint32_t updated = previous; + if ((previous & OPEN_BIT) == 0) { + rc = SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } else if ((previous & COUNT_MASK) == COUNT_MASK) { + rc = SCE_FONT_ERROR_FONT_OPEN_MAX; + } else if (!node) { + rc = SCE_FONT_ERROR_FATAL; + } else { + ++node->refcount; + ++updated; + } + std::atomic_ref(*lockWord).store(updated & ~LOCK_BIT, std::memory_order_release); + } + } + if (rc != SCE_FONT_OK) { + font->magic = 0; + font->open_info = {}; + font->library = library; + const std::uint16_t previousFlags = font->flags; + font->flags = 0; + if ((previousFlags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, target); + RemoveState(target); + return fail(rc); + } + if (owned) LinkFontToLibrary(library, target); + ReleaseFontLock(source, sourceLock); + FontState& targetState = ResetState(target); + if (FontState* sourceState = TryGetState(fontHandle)) { + targetState.scaleW = sourceState->scaleW; + targetState.scaleH = sourceState->scaleH; + if (!sourceState->faceData.empty()) LoadStateFace(targetState, sourceState->faceData, subFontIndex); + } + if (pFontHandle) *pFontHandle = target; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontOpenFontSet(FontLibrary library, std::uint32_t fontSetType, std::uint32_t openMode, const FontOpenDetail* openDetail, FontHandle* pFontHandle) { + (void)openDetail; + auto* lib = static_cast(library); + if (!lib || lib->magic != LIBRARY_MAGIC) { + if (pFontHandle) *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } + if (!pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER; + std::uint32_t libraryLock = 0; + if (!AcquireLibraryLock(lib, libraryLock)) { + *pFontHandle = nullptr; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } + int rc; + if (openMode != 1 && openMode != 2 && openMode != 3) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } else if (!lib->fontset_registry || !lib->sys_driver) { + rc = SCE_FONT_ERROR_INVALID_LIBRARY; + } else if (!lib->iface || !lib->iface->alloc || !lib->iface->dealloc) { + rc = SCE_FONT_ERROR_INVALID_MEMORY; + } else if (!lib->sysfonts_ctx) { + rc = SCE_FONT_ERROR_NO_SUPPORT_FUNCTION; + } else if (std::find(SYSTEM_FONT_SET_TYPES.begin(), SYSTEM_FONT_SET_TYPES.end(), fontSetType) == SYSTEM_FONT_SET_TYPES.end()) { + rc = SCE_FONT_ERROR_NO_SUPPORT_FONTSET; + } else { + rc = SCE_FONT_ERROR_FONT_OPEN_FAILED; + } + ReleaseLibraryLock(lib, libraryLock); + *pFontHandle = nullptr; + return rc; +} + +int APS5_VABI sceFontCloseFont(FontHandle fontHandle) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + auto* library = static_cast(font->library); + std::uint32_t libraryLock = 0; + if (!library || library->magic != LIBRARY_MAGIC || !AcquireLibraryLock(library, libraryLock)) { + ReleaseFontLock(font, fontLock); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + ReleaseFontObjectsForHandle(font); + const std::uint16_t flags = font->flags; + if ((flags & HANDLE_OWNED) != 0) UnlinkFontFromLibrary(library, fontHandle); + RemoveState(fontHandle); + font->magic = 0; + font->open_info = {}; + font->renderer = nullptr; + font->flags = 0; + font->lock_word = 0; + if ((flags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, fontHandle); + ReleaseLibraryLock(library, libraryLock); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontGetLibrary(FontHandle fontHandle, FontLibrary* pLibrary) { + if (!pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER; + const auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != HANDLE_MAGIC) { + *pLibrary = nullptr; + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + *pLibrary = font->library; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontBindRenderer(FontHandle fontHandle, FontRenderer renderer) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + std::uint32_t cachedLock = 0; + if (!AcquireCachedStyleLock(font, cachedLock)) { + ReleaseFontLock(font, fontLock); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + int rc = SCE_FONT_ERROR_ALREADY_BOUND_RENDERER; + if (!font->renderer) { + rc = SCE_FONT_ERROR_INVALID_RENDERER; + if (renderer && static_cast(renderer)->magic == RENDERER_MAGIC) { + CopyStyleToCache(font); + font->renderer = renderer; + rc = SCE_FONT_OK; + } + } + ReleaseCachedStyleLock(font, cachedLock); + ReleaseFontLock(font, fontLock); + return rc; +} + +int APS5_VABI sceFontRebindRenderer(FontHandle fontHandle) { + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != HANDLE_MAGIC) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + std::uint32_t fontLock = 0; + if (!AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + std::uint32_t cachedLock = 0; + if (!AcquireCachedStyleLock(font, cachedLock)) { + ReleaseFontLock(font, fontLock); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + int rc; + if (!font->renderer) { + rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER; + } else if (static_cast(font->renderer)->magic != RENDERER_MAGIC) { + rc = SCE_FONT_ERROR_FATAL; + } else { + CopyStyleToCache(font); + rc = SCE_FONT_OK; + } + ReleaseCachedStyleLock(font, cachedLock); + ReleaseFontLock(font, fontLock); + return rc; +} + +int APS5_VABI sceFontUnbindRenderer(FontHandle fontHandle) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t cachedLock = 0; + if (!font || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + int rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer) { + font->renderer = nullptr; + rc = SCE_FONT_OK; + } + ReleaseCachedStyleLock(font, cachedLock); + return rc; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/Render.cpp b/core/libs/prx/libSceFont/src/Render.cpp new file mode 100644 index 000000000..74cdde106 --- /dev/null +++ b/core/libs/prx/libSceFont/src/Render.cpp @@ -0,0 +1,449 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +constexpr std::uint16_t GENERATE_GLYPH_DETAIL_ID = 0x0FD3; + +std::uint16_t ClampToU16(float value) { + if (value <= 0.0f) return 0; + return static_cast(std::lround(std::min(value, static_cast(std::numeric_limits::max())))); +} + +std::uint8_t* LayoutCacheBytes(FontHandleNative* font) { + return reinterpret_cast(&font->cached_style) + offsetof(CachedStyle, layout_cache_bytes); +} + +RenderSurfaceSystemUse* SurfaceSystemUse(FontRenderSurface* surface) { + return reinterpret_cast(surface->reserved_q); +} + +bool SurfaceScaleFrame(FontRenderSurface* surface, StyleStateBlock& state, int& rc) { + if (!surface || (surface->styleFlag & 0x1) == 0) return false; + const FontStyleFrame* frame = SurfaceSystemUse(surface)->styleframe; + if (!frame || (frame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return false; + state = {}; + state.dpi_x = frame->hDpi; + state.dpi_y = frame->vDpi; + if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) state.slant_ratio = frame->slantRatio; + if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) { + state.effect_weight_x = frame->effectWeightX; + state.effect_weight_y = frame->effectWeightY; + } + StyleStateBlock frameScale{}; + frameScale.dpi_x = frame->hDpi; + frameScale.dpi_y = frame->vDpi; + frameScale.scale_unit = frame->scaleUnit; + frameScale.scale_w = frame->scalePixelW; + frameScale.scale_h = frame->scalePixelH; + rc = StyleStateGetScalePixel(&frameScale, &state.scale_w, &state.scale_h); + return true; +} + +int CachedBaseline(FontHandle handle, FontHandleNative* font, float& baseline) { + int rc = SCE_FONT_OK; + const std::uint8_t flags = CachedStyleCacheFlags(font->cached_style); + if ((flags & 0x1) == 0) { + rc = ComputeHorizontalLayout(handle, &font->cached_style.state, LayoutCacheBytes(font)); + if (rc == SCE_FONT_OK) CachedStyleSetCacheFlags(font->cached_style, static_cast(flags | 0x1)); + } + if (rc == SCE_FONT_OK) baseline = LoadFloat(LayoutCacheBytes(font), HORIZONTAL_BASELINE); + return rc; +} + +int CachedColumnOffset(FontHandle handle, FontHandleNative* font, float& offset) { + int rc = SCE_FONT_OK; + const std::uint8_t flags = CachedStyleCacheFlags(font->cached_style); + if ((flags & 0x2) == 0) { + std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {}; + rc = ComputeVerticalLayout(handle, &font->cached_style.state, layout); + if (rc == SCE_FONT_OK) { + offset = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X); + CachedStyleSetScalar(font->cached_style, offset); + CachedStyleSetCacheFlags(font->cached_style, static_cast(flags | 0x2)); + } + } + if (rc == SCE_FONT_OK && (CachedStyleCacheFlags(font->cached_style) & 0x2) != 0) offset = CachedStyleGetScalar(font->cached_style); + return rc; +} + +int RenderDirectional(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result, std::uint16_t direction) { + auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != HANDLE_MAGIC) { + ClearRenderOutputs(metrics, result); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (code == 0) { + ClearRenderOutputs(metrics, result); + return SCE_FONT_ERROR_NO_SUPPORT_CODE; + } + if (!surface || !metrics || !result) { + ClearRenderOutputs(metrics, result); + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + std::uint32_t fontLock = 0; + if (!AcquireFontLock(font, fontLock)) { + ClearRenderOutputs(metrics, result); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + CachedStyleSetDirectionWord(font->cached_style, direction); + const int rc = RenderCharGlyphImageCore(fontHandle, code, surface, x, y, metrics, result); + ReleaseFontLock(font, fontLock); + if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result); + return rc; +} + +} + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle fontHandle, std::uint32_t code, FontGlyphMetrics* metrics) { + return GetCharGlyphMetrics(fontHandle, code, metrics, false); +} + +int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle fontHandle, std::uint32_t code, FontGlyphMetrics* metrics) { + return GetCharGlyphMetrics(fontHandle, code, metrics, true); +} + +int APS5_VABI sceFontGetHorizontalLayout(FontHandle fontHandle, FontHorizontalLayout* layout) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) { + if (layout) *layout = {}; + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + if (!layout) { + ReleaseFontLock(font, fontLock); + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + std::uint8_t blocks[HORIZONTAL_LAYOUT_SIZE] = {}; + const int rc = ComputeHorizontalLayout(fontHandle, &font->style, blocks); + ReleaseFontLock(font, fontLock); + if (rc != SCE_FONT_OK) { + *layout = {}; + return rc; + } + layout->baselineOffset = LoadFloat(blocks, HORIZONTAL_BASELINE); + layout->lineAdvance = LoadFloat(blocks, HORIZONTAL_LINE_ADVANCE); + layout->decorationExtent = LoadFloat(blocks, HORIZONTAL_EFFECT_HEIGHT); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontGetVerticalLayout(FontHandle fontHandle, FontVerticalLayout* layout) { + int rc = SCE_FONT_ERROR_INVALID_FONT_HANDLE; + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (font && font->magic == HANDLE_MAGIC && AcquireFontLock(font, fontLock)) { + if (!layout) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } else { + std::uint8_t blocks[VERTICAL_LAYOUT_SIZE] = {}; + rc = ComputeVerticalLayout(fontHandle, &font->style, blocks); + if (rc == SCE_FONT_OK) { + layout->baselineOffsetX = LoadFloat(blocks, VERTICAL_BASELINE_OFFSET_X); + layout->columnAdvance = LoadFloat(blocks, VERTICAL_COLUMN_ADVANCE); + layout->decorationSpan = LoadFloat(blocks, VERTICAL_DECORATION_SPAN); + } + } + ReleaseFontLock(font, fontLock); + if (rc == SCE_FONT_OK) return rc; + } + if (layout) *layout = {}; + return rc; +} + +int APS5_VABI sceFontGetKerning(FontHandle fontHandle, std::uint32_t preCode, std::uint32_t code, FontKerning* kerning) { + if (!kerning) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + *kerning = {}; + const FontState* state = TryGetState(fontHandle); + const FT_Face face = state ? state->face : nullptr; + const FT_UInt previousGlyph = face ? FT_Get_Char_Index(face, preCode) : 0; + const FT_UInt glyph = face ? FT_Get_Char_Index(face, code) : 0; + if (face && previousGlyph != 0 && glyph != 0) { + const auto charW = static_cast(static_cast(state->scaleW * 64.0f)); + const auto charH = static_cast(static_cast(state->scaleH * 64.0f)); + FT_Set_Char_Size(face, charW, charH, 72, 72); + FT_Vector delta{}; + FT_Get_Kerning(face, previousGlyph, glyph, FT_KERNING_DEFAULT, &delta); + kerning->offsetX = static_cast(delta.x) / 64.0f; + } + ReleaseFontLock(font, fontLock); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontRenderCharGlyphImage(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) { + ClearRenderOutputs(metrics, result); + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } + int preRc = SCE_FONT_OK; + StyleStateBlock frameState{}; + const bool horizontal = static_cast(font->flags) >= 0; + float xUsed = x; + float yUsed = y; + if (horizontal) { + float baseline = 0.0f; + if (!SurfaceScaleFrame(surface, frameState, preRc)) { + preRc = CachedBaseline(fontHandle, font, baseline); + } else { + std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {}; + if (preRc == SCE_FONT_OK) preRc = ComputeHorizontalLayout(fontHandle, &frameState, layout); + if (preRc == SCE_FONT_OK) { + baseline = LoadFloat(layout, HORIZONTAL_BASELINE); + SurfaceSystemUse(surface)->catchedScale = baseline; + } + } + yUsed = y + baseline; + CachedStyleSetDirectionWord(font->cached_style, 1); + } else { + float offset = 0.0f; + if (!SurfaceScaleFrame(surface, frameState, preRc)) { + preRc = CachedColumnOffset(fontHandle, font, offset); + } else { + std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {}; + if (preRc == SCE_FONT_OK) preRc = ComputeVerticalLayout(fontHandle, &frameState, layout); + if (preRc == SCE_FONT_OK) { + offset = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X); + SurfaceSystemUse(surface)->catchedScale = offset; + } else { + preRc = CachedColumnOffset(fontHandle, font, offset); + } + } + xUsed = x + offset; + CachedStyleSetDirectionWord(font->cached_style, 2); + } + int rc; + if (code == 0) { + rc = SCE_FONT_ERROR_NO_SUPPORT_CODE; + } else if (!surface || !metrics || !result) { + rc = SCE_FONT_ERROR_INVALID_PARAMETER; + } else if (preRc != SCE_FONT_OK) { + rc = preRc; + } else { + rc = RenderCharGlyphImageCore(fontHandle, code, surface, xUsed, yUsed, metrics, result); + } + ReleaseFontLock(font, fontLock); + if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result); + return rc; +} + +int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + return RenderDirectional(fontHandle, code, surface, x, y, metrics, result, 1); +} + +int APS5_VABI sceFontRenderCharGlyphImageVertical(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + return RenderDirectional(fontHandle, code, surface, x, y, metrics, result, 2); +} + +void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* renderSurface, void* buffer, int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel) { + if (!renderSurface) return; + const auto width = static_cast(std::max(widthPixel, 0)); + const auto height = static_cast(std::max(heightPixel, 0)); + renderSurface->buffer = buffer; + renderSurface->widthByte = bufWidthByte; + renderSurface->pixelSizeByte = static_cast(pixelSizeByte); + renderSurface->pad0 = 0; + renderSurface->styleFlag = 0; + renderSurface->pad2 = 0; + renderSurface->width = static_cast(width); + renderSurface->height = static_cast(height); + renderSurface->sc_x0 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_x1 = width; + renderSurface->sc_y1 = height; +} + +void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* renderSurface, int x0, int y0, int w, int h) { + if (!renderSurface) return; + const auto surfaceW = static_cast(renderSurface->width); + if (surfaceW != 0) { + std::uint32_t x1; + std::uint32_t left; + auto width = static_cast(w); + if (x0 < 0) { + x1 = width + static_cast(x0); + if (surfaceW < x1) x1 = surfaceW; + if (width <= static_cast(-x0)) x1 = 0; + left = 0; + } else { + x1 = surfaceW; + left = surfaceW; + if (static_cast(x0) <= surfaceW) { + if (surfaceW < width) width = surfaceW; + x1 = width + static_cast(x0); + left = static_cast(x0); + if (surfaceW < x1) x1 = surfaceW; + } + } + renderSurface->sc_x0 = left; + renderSurface->sc_x1 = x1; + } + const auto surfaceH = static_cast(renderSurface->height); + if (surfaceH == 0) return; + std::uint32_t top; + std::uint32_t y1 = surfaceH; + auto height = static_cast(h); + if (y0 < 0) { + top = 0; + if (height <= static_cast(-y0)) { + renderSurface->sc_y0 = 0; + renderSurface->sc_y1 = 0; + return; + } + } else { + if (surfaceH < static_cast(y0)) { + renderSurface->sc_y0 = surfaceH; + renderSurface->sc_y1 = y1; + return; + } + top = static_cast(y0); + if (surfaceH < height) height = surfaceH; + } + const std::uint32_t candidate = height + static_cast(y0); + if (candidate <= surfaceH) y1 = candidate; + renderSurface->sc_y0 = top; + renderSurface->sc_y1 = y1; +} + +int APS5_VABI sceFontRenderSurfaceSetStyleFrame(FontRenderSurface* renderSurface, FontStyleFrame* styleFrame) { + if (!renderSurface) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!styleFrame) { + renderSurface->styleFlag &= static_cast(~0x1u); + renderSurface->reserved_q[0] = 0; + renderSurface->reserved_q[1] = 0; + return SCE_FONT_OK; + } + if (styleFrame->magic != STYLE_FRAME_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER; + renderSurface->styleFlag |= 0x1; + renderSurface->reserved_q[0] = reinterpret_cast(styleFrame); + renderSurface->reserved_q[1] = 0; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontGenerateCharGlyph(FontHandle fontHandle, std::uint32_t code, const FontGenerateGlyphDetail* detail, FontGlyph* pGlyph) { + if (!pGlyph) return SCE_FONT_ERROR_INVALID_PARAMETER; + *pGlyph = nullptr; + if (!fontHandle) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + if (code == 0) return SCE_FONT_ERROR_NO_SUPPORT_CODE; + const FontState* state = TryGetState(fontHandle); + if (!state) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + const std::uint8_t glyphForm = detail ? detail->glyph_form : 0; + const std::uint8_t metricsForm = detail ? detail->metrics_form : 0; + const std::uint16_t formOptions = detail ? detail->form_options : 0; + const FontMemory* glyphMemory = detail ? detail->mem : nullptr; + if (detail && detail->id != GENERATE_GLYPH_DETAIL_ID) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((formOptions & static_cast(~0x11u)) != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (glyphForm == 0 && metricsForm != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((glyphForm != 0 && metricsForm == 0) || glyphForm > 1 || metricsForm > 4) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (glyphMemory && glyphMemory->mem_kind != MEMORY_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER; + FontGlyphMetrics metrics{}; + const int rc = GetCharGlyphMetrics(fontHandle, code, &metrics, false); + if (rc != SCE_FONT_OK) return rc; + auto* generated = new (std::nothrow) GeneratedGlyph(); + if (!generated) return SCE_FONT_ERROR_ALLOCATION_FAILED; + generated->codepoint = code; + generated->metrics = metrics; + generated->glyph.magic = GLYPH_MAGIC; + generated->glyph.flags = formOptions; + generated->glyph.glyph_form = glyphForm; + generated->glyph.metrics_form = metricsForm; + generated->glyph.em_size = ClampToU16(state->scaleH); + generated->glyph.baseline = ClampToU16(metrics.Horizontal.bearingY); + generated->glyph.height_px = ClampToU16(metrics.height); + generated->glyph.origin_x = 0; + generated->glyph.origin_y = generated->glyph.baseline; + generated->glyph.scale_x = state->scaleW; + generated->glyph.base_scale = state->scaleH; + generated->glyph.memory = glyphMemory; + generated->owner = fontHandle; + generated->outline.outline_flags = generated->glyph.flags; + TrackGeneratedGlyph(&generated->glyph); + *pGlyph = &generated->glyph; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, FontGlyph* pGlyph) { + (void)memory; + if (!pGlyph) return SCE_FONT_ERROR_INVALID_PARAMETER; + const FontGlyph glyph = *pGlyph; + if (!glyph || glyph->magic != GLYPH_MAGIC || !ForgetGeneratedGlyph(glyph)) return SCE_FONT_ERROR_INVALID_GLYPH; + delete reinterpret_cast(glyph); + *pGlyph = nullptr; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontGlyphGetGlyphForm(FontGlyph glyph) { + if (!glyph || glyph->magic != GLYPH_MAGIC) return SCE_FONT_ERROR_INVALID_GLYPH; + return glyph->glyph_form; +} + +int APS5_VABI sceFontGlyphGetMetricsForm(FontGlyph glyph) { + if (!glyph || glyph->magic != GLYPH_MAGIC) return SCE_FONT_ERROR_INVALID_GLYPH; + return glyph->metrics_form; +} + +int APS5_VABI sceFontGlyphGetScalePixel(FontGlyph glyph, float* w, float* h) { + if (!glyph || glyph->magic != GLYPH_MAGIC || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (w) *w = glyph->scale_x; + if (h) *h = glyph->base_scale; + return SCE_FONT_OK; +} + +const FontGlyphMetrics* APS5_VABI sceFontGlyphRefersMetrics(FontGlyph glyph) { + auto* generated = TryGetGeneratedGlyph(glyph); + return generated ? &generated->metrics : nullptr; +} + +const FontGlyphMetricsHorizontal* APS5_VABI sceFontGlyphRefersMetricsHorizontal(FontGlyph glyph) { + auto* generated = TryGetGeneratedGlyph(glyph); + if (!generated) return nullptr; + PopulateGlyphMetricVariants(*generated); + return &generated->metricsHorizontal; +} + +const FontGlyphMetricsHorizontalAdvance* APS5_VABI sceFontGlyphRefersMetricsHorizontalAdvance(FontGlyph glyph) { + auto* generated = TryGetGeneratedGlyph(glyph); + if (!generated) return nullptr; + PopulateGlyphMetricVariants(*generated); + return &generated->metricsHorizontalAdvance; +} + +const FontGlyphMetricsHorizontalX* APS5_VABI sceFontGlyphRefersMetricsHorizontalX(FontGlyph glyph) { + auto* generated = TryGetGeneratedGlyph(glyph); + if (!generated) return nullptr; + PopulateGlyphMetricVariants(*generated); + return &generated->metricsHorizontalX; +} + +FontGlyphOutline* APS5_VABI sceFontGlyphRefersOutline(FontGlyph glyph) { + if (!glyph || glyph->magic != GLYPH_MAGIC || glyph->glyph_form != 1) return nullptr; + auto* generated = TryGetGeneratedGlyph(glyph); + if (!generated) return nullptr; + if (!generated->outlineInitialized && !BuildTrueOutline(*generated)) BuildBoundingOutline(*generated); + return &generated->outline; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/State.cpp b/core/libs/prx/libSceFont/src/State.cpp new file mode 100644 index 000000000..58b7e0e49 --- /dev/null +++ b/core/libs/prx/libSceFont/src/State.cpp @@ -0,0 +1,433 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +std::mutex stateMutex; +std::unordered_map> fontStates; +std::mutex glyphMutex; +std::unordered_set generatedGlyphs; + +FT_Library HostFreeTypeLibrary() { + static const FT_Library library = [] { + FT_Library created = nullptr; + if (FT_Init_FreeType(&created) != 0) return static_cast(nullptr); + return created; + }(); + return library; +} + +bool AcquireMagicLock(std::uint32_t& word, const std::uint16_t& magic, std::uint16_t expectedMagic, std::uint32_t& previous) { + for (;;) { + const std::uint32_t current = std::atomic_ref(word).load(std::memory_order_acquire); + if (magic != expectedMagic) return false; + if (static_cast(current) >= 0) { + std::uint32_t expected = current; + if (std::atomic_ref(word).compare_exchange_weak(expected, current | Font::LOCK_BIT, std::memory_order_acq_rel)) { + previous = current; + return true; + } + } + Font::Backoff(); + } +} + +void ReleaseWord(std::uint32_t& word, std::uint32_t previous) { + std::atomic_ref(word).store(previous & ~Font::LOCK_BIT, std::memory_order_release); +} + +} + +Font::FontState::~FontState() { + if (face) FT_Done_Face(face); +} + +void Font::Backoff() { + std::this_thread::sleep_for(std::chrono::microseconds(30)); +} + +std::uint32_t Font::AcquireWordLock(std::uint32_t& word) { + for (;;) { + const std::uint32_t current = std::atomic_ref(word).load(std::memory_order_acquire); + if (static_cast(current) >= 0) { + std::uint32_t expected = current; + if (std::atomic_ref(word).compare_exchange_weak(expected, current | LOCK_BIT, std::memory_order_acq_rel)) return current; + } + Backoff(); + } +} + +Font::FontHandleNative* Font::GetNativeFont(FontHandle handle) { + return reinterpret_cast(handle); +} + +bool Font::AcquireLibraryLock(FontLibNative* library, std::uint32_t& previous) { + return library && AcquireMagicLock(library->lock_word, library->magic, LIBRARY_MAGIC, previous); +} + +void Font::ReleaseLibraryLock(FontLibNative* library, std::uint32_t previous) { + if (library) ReleaseWord(library->lock_word, previous); +} + +bool Font::AcquireFontLock(FontHandleNative* font, std::uint32_t& previous) { + return font && AcquireMagicLock(font->lock_word, font->magic, HANDLE_MAGIC, previous); +} + +void Font::ReleaseFontLock(FontHandleNative* font, std::uint32_t previous) { + if (font) ReleaseWord(font->lock_word, previous); +} + +bool Font::AcquireCachedStyleLock(FontHandleNative* font, std::uint32_t& previous) { + return font && AcquireMagicLock(font->cached_style.cache_lock_word, font->magic, HANDLE_MAGIC, previous); +} + +void Font::ReleaseCachedStyleLock(FontHandleNative* font, std::uint32_t previous) { + if (font) ReleaseWord(font->cached_style.cache_lock_word, previous); +} + +namespace { + +FontHandle* AcquireFontList(Font::FontLibNative* library) { + auto* const busy = reinterpret_cast(std::numeric_limits::max()); + std::atomic_ref slot(library->list_head_ptr); + for (;;) { + FontHandle* current = slot.load(std::memory_order_acquire); + if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current; + Font::Backoff(); + } +} + +void ReleaseFontList(Font::FontLibNative* library, FontHandle* head) { + std::atomic_ref(library->list_head_ptr).store(head, std::memory_order_release); +} + +} + +void Font::LinkFontToLibrary(FontLibNative* library, FontHandle handle) { + FontHandle* head = AcquireFontList(library); + if (head) { + const FontHandle oldHead = *head; + auto* font = GetNativeFont(handle); + font->prevFont = nullptr; + font->nextFont = oldHead; + if (oldHead) GetNativeFont(oldHead)->prevFont = handle; + *head = handle; + } + ReleaseFontList(library, head); +} + +void Font::UnlinkFontFromLibrary(FontLibNative* library, FontHandle handle) { + FontHandle* head = AcquireFontList(library); + auto* font = GetNativeFont(handle); + if (head) { + if (font->prevFont) { + GetNativeFont(font->prevFont)->nextFont = font->nextFont; + } else if (*head == handle) { + *head = font->nextFont; + } + if (font->nextFont) GetNativeFont(font->nextFont)->prevFont = font->prevFont; + } + font->prevFont = nullptr; + font->nextFont = nullptr; + ReleaseFontList(library, head); +} + +std::uint32_t* Font::EntryLockWord(FontCtxEntry* entry, std::uint32_t modeLow) { + if (modeLow == 3) return &entry->lock_mode3; + if (modeLow == 1) return &entry->lock_mode1; + return &entry->lock_mode2; +} + +void** Font::EntryObjectSlot(FontCtxEntry* entry, std::uint32_t modeLow) { + if (modeLow == 3) return &entry->obj_mode3; + if (modeLow == 1) return &entry->obj_mode1; + return &entry->obj_mode2; +} + +Font::FontCtxEntry* Font::AcquireFontCtxEntry(void* ctx, std::uint32_t index, std::uint32_t modeLow, FontObj** outObject, std::uint32_t* outLockWord) { + if (!ctx || !outObject || !outLockWord) return nullptr; + auto* header = static_cast(ctx); + if (index >= header->max_entries || !header->base) { + *outLockWord = 0; + *outObject = nullptr; + return nullptr; + } + auto* entry = static_cast(header->base) + index; + if (modeLow != 1 && modeLow != 2 && modeLow != 3) { + *outLockWord = 0; + *outObject = nullptr; + return entry; + } + *outLockWord = AcquireWordLock(*EntryLockWord(entry, modeLow)) | LOCK_BIT; + *outObject = static_cast(*EntryObjectSlot(entry, modeLow)); + return entry; +} + +void Font::ReleaseFontCtxEntryLock(FontCtxEntry* entry, std::uint32_t modeLow, std::uint32_t lockWord) { + if (!entry) return; + if ((lockWord & COUNT_MASK) == 0) *EntryObjectSlot(entry, modeLow) = nullptr; + if ((lockWord & COUNT_MASK) == 0 && !entry->obj_mode1 && !entry->obj_mode3 && !entry->obj_mode2) { + entry->unique_id = 0; + entry->active = 0; + } + ReleaseWord(*EntryLockWord(entry, modeLow), lockWord); +} + +Font::FontObj* Font::FindSubFont(FontObj* head, std::uint32_t subFontIndex) { + for (auto* node = head; node; node = node->next) { + if (node->sub_font_index == subFontIndex) return node; + } + return nullptr; +} + +void Font::ReleaseFontObjectsForHandle(FontHandleNative* font) { + auto* library = static_cast(font->library); + if (!library || library->magic != LIBRARY_MAGIC || !library->sys_driver || !library->sys_driver->close) return; + const std::uint32_t fontId = font->open_info.ctx_entry_index; + if (static_cast(fontId) < 0) return; + const std::uint32_t modeLow = font->flags & 0x0Fu; + FontObj* head = nullptr; + std::uint32_t lockWord = 0; + auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord); + if (!entry) return; + FontObj* match = FindSubFont(head, font->open_info.sub_font_index); + const std::uint32_t openFlag = lockWord & OPEN_BIT; + const std::uint32_t countBits = lockWord & COUNT_MASK; + if (openFlag != 0 && countBits != 0 && match) { + const std::uint32_t refcount = match->refcount; + const std::uint32_t decremented = lockWord - 1; + lockWord = decremented; + if (refcount == 1) { + FontObj* next = match->next; + FontObj* prev = match->prev; + library->sys_driver->close(match, 0); + if (!prev) *EntryObjectSlot(entry, modeLow) = next; + } else if (refcount != 0) { + match->refcount = refcount - 1; + } + if (countBits == 1) lockWord = ~openFlag & decremented; + } + ReleaseFontCtxEntryLock(entry, modeLow, lockWord); +} + +Font::FontState* Font::TryGetState(FontHandle handle) { + if (!handle) return nullptr; + std::lock_guard lock(stateMutex); + const auto it = fontStates.find(handle); + return it == fontStates.end() ? nullptr : it->second.get(); +} + +Font::FontState& Font::ResetState(FontHandle handle) { + std::lock_guard lock(stateMutex); + auto& slot = fontStates[handle]; + slot = std::make_unique(); + return *slot; +} + +void Font::RemoveState(FontHandle handle) { + if (!handle) return; + std::lock_guard lock(stateMutex); + fontStates.erase(handle); +} + +void Font::LoadStateFace(FontState& state, std::vector bytes, std::uint32_t subFontIndex) { + if (state.face) { + FT_Done_Face(state.face); + state.face = nullptr; + } + state.faceData = std::move(bytes); + const FT_Library library = HostFreeTypeLibrary(); + if (!library || state.faceData.empty()) return; + FT_Face face = nullptr; + if (FT_New_Memory_Face(library, state.faceData.data(), static_cast(state.faceData.size()), static_cast(subFontIndex), &face) != 0) return; + FT_Select_Charmap(face, FT_ENCODING_UNICODE); + state.face = face; +} + +void Font::TrackGeneratedGlyph(FontGlyph glyph) { + std::lock_guard lock(glyphMutex); + generatedGlyphs.insert(glyph); +} + +bool Font::ForgetGeneratedGlyph(FontGlyph glyph) { + std::lock_guard lock(glyphMutex); + return generatedGlyphs.erase(glyph) > 0; +} + +Font::GeneratedGlyph* Font::TryGetGeneratedGlyph(FontGlyph glyph) { + if (!glyph || glyph->magic != GLYPH_MAGIC) return nullptr; + std::lock_guard lock(glyphMutex); + if (!generatedGlyphs.contains(glyph)) return nullptr; + return reinterpret_cast(glyph); +} + +void Font::PopulateGlyphMetricVariants(GeneratedGlyph& glyph) { + if (glyph.metricsInitialized) return; + glyph.metricsHorizontal.width = glyph.metrics.width; + glyph.metricsHorizontal.height = glyph.metrics.height; + glyph.metricsHorizontal.horizontal.bearing_x = glyph.metrics.Horizontal.bearingX; + glyph.metricsHorizontal.horizontal.bearing_y = glyph.metrics.Horizontal.bearingY; + glyph.metricsHorizontal.horizontal.advance = glyph.metrics.Horizontal.advance; + glyph.metricsHorizontalX.width = glyph.metrics.width; + glyph.metricsHorizontalX.horizontal.bearing_x = glyph.metrics.Horizontal.bearingX; + glyph.metricsHorizontalX.horizontal.advance = glyph.metrics.Horizontal.advance; + glyph.metricsHorizontalAdvance.horizontal.advance = glyph.metrics.Horizontal.advance; + glyph.metricsInitialized = true; +} + +void Font::BuildBoundingOutline(GeneratedGlyph& glyph) { + const float left = glyph.metrics.Horizontal.bearingX; + const float top = glyph.metrics.Horizontal.bearingY; + const float right = left + glyph.metrics.width; + const float bottom = top - glyph.metrics.height; + glyph.outlinePoints = {{left, top}, {right, top}, {right, bottom}, {left, bottom}}; + glyph.outlineTags.assign(glyph.outlinePoints.size(), 1); + glyph.outlineContours = {static_cast(glyph.outlinePoints.size() - 1)}; + glyph.outline.points_ptr = glyph.outlinePoints.data(); + glyph.outline.tags_ptr = glyph.outlineTags.data(); + glyph.outline.contour_end_idx = glyph.outlineContours.data(); + glyph.outline.points_cnt = static_cast(glyph.outlinePoints.size()); + glyph.outline.contours_cnt = static_cast(glyph.outlineContours.size()); + glyph.outlineInitialized = true; +} + +bool Font::ValidStyleFrame(const FontStyleFrame* frame) { + return frame && frame->magic == STYLE_FRAME_MAGIC; +} + +int Font::StyleStateSetScalePixel(StyleStateBlock* style, float w, float h) { + if (!style) return 0; + if (style->scale_w == w && style->scale_h == h && style->scale_unit == 0) return 0; + style->scale_unit = 0; + style->scale_w = w; + style->scale_h = h; + return 1; +} + +int Font::StyleStateSetScalePoint(StyleStateBlock* style, float w, float h) { + if (!style) return 0; + if (style->scale_w == w && style->scale_h == h && style->scale_unit == 1) return 0; + style->scale_unit = 1; + style->scale_w = w; + style->scale_h = h; + return 1; +} + +int Font::StyleStateGetScalePixel(const StyleStateBlock* style, float* w, float* h) { + if (!style || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER; + const bool points = style->scale_unit != 0; + if (w) { + float value = style->scale_w; + if (points && style->dpi_x != 0) value *= static_cast(style->dpi_x) / POINTS_PER_INCH; + *w = value; + } + if (h) { + float value = style->scale_h; + if (points && style->dpi_y != 0) value *= static_cast(style->dpi_y) / POINTS_PER_INCH; + *h = value; + } + return SCE_FONT_OK; +} + +int Font::StyleStateGetScalePoint(const StyleStateBlock* style, float* w, float* h) { + if (!style || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER; + const bool pixels = style->scale_unit == 0; + if (w) { + float value = style->scale_w; + if (pixels && style->dpi_x != 0) value *= POINTS_PER_INCH / static_cast(style->dpi_x); + *w = value; + } + if (h) { + float value = style->scale_h; + if (pixels && style->dpi_y != 0) value *= POINTS_PER_INCH / static_cast(style->dpi_y); + *h = value; + } + return SCE_FONT_OK; +} + +int Font::StyleStateSetDpi(StyleStateBlock* style, std::uint32_t hDpi, std::uint32_t vDpi) { + if (!style) return 0; + if (hDpi == 0) hDpi = 0x48; + if (vDpi == 0) vDpi = 0x48; + if (style->dpi_x == hDpi && style->dpi_y == vDpi) return 0; + style->dpi_x = hDpi; + style->dpi_y = vDpi; + return 1; +} + +int Font::StyleStateSetSlantRatio(StyleStateBlock* style, float slantRatio) { + if (!style || style->slant_ratio == slantRatio) return 0; + style->slant_ratio = std::clamp(slantRatio, -1.0f, 1.0f); + return 1; +} + +int Font::StyleStateGetSlantRatio(const StyleStateBlock* style, float* slantRatio) { + if (!style || !slantRatio) return SCE_FONT_ERROR_INVALID_PARAMETER; + *slantRatio = style->slant_ratio; + return SCE_FONT_OK; +} + +int Font::StyleStateSetWeightScale(StyleStateBlock* style, float weightXScale, float weightYScale) { + if (!style) return 0; + const float deltaX = std::clamp(weightXScale - 1.0f, -0.04f, 0.04f); + const float deltaY = std::clamp(weightYScale - 1.0f, -0.04f, 0.04f); + bool changed = false; + if (style->effect_weight_x != deltaX) { + style->effect_weight_x = deltaX; + changed = true; + } + if (style->effect_weight_y != deltaY) { + style->effect_weight_y = deltaY; + changed = true; + } + return changed ? 1 : 0; +} + +int Font::StyleStateGetWeightScale(const StyleStateBlock* style, float* weightXScale, float* weightYScale, std::uint32_t* mode) { + if (!weightXScale && !weightYScale && !mode) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!style) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (weightXScale) *weightXScale = style->effect_weight_x + 1.0f; + if (weightYScale) *weightYScale = style->effect_weight_y + 1.0f; + if (mode) *mode = 0; + return SCE_FONT_OK; +} + +std::uint8_t Font::CachedStyleCacheFlags(const CachedStyle& style) { + return static_cast(style.cache_flags_and_direction & 0xFFu); +} + +void Font::CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags) { + style.cache_flags_and_direction = (style.cache_flags_and_direction & 0xFFFFFF00u) | flags; +} + +void Font::CachedStyleSetDirectionWord(CachedStyle& style, std::uint16_t word) { + style.cache_flags_and_direction = (style.cache_flags_and_direction & 0x0000FFFFu) | (static_cast(word) << 16); +} + +float Font::CachedStyleGetScalar(const CachedStyle& style) { + float value; + std::memcpy(&value, &style.cached_scalar_bits, sizeof(value)); + return value; +} + +void Font::CachedStyleSetScalar(CachedStyle& style, float value) { + std::memcpy(&style.cached_scalar_bits, &value, sizeof(value)); +} + +void Font::ClearRenderOutputs(FontGlyphMetrics* metrics, FontRenderOutput* result) { + if (metrics) *metrics = {}; + if (result) *result = {}; +} diff --git a/core/libs/prx/libSceFont/src/Style.cpp b/core/libs/prx/libSceFont/src/Style.cpp new file mode 100644 index 000000000..42c4d9194 --- /dev/null +++ b/core/libs/prx/libSceFont/src/Style.cpp @@ -0,0 +1,303 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +template +int UpdateFontStyle(FontHandle fontHandle, Setter&& setter) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + if (setter(font) != 0) font->cached_style.layout_cache_state = 0; + ReleaseFontLock(font, fontLock); + return SCE_FONT_OK; +} + +template +int UpdateRenderStyle(FontHandle fontHandle, Setter&& setter) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t cachedLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + int rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER; + if (font->renderer) rc = setter(font); + ReleaseCachedStyleLock(font, cachedLock); + return rc; +} + +template +int ReadRenderStyle(FontHandle fontHandle, bool checkMagic, Getter&& getter) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t cachedLock = 0; + if (!font || (checkMagic && font->magic != HANDLE_MAGIC) || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + const int rc = font->renderer ? getter(font) : SCE_FONT_ERROR_NOT_BOUND_RENDERER; + ReleaseCachedStyleLock(font, cachedLock); + return rc; +} + +template +int ReadFontStyle(FontHandle fontHandle, bool checkMagic, Getter&& getter) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || (checkMagic && font->magic != HANDLE_MAGIC) || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + const int rc = getter(font); + ReleaseFontLock(font, fontLock); + return rc; +} + +void ResetScaleOutputs(float* w, float* h) { + if (w) *w = 0.0f; + if (h) *h = 0.0f; +} + +void ResetWeightOutputs(float* weightXScale, float* weightYScale, std::uint32_t* mode) { + if (weightXScale) *weightXScale = 1.0f; + if (weightYScale) *weightYScale = 1.0f; + if (mode) *mode = 0; +} + +float ClampWeightDelta(float scale) { + return std::clamp(scale - 1.0f, -0.04f, 0.04f); +} + +} + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontSetScalePixel(FontHandle fontHandle, float w, float h) { + return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { + const int changed = StyleStateSetScalePixel(&font->style, w, h); + if (FontState* state = TryGetState(fontHandle)) { + state->scaleW = w; + state->scaleH = h; + } + return changed; + }); +} + +int APS5_VABI sceFontSetScalePoint(FontHandle fontHandle, float w, float h) { + return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetScalePoint(&font->style, w, h); }); +} + +int APS5_VABI sceFontSetResolutionDpi(FontHandle fontHandle, std::uint32_t hDpi, std::uint32_t vDpi) { + auto* font = GetNativeFont(fontHandle); + std::uint32_t fontLock = 0; + if (!font || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + if (StyleStateSetDpi(&font->style, hDpi, vDpi) != 0) font->cached_style.layout_cache_state = 0; + ReleaseFontLock(font, fontLock); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontSetEffectSlant(FontHandle fontHandle, float slantRatio) { + return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetSlantRatio(&font->style, slantRatio); }); +} + +int APS5_VABI sceFontSetEffectWeight(FontHandle fontHandle, float weightXScale, float weightYScale, std::uint32_t mode) { + if (mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetWeightScale(&font->style, weightXScale, weightYScale); }); +} + +int APS5_VABI sceFontGetScalePixel(FontHandle fontHandle, float* w, float* h) { + const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetScalePixel(&font->style, w, h); }); + if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h); + return rc; +} + +int APS5_VABI sceFontGetScalePoint(FontHandle fontHandle, float* w, float* h) { + const int rc = ReadFontStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePoint(&font->style, w, h); }); + if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h); + return rc; +} + +int APS5_VABI sceFontGetEffectSlant(FontHandle fontHandle, float* slantRatio) { + const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetSlantRatio(&font->style, slantRatio); }); + if (rc != SCE_FONT_OK && slantRatio) *slantRatio = 0.0f; + return rc; +} + +int APS5_VABI sceFontGetEffectWeight(FontHandle fontHandle, float* weightXScale, float* weightYScale, std::uint32_t* mode) { + const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetWeightScale(&font->style, weightXScale, weightYScale, mode); }); + if (rc != SCE_FONT_OK) ResetWeightOutputs(weightXScale, weightYScale, mode); + return rc; +} + +int APS5_VABI sceFontSetupRenderScalePixel(FontHandle fontHandle, float w, float h) { + return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) { + if (StyleStateSetScalePixel(&font->cached_style.state, w, h) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; + return SCE_FONT_OK; + }); +} + +int APS5_VABI sceFontSetupRenderScalePoint(FontHandle fontHandle, float w, float h) { + return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) { + if (StyleStateSetScalePoint(&font->cached_style.state, w, h) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; + return SCE_FONT_OK; + }); +} + +int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle fontHandle, float slantRatio) { + return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) { + if (StyleStateSetSlantRatio(&font->cached_style.state, slantRatio) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; + return SCE_FONT_OK; + }); +} + +int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle fontHandle, float weightXScale, float weightYScale, std::uint32_t mode) { + return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) { + if (mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (StyleStateSetWeightScale(&font->cached_style.state, weightXScale, weightYScale) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u; + return SCE_FONT_OK; + }); +} + +int APS5_VABI sceFontGetRenderScalePixel(FontHandle fontHandle, float* w, float* h) { + const int rc = ReadRenderStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePixel(&font->cached_style.state, w, h); }); + if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h); + return rc; +} + +int APS5_VABI sceFontGetRenderScalePoint(FontHandle fontHandle, float* w, float* h) { + const int rc = ReadRenderStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePoint(&font->cached_style.state, w, h); }); + if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h); + return rc; +} + +int APS5_VABI sceFontGetRenderEffectSlant(FontHandle fontHandle, float* slantRatio) { + const int rc = ReadRenderStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetSlantRatio(&font->cached_style.state, slantRatio); }); + if (rc != SCE_FONT_OK && slantRatio) *slantRatio = 0.0f; + return rc; +} + +int APS5_VABI sceFontGetRenderEffectWeight(FontHandle fontHandle, float* weightXScale, float* weightYScale, std::uint32_t* mode) { + const int rc = ReadRenderStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetWeightScale(&font->cached_style.state, weightXScale, weightYScale, mode); }); + if (rc != SCE_FONT_OK) ResetWeightOutputs(weightXScale, weightYScale, mode); + return rc; +} + +int APS5_VABI sceFontStyleFrameInit(FontStyleFrame* styleFrame) { + if (!styleFrame) return SCE_FONT_ERROR_INVALID_PARAMETER; + std::memset(&styleFrame->scaleUnit, 0, 0x20); + std::memset(&styleFrame->layout_cache_state, 0, 0x20); + std::memset(&styleFrame->layout_cache_bytes[0x08], 0, 0x20); + styleFrame->magic = STYLE_FRAME_MAGIC; + styleFrame->flags1 = 0; + styleFrame->flags2 = 0; + styleFrame->hDpi = 0x48; + styleFrame->vDpi = 0x48; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameSetScalePixel(FontStyleFrame* styleFrame, float w, float h) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->scaleUnit = 0; + styleFrame->scalePixelW = w; + styleFrame->scalePixelH = h; + styleFrame->flags1 |= STYLE_FRAME_FLAG_SCALE; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameSetScalePoint(FontStyleFrame* styleFrame, float w, float h) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->scaleUnit = 1; + styleFrame->scalePixelW = w; + styleFrame->scalePixelH = h; + styleFrame->flags1 |= STYLE_FRAME_FLAG_SCALE; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameSetResolutionDpi(FontStyleFrame* styleFrame, std::uint32_t hDpi, std::uint32_t vDpi) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->hDpi = hDpi == 0 ? 0x48 : hDpi; + styleFrame->vDpi = vDpi == 0 ? 0x48 : vDpi; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameSetEffectSlant(FontStyleFrame* styleFrame, float slantRatio) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->slantRatio = std::clamp(slantRatio, -1.0f, 1.0f); + styleFrame->flags1 |= STYLE_FRAME_FLAG_SLANT; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameSetEffectWeight(FontStyleFrame* styleFrame, float weightXScale, float weightYScale, std::uint32_t mode) { + if (!ValidStyleFrame(styleFrame) || mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->effectWeightX = ClampWeightDelta(weightXScale); + styleFrame->effectWeightY = ClampWeightDelta(weightYScale); + styleFrame->flags1 |= STYLE_FRAME_FLAG_WEIGHT; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameUnsetScale(FontStyleFrame* styleFrame) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags1 &= static_cast(~STYLE_FRAME_FLAG_SCALE); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameUnsetEffectSlant(FontStyleFrame* styleFrame) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags1 &= static_cast(~STYLE_FRAME_FLAG_SLANT); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameUnsetEffectWeight(FontStyleFrame* styleFrame) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + styleFrame->flags1 &= static_cast(~STYLE_FRAME_FLAG_WEIGHT); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameGetScalePixel(const FontStyleFrame* styleFrame, float* w, float* h) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER; + if (!w && !h) return SCE_FONT_ERROR_INVALID_PARAMETER; + const bool points = styleFrame->scaleUnit != 0; + if (w) *w = points && styleFrame->hDpi != 0 ? styleFrame->scalePixelW * (static_cast(styleFrame->hDpi) / POINTS_PER_INCH) : styleFrame->scalePixelW; + if (h) *h = points && styleFrame->vDpi != 0 ? styleFrame->scalePixelH * (static_cast(styleFrame->vDpi) / POINTS_PER_INCH) : styleFrame->scalePixelH; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameGetScalePoint(const FontStyleFrame* styleFrame, float* w, float* h) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER; + if (!w && !h) return SCE_FONT_ERROR_INVALID_PARAMETER; + const bool pixels = styleFrame->scaleUnit == 0; + if (w) *w = pixels && styleFrame->hDpi != 0 ? styleFrame->scalePixelW * (POINTS_PER_INCH / static_cast(styleFrame->hDpi)) : styleFrame->scalePixelW; + if (h) *h = pixels && styleFrame->vDpi != 0 ? styleFrame->scalePixelH * (POINTS_PER_INCH / static_cast(styleFrame->vDpi)) : styleFrame->scalePixelH; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameGetResolutionDpi(const FontStyleFrame* styleFrame, std::uint32_t* hDpi, std::uint32_t* vDpi) { + if (!ValidStyleFrame(styleFrame) || (!hDpi && !vDpi)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (hDpi) *hDpi = styleFrame->hDpi; + if (vDpi) *vDpi = styleFrame->vDpi; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameGetEffectSlant(const FontStyleFrame* styleFrame, float* slantRatio) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SLANT) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER; + if (!slantRatio) return SCE_FONT_ERROR_INVALID_PARAMETER; + *slantRatio = styleFrame->slantRatio; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontStyleFrameGetEffectWeight(const FontStyleFrame* styleFrame, float* weightXScale, float* weightYScale, std::uint32_t* mode) { + if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER; + if ((styleFrame->flags1 & STYLE_FRAME_FLAG_WEIGHT) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER; + if (!weightXScale && !weightYScale && !mode) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (weightXScale) *weightXScale = styleFrame->effectWeightX + 1.0f; + if (weightYScale) *weightYScale = styleFrame->effectWeightY + 1.0f; + if (mode) *mode = 0; + return SCE_FONT_OK; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/Text.cpp b/core/libs/prx/libSceFont/src/Text.cpp new file mode 100644 index 000000000..5a43ff63f --- /dev/null +++ b/core/libs/prx/libSceFont/src/Text.cpp @@ -0,0 +1,633 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include "prx/libSceFont/include/FontInternal.hpp" + +namespace { + +using namespace Font; + +constexpr std::uint16_t TEXT_SOURCE_MAGIC = 0x0F04; +constexpr std::uint16_t FONT_STRING_MAGIC = 0x0F05; +constexpr std::uint16_t FONT_WRITING_MAGIC = 0x0F06; +constexpr std::uint16_t CREATE_STRING_DETAIL_ID = 0x0FD4; +constexpr std::uint32_t WRITING_FORM_HORIZONTAL = 0x10; +constexpr std::uint32_t WRITING_FORM_HORIZONTAL_LTR = 0x12; +constexpr std::int32_t PARSER_RESULT_ERROR = -1; +constexpr std::int32_t PARSER_RESULT_TERMINATE = 0; +constexpr std::int32_t PARSER_RESULT_FONT_CODE = 1; +constexpr std::size_t MAX_PARSED_CHARACTERS = 4096; +constexpr std::int32_t WRITING_MASK_NONE = 0; +constexpr std::int32_t WRITING_MASK_FORMAT_CHARACTERS = 1; +constexpr std::uint64_t CHARACTER_FLAG_FORMAT = 1ull << 42; + +struct CharacterStorage { + std::atomic refCount{1}; + std::vector characters; +}; + +struct FontStringData { + std::uint16_t magic = FONT_STRING_MAGIC; + std::uint8_t flags = 0; + std::uint8_t reserved0 = 0; + std::uint32_t writingForm = WRITING_FORM_HORIZONTAL; + const FontMemory* memory = nullptr; + FontTextSource* source = nullptr; + FontHandle defaultFont = nullptr; + std::uint32_t terminateCode = 0; + void* terminateOrder = nullptr; + std::uint32_t textCount = 0; + std::uint32_t renderCount = 0; + CharacterStorage* storage = nullptr; +}; + +struct FontWritingData { + std::uint16_t magic = FONT_WRITING_MAGIC; + std::uint16_t reserved0 = 0; + std::uint32_t writingForm = WRITING_FORM_HORIZONTAL; + FontString string = nullptr; + CharacterStorage* storage = nullptr; + FontHandle defaultFont = nullptr; + std::size_t nextIndex = 0; + std::size_t currentIndex = 0; + float penX = 0.0f; + float penY = 0.0f; + bool hasAggregateMetrics = false; + bool hasCurrentStep = false; + bool maskFormatCharacters = true; + bool ownsStorage = false; + FontWritingStep step{}; + FontWritingMetrics metrics{}; + FontGlyphMetrics rawGlyphMetrics{}; + bool hasRawGlyphMetrics = false; +}; + +static_assert(sizeof(FontWritingData) <= sizeof(FontWriting)); + +FontStringData* GetStringData(FontString fontString) { + auto* data = reinterpret_cast(fontString); + return data && data->magic == FONT_STRING_MAGIC ? data : nullptr; +} + +FontWritingData* GetWritingData(FontWriting* fontWriting) { + if (!fontWriting) return nullptr; + auto* data = reinterpret_cast(fontWriting->systemUse); + return data->magic == FONT_WRITING_MAGIC ? data : nullptr; +} + +void ReleaseStorage(CharacterStorage*& storage) { + if (!storage) return; + if (storage->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1) delete storage; + storage = nullptr; +} + +void ReleaseWritingStorage(FontWritingData& writing) { + if (!writing.ownsStorage) return; + ReleaseStorage(writing.storage); + writing.ownsStorage = false; +} + +std::uint16_t TextSourceMagic(const FontTextSource* source) { + return static_cast(source->systemUse0 & 0xFFFFu); +} + +bool IsWhitespaceCode(std::uint32_t code) { + switch (code) { + case 0x0009: case 0x000A: case 0x000B: case 0x000C: case 0x000D: case 0x0020: case 0x0085: + case 0x00A0: case 0x1680: case 0x2028: case 0x2029: case 0x202F: case 0x205F: case 0x3000: + return true; + default: + return code >= 0x2000 && code <= 0x200A; + } +} + +bool IsFormatCode(std::uint32_t code) { + switch (code) { + case 0x00AD: case 0x061C: case 0x200B: case 0x200C: case 0x200D: case 0x200E: case 0x200F: + case 0x2060: case 0x2061: case 0x2062: case 0x2063: case 0x2064: case 0x2066: case 0x2067: + case 0x2068: case 0x2069: case 0xFE0E: case 0xFE0F: case 0xFEFF: + return true; + default: + return false; + } +} + +FontTextCharacter MakeCharacter(FontHandle font, std::uint32_t code, void* textOrder) { + FontTextCharacter character{}; + character.textOrder = textOrder; + character.font = font; + character.characterCode = code; + character.clusterSpan = 1; + character.flags = static_cast(IsWhitespaceCode(code) ? 0x0E : 0) << 8; + if (IsFormatCode(code)) character.flags |= CHARACTER_FLAG_FORMAT; + return character; +} + +void LinkCharacters(std::vector& characters) { + for (std::size_t i = 0; i < characters.size(); ++i) { + characters[i].prev = i == 0 ? nullptr : &characters[i - 1]; + characters[i].next = i + 1 < characters.size() ? &characters[i + 1] : nullptr; + } +} + +FontTextCharacter* TextCharacters(FontStringData* data) { + return data && data->storage && !data->storage->characters.empty() ? data->storage->characters.data() : nullptr; +} + +std::uint32_t LevelOf(const FontTextCharacter* character) { + return static_cast((character->flags >> 24) & 0xFFu); +} + +void FillWritingMetricsFromGlyph(FontWritingData& writing, const FontGlyphMetrics& glyphMetrics) { + writing.rawGlyphMetrics = glyphMetrics; + writing.hasRawGlyphMetrics = true; + writing.step.advanceX = glyphMetrics.Horizontal.advance; + writing.step.advanceY = glyphMetrics.Vertical.advance; + writing.step.GlyphMetrics = glyphMetrics; + writing.step.Positioning.x = writing.step.x; + writing.step.Positioning.y = writing.step.y; + writing.step.GlyphMetrics.width = writing.step.x; + writing.step.GlyphMetrics.height = writing.step.y; +} + +bool PopulateWritingStep(FontWritingData& writing, std::size_t index) { + if (!writing.storage || index >= writing.storage->characters.size()) return false; + const FontTextCharacter& character = writing.storage->characters[index]; + const FontHandle font = character.font ? character.font : writing.defaultFont; + writing.step.x = writing.penX; + writing.step.y = writing.penY; + writing.step.advanceX = 0.0f; + writing.step.advanceY = 0.0f; + writing.step.font = font; + writing.step.Profile = {}; + writing.step.Profile.characterCount = 1; + writing.step.glyphCode = character.characterCode; + writing.step.Positioning.x = 0.0f; + writing.step.Positioning.y = 0.0f; + writing.step.GlyphMetrics = {}; + writing.rawGlyphMetrics = {}; + writing.hasRawGlyphMetrics = false; + if ((character.flags & CHARACTER_FLAG_FORMAT) != 0 && writing.maskFormatCharacters) writing.step.Profile.invisibleGlyph = 1; + if (font && character.characterCode != 0) { + FontGlyphMetrics glyphMetrics{}; + int rc = GetCharGlyphMetrics(font, character.characterCode, &glyphMetrics, true); + if (rc != SCE_FONT_OK) rc = GetCharGlyphMetrics(font, character.characterCode, &glyphMetrics, false); + if (rc == SCE_FONT_OK) FillWritingMetricsFromGlyph(writing, glyphMetrics); + } + writing.penX += writing.step.advanceX; + writing.penY += writing.step.advanceY; + return true; +} + +void AccumulateWritingMetrics(FontWritingData& writing) { + const FontWritingMetrics previous = writing.metrics; + const FontGlyphMetrics& glyphMetrics = writing.hasRawGlyphMetrics ? writing.rawGlyphMetrics : writing.step.GlyphMetrics; + const float left = writing.step.x + glyphMetrics.Horizontal.bearingX; + const float right = left + glyphMetrics.width; + const float top = writing.step.y - glyphMetrics.Horizontal.bearingY; + const float bottom = top + glyphMetrics.height; + writing.metrics.advanceX = writing.step.x + writing.step.advanceX; + writing.metrics.advanceY = writing.step.y + writing.step.advanceY; + if (!writing.hasAggregateMetrics) { + writing.metrics.Extent = {top, bottom, left, right}; + writing.hasAggregateMetrics = true; + return; + } + writing.metrics.Extent.left = std::min(previous.Extent.left, left); + writing.metrics.Extent.right = std::max(previous.Extent.right, right); + writing.metrics.Extent.top = std::min(previous.Extent.top, top); + writing.metrics.Extent.bottom = std::max(previous.Extent.bottom, bottom); +} + +} + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontTextSourceInit(FontTextSource* textSource, const void* textAddress, std::uint32_t textSizeByte, FontTextParseFunction textParser, void* textObject) { + if (!textSource) return SCE_FONT_ERROR_INVALID_PARAMETER; + std::memset(textSource, 0, sizeof(*textSource)); + if (!textParser) return SCE_FONT_ERROR_INVALID_PARAMETER; + const void* end = textSizeByte != 0 ? static_cast(textAddress) + textSizeByte : nullptr; + textSource->systemUse0 = (static_cast(WRITING_FORM_HORIZONTAL) << 32) | TEXT_SOURCE_MAGIC; + textSource->start = textAddress; + textSource->end = end; + textSource->current = textAddress; + textSource->textParser = textParser; + textSource->textObject = textObject; + textSource->defaultFont = nullptr; + textSource->systemUse[0] = const_cast(textAddress); + textSource->systemUse[1] = const_cast(end); + textSource->systemUse[2] = reinterpret_cast(textParser); + textSource->systemUse[3] = textObject; + textSource->systemUse[4] = nullptr; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontTextSourceRewind(FontTextSource* textSource) { + if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER; + textSource->start = textSource->systemUse[0]; + textSource->end = textSource->systemUse[1]; + textSource->current = textSource->systemUse[0]; + textSource->textParser = reinterpret_cast(textSource->systemUse[2]); + textSource->textObject = textSource->systemUse[3]; + textSource->defaultFont = static_cast(textSource->systemUse[4]); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* textSource, FontHandle defaultFont) { + if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER; + textSource->defaultFont = defaultFont; + textSource->systemUse[4] = defaultFont; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* textSource, std::int32_t writingForm) { + if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (writingForm < static_cast(WRITING_FORM_HORIZONTAL) || writingForm > static_cast(WRITING_FORM_HORIZONTAL_LTR)) return SCE_FONT_ERROR_INVALID_PARAMETER; + textSource->systemUse0 = (textSource->systemUse0 & 0xFFFFFFFFull) | (static_cast(writingForm) << 32); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCreateString(const FontMemory* fontMemory, FontTextSource* textSource, const FontCreateStringDetail* detail, FontString* pFontString) { + if (!fontMemory || !textSource || !pFontString || !textSource->textParser) { + if (pFontString) *pFontString = nullptr; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + *pFontString = nullptr; + if (TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + if (fontMemory->mem_kind != MEMORY_MAGIC || !fontMemory->iface || !fontMemory->iface->alloc || !fontMemory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY; + if (detail) { + constexpr std::uint32_t validOrders = (1u << 0) | (1u << 1) | (1u << 2) | (1u << 31); + if (detail->detailId != CREATE_STRING_DETAIL_ID || detail->detailType > 1 || (detail->detections & ~0x07u) != 0 || (detail->ordersOption & ~validOrders) != 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + } + void* raw = fontMemory->iface->alloc(fontMemory->mspace_handle, sizeof(FontStringData)); + if (!raw) return SCE_FONT_ERROR_ALLOCATION_FAILED; + auto* data = new (raw) FontStringData{}; + data->memory = fontMemory; + data->source = textSource; + data->defaultFont = textSource->defaultFont; + data->storage = new CharacterStorage{}; + if (!data->defaultFont && detail) data->defaultFont = detail->defaultFont; + data->writingForm = static_cast(textSource->systemUse0 >> 32); + std::int32_t state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + void* textOrder = nullptr; + for (std::size_t parsed = 0; parsed < MAX_PARSED_CHARACTERS; ++parsed) { + FontTextParseResult result{}; + state = textSource->textParser(textSource, &textOrder, &result); + if (state == PARSER_RESULT_FONT_CODE) { + const FontHandle font = result.FontCode.font ? result.FontCode.font : data->defaultFont; + if (!font) { + state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + break; + } + data->storage->characters.push_back(MakeCharacter(font, result.FontCode.code, textOrder)); + continue; + } + if (state == PARSER_RESULT_TERMINATE) { + data->terminateOrder = textOrder; + data->terminateCode = result.Terminate.terminateCode; + state = SCE_FONT_OK; + } else if (state == PARSER_RESULT_ERROR) { + state = result.Error.errorCode < 0 ? result.Error.errorCode : SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + } else { + state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + } + break; + } + if (state == PARSER_RESULT_FONT_CODE) state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE; + if (state != SCE_FONT_OK) { + ReleaseStorage(data->storage); + data->~FontStringData(); + fontMemory->iface->dealloc(fontMemory->mspace_handle, raw); + return state; + } + LinkCharacters(data->storage->characters); + data->textCount = static_cast(data->storage->characters.size()); + data->renderCount = data->textCount; + *pFontString = reinterpret_cast(data); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontDestroyString(FontString* pFontString) { + if (!pFontString) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* data = GetStringData(*pFontString); + if (!data) { + *pFontString = nullptr; + return SCE_FONT_ERROR_INVALID_STRING; + } + const FontMemory* memory = data->memory; + ReleaseStorage(data->storage); + data->~FontStringData(); + memory->iface->dealloc(memory->mspace_handle, data); + *pFontString = nullptr; + return SCE_FONT_OK; +} + +std::uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString fontString) { + const auto* data = GetStringData(fontString); + return data ? data->terminateCode : 0; +} + +void* APS5_VABI sceFontStringGetTerminateOrder(FontString fontString) { + const auto* data = GetStringData(fontString); + return data ? data->terminateOrder : nullptr; +} + +int APS5_VABI sceFontStringGetWritingForm(FontString fontString) { + const auto* data = GetStringData(fontString); + return data ? static_cast(data->writingForm) : 0; +} + +FontTextCharacter* APS5_VABI sceFontStringRefersTextCharacters(FontString fontString, std::uint32_t* characterCount) { + auto* data = GetStringData(fontString); + if (!data || data->textCount == 0) { + if (characterCount) *characterCount = 0; + return nullptr; + } + if (characterCount) *characterCount = data->textCount; + return TextCharacters(data); +} + +const FontTextCharacter* APS5_VABI sceFontStringRefersRenderCharacters(FontString fontString, FontTextCharacter* startCharacter, FontTextCharacter* lastCharacter, std::uint32_t* characterCount) { + auto* data = GetStringData(fontString); + if (!data || !characterCount || data->renderCount == 0) { + if (characterCount) *characterCount = 0; + return nullptr; + } + FontTextCharacter* characters = TextCharacters(data); + if (!characters) { + *characterCount = 0; + return nullptr; + } + const FontTextCharacter* begin = characters; + const FontTextCharacter* end = characters + data->renderCount; + if (!startCharacter) startCharacter = characters; + if (startCharacter < begin || startCharacter >= end || (lastCharacter && (lastCharacter < begin || lastCharacter >= end))) { + *characterCount = 0; + return nullptr; + } + FontTextCharacter* probe = startCharacter; + if (startCharacter->clusterKind > 1) { + do { + startCharacter = probe; + probe = startCharacter->prev; + if (!probe) break; + } while (probe->synthetic < 0); + } + auto startFlags = static_cast(startCharacter->flags); + const std::uint32_t paragraphLevel = (data->writingForm >> 8) & 1u; + if ((startFlags & 0xFF00u) == 0x100u && startCharacter->prev) { + if (startCharacter->prev->clusterKind == 1) startCharacter = startCharacter->prev; + startFlags = static_cast(startCharacter->flags); + } + std::uint32_t startLevel = startFlags >> 24; + std::uint32_t lastLevel = startLevel; + std::uint32_t minLevel = startLevel; + std::uint32_t count = startCharacter->clusterIndex == 0 ? 1u : 0u; + probe = startCharacter; + while (probe != lastCharacter) { + probe = probe->next; + if (!probe) { + if (lastCharacter) { + *characterCount = 0; + return nullptr; + } + break; + } + if (probe->clusterIndex == 0) { + lastLevel = LevelOf(probe); + if (lastLevel < minLevel && ((lastLevel ^ paragraphLevel) & 1u) == 0) minLevel = lastLevel; + ++count; + } + } + if (startLevel != lastLevel || startLevel != minLevel) { + probe = startCharacter->prev; + while (probe) { + if (probe->clusterIndex == 0) { + if (LevelOf(probe) <= minLevel) break; + ++count; + } + startCharacter = probe; + probe = probe->prev; + } + if (lastCharacter) { + probe = lastCharacter->next; + while (probe) { + if (probe->clusterIndex == 0) { + if (LevelOf(probe) <= minLevel) break; + ++count; + lastCharacter = probe; + } + probe = probe->next; + } + } + } + if (lastCharacter) { + probe = lastCharacter->next; + std::uint32_t clusterTrim = 0; + while (probe) { + std::uint32_t nextTrim = clusterTrim; + if (probe->clusterIndex == 0) { + if (probe->synthetic == 0) break; + nextTrim = 0; + FontTextCharacter* nextLast = probe; + if (probe->synthetic < 1) { + if (probe->clusterKind == 1) break; + nextTrim = clusterTrim + 1; + nextLast = lastCharacter; + } + lastCharacter = nextLast; + ++count; + } + probe = probe->next; + count -= nextTrim; + clusterTrim = nextTrim; + } + } + startLevel = LevelOf(startCharacter); + if (startLevel != paragraphLevel) { + probe = startCharacter; + if (!lastCharacter || LevelOf(lastCharacter) != startLevel) { + do { + probe = probe->next; + if (!probe) { + if (lastCharacter) startCharacter = nullptr; + break; + } + FontTextCharacter* best = startCharacter; + std::uint32_t bestLevel = startLevel; + if (probe->clusterIndex == 0) { + const std::uint32_t probeLevel = LevelOf(probe); + if (probeLevel <= paragraphLevel) break; + if ((startLevel & 1u) == paragraphLevel && probeLevel < startLevel && (probeLevel & 1u) != paragraphLevel) { + best = probe; + bestLevel = probeLevel; + } + } + startLevel = bestLevel; + startCharacter = best; + } while (probe != lastCharacter); + } else if ((LevelOf(startCharacter) & 1u) != paragraphLevel) { + startCharacter = lastCharacter; + } + } + if (!startCharacter) count = 0; + *characterCount = count; + return startCharacter; +} + +int APS5_VABI sceFontCharacterGetBidiLevel(const FontTextCharacter* textCharacter, int* bidiLevel) { + if (!textCharacter || !bidiLevel) return SCE_FONT_ERROR_INVALID_PARAMETER; + *bidiLevel = static_cast(LevelOf(textCharacter)); + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCharacterGetTextFontCode(const FontTextCharacter* textCharacter, FontHandle* pFontHandle, std::uint32_t* textCode) { + if (!textCharacter) { + if (pFontHandle) *pFontHandle = nullptr; + if (textCode) *textCode = 0; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + if (!pFontHandle || !textCode) return SCE_FONT_ERROR_INVALID_PARAMETER; + *pFontHandle = textCharacter->font; + *textCode = textCharacter->characterCode; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontCharacterGetTextOrder(const FontTextCharacter* textCharacter, void** pTextOrder) { + if (!pTextOrder) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!textCharacter) { + *pTextOrder = nullptr; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + *pTextOrder = textCharacter->textOrder; + return SCE_FONT_OK; +} + +std::uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(const FontTextCharacter* textCharacter) { + if (!textCharacter) return 0; + return (textCharacter->flags & CHARACTER_FLAG_FORMAT) != 0 ? textCharacter->characterCode : 0; +} + +std::uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(const FontTextCharacter* textCharacter) { + if (!textCharacter) return 0; + return ((textCharacter->flags >> 8) & 0xFFu) == 0x0E ? textCharacter->characterCode : 0; +} + +FontTextCharacter* APS5_VABI sceFontCharacterRefersTextBack(const FontTextCharacter* textCharacter) { + if (!textCharacter) return nullptr; + for (FontTextCharacter* current = textCharacter->prev; current; current = current->prev) { + if (current->synthetic == 0 && current->clusterIndex == 0) return current; + } + return nullptr; +} + +FontTextCharacter* APS5_VABI sceFontCharacterRefersTextNext(const FontTextCharacter* textCharacter) { + if (!textCharacter) return nullptr; + for (FontTextCharacter* current = textCharacter->next; current; current = current->next) { + if (current->synthetic == 0 && current->clusterIndex == 0) return current; + } + return nullptr; +} + +int APS5_VABI sceFontWritingInit(FontWriting* fontWriting, FontString fontString, const FontTextCharacter* fontCharacter) { + if (!fontWriting || !fontString || !fontCharacter) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* data = GetStringData(fontString); + if (!data) return SCE_FONT_ERROR_INVALID_STRING; + if (auto* previous = GetWritingData(fontWriting)) ReleaseWritingStorage(*previous); + std::memset(fontWriting, 0, sizeof(*fontWriting)); + auto* writing = new (fontWriting->systemUse) FontWritingData{}; + writing->string = fontString; + writing->storage = data->storage; + writing->defaultFont = data->defaultFont; + if (writing->storage) writing->storage->refCount.fetch_add(1, std::memory_order_relaxed); + writing->ownsStorage = true; + writing->writingForm = data->writingForm; + const FontTextCharacter* characters = TextCharacters(data); + if (!characters || fontCharacter < characters || fontCharacter >= characters + data->storage->characters.size()) { + ReleaseWritingStorage(*writing); + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + writing->nextIndex = static_cast(fontCharacter - characters); + writing->currentIndex = writing->nextIndex; + writing->hasCurrentStep = false; + if (!PopulateWritingStep(*writing, writing->nextIndex)) { + ReleaseWritingStorage(*writing); + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + writing->penX = 0.0f; + writing->penY = 0.0f; + writing->hasAggregateMetrics = false; + writing->metrics = {}; + return SCE_FONT_OK; +} + +int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* fontWriting, FontWritingMetrics* writingMetrics) { + if (!fontWriting || !writingMetrics) { + if (writingMetrics) *writingMetrics = {}; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + const auto* writing = GetWritingData(fontWriting); + if (!writing) { + *writingMetrics = {}; + return SCE_FONT_ERROR_INVALID_WRITING; + } + *writingMetrics = writing->metrics; + return SCE_FONT_OK; +} + +const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* fontWriting) { + auto* writing = GetWritingData(fontWriting); + if (!writing || !writing->storage || writing->nextIndex >= writing->storage->characters.size()) return nullptr; + if (!PopulateWritingStep(*writing, writing->nextIndex)) return nullptr; + writing->currentIndex = writing->nextIndex; + writing->hasCurrentStep = true; + AccumulateWritingMetrics(*writing); + ++writing->nextIndex; + return &writing->step; +} + +FontTextCharacter* APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* fontWriting, FontWritingLetterStep* letterStep) { + if (letterStep) std::memset(letterStep, 0, sizeof(*letterStep)); + auto* writing = GetWritingData(fontWriting); + if (!writing || !writing->hasCurrentStep || !writing->storage || writing->currentIndex >= writing->storage->characters.size()) return nullptr; + if (letterStep) { + letterStep->x = writing->step.x; + letterStep->y = writing->step.y; + letterStep->advanceX = writing->step.advanceX; + letterStep->advanceY = writing->step.advanceY; + letterStep->Components.textsCount = 1; + letterStep->Components.textsIndex = static_cast(writing->currentIndex); + letterStep->Components.glyphsCount = 1; + letterStep->Components.glyphsIndex = static_cast(writing->currentIndex); + letterStep->Components.characterTextCount = static_cast(std::max(writing->step.Profile.characterCount, 1)); + } + return &writing->storage->characters[writing->currentIndex]; +} + +int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* fontWriting, std::int32_t mask) { + auto* writing = GetWritingData(fontWriting); + if (!writing) return SCE_FONT_ERROR_INVALID_WRITING; + if (mask == WRITING_MASK_FORMAT_CHARACTERS) { + writing->maskFormatCharacters = false; + return SCE_FONT_OK; + } + if (mask == WRITING_MASK_NONE) { + writing->maskFormatCharacters = true; + return SCE_FONT_OK; + } + return SCE_FONT_ERROR_INVALID_PARAMETER; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp new file mode 100644 index 000000000..de6b6e3d0 --- /dev/null +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -0,0 +1,477 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "prx/libc/include/General.hpp" + +#pragma GCC visibility push(default) + +extern "C" { + +int APS5_VABI sceFontCharacterGetSyllableStringState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCharactersRefersTextCodes() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontControl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCreateGraphicsDevice() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCreateGraphicsService() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCreateGraphicsServiceWithEdition() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCreateWords() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontCreateWritingLine() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDefineAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDestroyGraphicsDevice() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDestroyGraphicsService() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDestroyWords() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDestroyWritingLine() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontDettachDeviceCacheBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetCharGlyphCode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetFontGlyphsCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetFontGlyphsOutlineProfile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetFontMetrics() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetFontResolution() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetFontStyleInformation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetGlyphExpandBufferState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetPixelResolution() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetRenderScaledKerning() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetResolutionDpi() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetScriptLanguage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGetTypographicDesign() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGlyphDefineAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGlyphGetAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGlyphRenderImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGlyphRenderImageHorizontal() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGlyphRenderImageVertical() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsBeginFrame() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsDrawingCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsDrawingFinish() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsEndFrame() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsExchangeResource() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillMethodInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillPlotInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillPlotSetLayout() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillPlotSetMapping() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillRatesInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillRatesSetFillEffect() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillRatesSetLayout() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsFillRatesSetMapping() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsGetDeviceUsage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsRegionInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsRegionInitCircular() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsRegionInitRoundish() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsRelease() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsRenderResource() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetFramePolicy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupClipping() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupColorRates() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupFillMethod() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupFillRates() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupGlyphFill() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupGlyphFillPlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupHandleDefault() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupLocation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupPositioning() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupRotation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupScaling() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupShapeFill() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsSetupShapeFillPlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsStructureCanvas() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsStructureCanvasSequence() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsStructureDesign() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsStructureDesignResource() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsStructureSurfaceTexture() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateClipping() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateColorRates() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateFillMethod() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateFillRates() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateGlyphFill() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateGlyphFillPlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateLocation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdatePositioning() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateRotation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateScaling() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateShapeFill() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontGraphicsUpdateShapeFillPlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontSetScriptLanguage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontSetTypographicDesign() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontSupportGlyphs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontTextCodesStepBack() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontTextCodesStepNext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWordsFindWordCharacters() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWritingLineClear() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWritingLineGetOrderingSpace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWritingLineGetRenderMetrics() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWritingLineRefersRenderStep() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontWritingLineWritesOrder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +} + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFontFt/CMakeLists.txt b/core/libs/prx/libSceFontFt/CMakeLists.txt new file mode 100644 index 000000000..c1fa26487 --- /dev/null +++ b/core/libs/prx/libSceFontFt/CMakeLists.txt @@ -0,0 +1,15 @@ +find_package(Freetype REQUIRED) + +add_library(libSceFontFt SHARED EXCLUDE_FROM_ALL + ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Driver.cpp +) + +target_include_directories(libSceFontFt PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(libSceFontFt PRIVATE Freetype::Freetype libc) +set_target_properties(libSceFontFt PROPERTIES + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON +) +configure_windows_unwind(libSceFontFt) diff --git a/core/libs/prx/libSceFontFt/Export.cpp b/core/libs/prx/libSceFontFt/Export.cpp index a15c312e4..d34a3ca55 100644 --- a/core/libs/prx/libSceFontFt/Export.cpp +++ b/core/libs/prx/libSceFontFt/Export.cpp @@ -1,20 +1,116 @@ -#include -#include -#include "SceTypes.hpp" +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "prx/libSceFontFt/include/FontFtDriver.hpp" #include "prx/libc/include/General.hpp" +#pragma GCC visibility push(default) + extern "C" { -void* APS5_VABI sceFontSelectLibraryFt(int value) { - (void)value; - NotImplemented_nid_no_patch(__func__); - return nullptr; +const Font::SysDriver* APS5_VABI sceFontSelectLibraryFt(int value) { + return value == 0 ? FontFt::DriverTable() : nullptr; } -void* APS5_VABI sceFontSelectRendererFt(int value) { - (void)value; - NotImplemented_nid_no_patch(__func__); - return nullptr; +const Font::RendererSelection* APS5_VABI sceFontSelectRendererFt(int value) { + return value == 0 ? FontFt::RendererTable() : nullptr; +} + +int APS5_VABI sceFontFtInitAliases() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSetAliasFont() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSetAliasPath() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportBdf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportCid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportFontFormats() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportOpenType() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportOpenTypeOtf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportOpenTypeTtf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportPcf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportPfr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportSystemFonts() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportTrueType() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportTrueTypeGx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportType1() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportType42() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtSupportWinFonts() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontFtTermAliases() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFontSelectGlyphsFt() { + NotImplemented_nid_no_patch(__func__); + return 0; } } + +#pragma GCC visibility pop diff --git a/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp b/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp new file mode 100644 index 000000000..87e7e9293 --- /dev/null +++ b/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifndef CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP +#define CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP + +#include "prx/libSceFont/include/FontDriver.hpp" + +namespace FontFt { + +const Font::SysDriver* DriverTable(); +const Font::RendererSelection* RendererTable(); + +} + +#endif diff --git a/core/libs/prx/libSceFontFt/src/Driver.cpp b/core/libs/prx/libSceFontFt/src/Driver.cpp new file mode 100644 index 000000000..71c1ff144 --- /dev/null +++ b/core/libs/prx/libSceFontFt/src/Driver.cpp @@ -0,0 +1,711 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_MODULE_H +#include FT_SYSTEM_H +#include FT_TRUETYPE_TABLES_H + +#include "prx/libSceFont/include/FontFreeType.hpp" +#include "prx/libSceFontFt/include/FontFtDriver.hpp" + +namespace { + +using namespace Font; + +struct FtLibraryContext { + void* allocCtx; + const FontMemoryInterface* iface; + FT_Memory memory; + FT_Library library; +}; + +void* FtAlloc(FT_Memory memory, long size) { + if (!memory || size <= 0) return nullptr; + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->iface || !ctx->iface->alloc) return nullptr; + return ctx->iface->alloc(ctx->allocCtx, static_cast(size)); +} + +void FtFree(FT_Memory memory, void* block) { + if (!memory || !block) return; + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->iface || !ctx->iface->dealloc) return; + ctx->iface->dealloc(ctx->allocCtx, block); +} + +void* FtRealloc(FT_Memory memory, long currentSize, long newSize, void* block) { + if (!memory) return nullptr; + auto* ctx = static_cast(memory->user); + if (!ctx || !ctx->iface) return nullptr; + if (ctx->iface->realloc_fn) return ctx->iface->realloc_fn(ctx->allocCtx, block, static_cast(newSize)); + if (newSize <= 0) { + FtFree(memory, block); + return nullptr; + } + void* result = FtAlloc(memory, newSize); + if (!result) return nullptr; + if (block && currentSize > 0) std::memcpy(result, block, static_cast(std::min(currentSize, newSize))); + FtFree(memory, block); + return result; +} + +void WriteU16(std::uint8_t* out, std::size_t offset, std::uint16_t value) { + std::memcpy(out + offset, &value, sizeof(value)); +} + +void WriteU32(std::uint8_t* out, std::size_t offset, std::uint32_t value) { + std::memcpy(out + offset, &value, sizeof(value)); +} + +void WriteS32(std::uint8_t* out, std::size_t offset, std::int32_t value) { + std::memcpy(out + offset, &value, sizeof(value)); +} + +void WritePointer(std::uint8_t* out, std::size_t offset, const void* value) { + std::memcpy(out + offset, &value, sizeof(value)); +} + +float FromF26Dot6(std::int64_t value) { + return static_cast(value) * ONE_OVER_64; +} + +FT_Face FaceOf(const FontObj* obj) { + return static_cast(obj->ft_face); +} + +std::uint32_t APS5_VABI LibraryGetPixelResolution() { + return 0x40; +} + +int APS5_VABI LibraryInit(const FontMemory* memory, FontLibNative* library) { + if (!memory || !library) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!memory->iface || !memory->iface->alloc || !memory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY; + const auto allocFn = memory->iface->alloc; + const auto freeFn = memory->iface->dealloc; + void* allocCtx = memory->mspace_handle; + auto* ctx = static_cast(allocFn(allocCtx, sizeof(FtLibraryContext))); + if (!ctx) return SCE_FONT_ERROR_ALLOCATION_FAILED; + std::memset(ctx, 0, sizeof(FtLibraryContext)); + ctx->allocCtx = allocCtx; + ctx->iface = memory->iface; + auto* ftMemory = static_cast(allocFn(allocCtx, sizeof(FT_MemoryRec_))); + if (!ftMemory) { + freeFn(allocCtx, ctx); + return SCE_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(ftMemory, 0, sizeof(*ftMemory)); + ftMemory->user = ctx; + ftMemory->alloc = &FtAlloc; + ftMemory->free = &FtFree; + ftMemory->realloc = &FtRealloc; + ctx->memory = ftMemory; + FT_Library ftLibrary = nullptr; + if (FT_New_Library(ftMemory, &ftLibrary) != 0 || !ftLibrary) { + freeFn(allocCtx, ftMemory); + freeFn(allocCtx, ctx); + return SCE_FONT_ERROR_ALLOCATION_FAILED; + } + FT_Add_Default_Modules(ftLibrary); + ctx->library = ftLibrary; + library->fontset_registry = ctx; + library->flags = 0x60000000; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryTerm(FontLibNative* library) { + if (!library) return SCE_FONT_ERROR_INVALID_PARAMETER; + const auto freeFn = library->iface ? library->iface->dealloc : nullptr; + if (!freeFn) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* ctx = static_cast(library->fontset_registry); + if (!ctx) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (ctx->library) { + FT_Done_Library(ctx->library); + ctx->library = nullptr; + } + if (ctx->memory) { + freeFn(library->alloc_ctx, ctx->memory); + ctx->memory = nullptr; + } + freeFn(library->alloc_ctx, ctx); + library->fontset_registry = nullptr; + return SCE_FONT_OK; +} + +int APS5_VABI LibrarySupport(FontLibNative* library, std::uint32_t formats) { + (void)formats; + if (!library || !library->fontset_registry) return SCE_FONT_ERROR_INVALID_PARAMETER; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryOpenFont(FontLibNative* library, std::uint32_t mode, const void* fontAddress, std::uint32_t fontSize, std::uint32_t subFontIndex, std::uint32_t uniqueWord, FontObj** inoutFontObj) { + (void)uniqueWord; + if (!library || !fontAddress || !inoutFontObj) return SCE_FONT_ERROR_INVALID_PARAMETER; + const auto allocFn = library->iface ? library->iface->alloc : nullptr; + const auto freeFn = library->iface ? library->iface->dealloc : nullptr; + if (!allocFn || !freeFn) return SCE_FONT_ERROR_INVALID_PARAMETER; + const bool fromMemory = mode == 1; + if (fromMemory) { + if (fontSize == 0) return SCE_FONT_ERROR_INVALID_PARAMETER; + } else if (mode == 5 || mode == 6 || mode == 7) { + if (static_cast(fontAddress)[0] == '\0') return SCE_FONT_ERROR_INVALID_PARAMETER; + } else { + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + auto* ctx = static_cast(library->fontset_registry); + if (!ctx || !ctx->library) return SCE_FONT_ERROR_INVALID_LIBRARY; + FT_Face face = nullptr; + FT_Error error; + if (fromMemory) { + error = FT_New_Memory_Face(ctx->library, static_cast(fontAddress), static_cast(fontSize), static_cast(subFontIndex), &face); + } else { + error = FT_New_Face(ctx->library, static_cast(fontAddress), static_cast(subFontIndex), &face); + } + if (error != 0 || !face) { + if (fromMemory || error == FT_Err_Unknown_File_Format) return SCE_FONT_ERROR_NO_SUPPORT_FORMAT; + return SCE_FONT_ERROR_FS_OPEN_FAILED; + } + FT_Select_Charmap(face, FT_ENCODING_UNICODE); + auto* obj = static_cast(allocFn(library->alloc_ctx, sizeof(FontObj))); + if (!obj) { + FT_Done_Face(face); + return SCE_FONT_ERROR_ALLOCATION_FAILED; + } + std::memset(obj, 0, sizeof(FontObj)); + obj->refcount = 1; + obj->sub_font_index = subFontIndex; + obj->prev = nullptr; + obj->next = *inoutFontObj; + if (obj->next) obj->next->prev = obj; + obj->ft_face = face; + obj->ft_ctx_0x58 = &ctx->library; + *inoutFontObj = obj; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryCloseFont(FontObj* obj, std::uint32_t flags) { + (void)flags; + if (!obj) return SCE_FONT_ERROR_FATAL; + if (obj->refcount > 1) { + --obj->refcount; + return SCE_FONT_OK; + } + FT_Face face = FaceOf(obj); + FtLibraryContext* ctx = face && face->memory ? static_cast(face->memory->user) : nullptr; + const auto freeFn = ctx && ctx->iface ? ctx->iface->dealloc : nullptr; + if (face) { + FT_Done_Face(face); + obj->ft_face = nullptr; + } + if (!freeFn) return SCE_FONT_ERROR_FATAL; + FontObj* next = obj->next; + FontObj* prev = obj->prev; + if (prev) prev->next = next; + if (next) next->prev = prev; + freeFn(ctx->allocCtx, obj); + return SCE_FONT_OK; +} + +int APS5_VABI LibraryGetFaceScale(FontObj* obj, std::uint16_t* outUnitsPerEm, float* outScale) { + if (!obj || !outUnitsPerEm || !outScale) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face) return SCE_FONT_ERROR_FATAL; + const std::uint16_t units = face->units_per_EM; + *outUnitsPerEm = units; + *outScale = static_cast(units) * ONE_OVER_64; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryGetFaceMetric(FontObj* obj, std::uint32_t metricId, std::uint16_t* outMetric) { + if (!obj || !outMetric) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face) return SCE_FONT_ERROR_FATAL; + const std::uint16_t units = face->units_per_EM; + if (metricId == 0x0E00) { + *outMetric = units; + return SCE_FONT_OK; + } + if (metricId == 0xEA00) { + if (const auto* os2 = static_cast(FT_Get_Sfnt_Table(face, FT_SFNT_OS2))) { + *outMetric = static_cast(os2->sTypoAscender); + return SCE_FONT_OK; + } + *outMetric = units; + return SCE_FONT_ERROR_NO_SUPPORT_FUNCTION; + } + *outMetric = 0; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryGetGlyphIndex(FontObj* obj, std::uint32_t codepoint, std::uint32_t* outGlyphIndex) { + if (!obj || !outGlyphIndex) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face) { + *outGlyphIndex = 0; + return SCE_FONT_ERROR_FATAL; + } + const std::uint32_t glyphIndex = ResolveGlyphIndexWithFallback(face, codepoint); + *outGlyphIndex = glyphIndex; + return glyphIndex ? SCE_FONT_OK : SCE_FONT_ERROR_NO_SUPPORT_GLYPH; +} + +int APS5_VABI LibrarySetCharSizeWithDpi(FontObj* obj, std::uint32_t dpiX, std::uint32_t dpiY, float scaleX, float scaleY, float* outScaleX, float* outScaleY) { + if (!obj || !outScaleX || !outScaleY) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face) return SCE_FONT_ERROR_FATAL; + const auto charW = static_cast(static_cast(scaleX * 64.0f)); + const auto charH = static_cast(static_cast(scaleY * 64.0f)); + if (SetCharSizeCompat(face, charW, charH, dpiX, dpiY) != 0) return SCE_FONT_ERROR_FATAL; + if (!face->size) return SCE_FONT_ERROR_FATAL; + const std::uint16_t units = face->units_per_EM; + *outScaleX = FixedMulUnitsToF26Dot6(static_cast(face->size->metrics.x_scale), units); + *outScaleY = FixedMulUnitsToF26Dot6(static_cast(face->size->metrics.y_scale), units); + return SCE_FONT_OK; +} + +int APS5_VABI LibrarySetCharSizeDefaultDpi(FontObj* obj, float scaleX, float scaleY, float* outScaleX, float* outScaleY) { + if (!obj || !outScaleX || !outScaleY) return SCE_FONT_ERROR_FATAL; + return LibrarySetCharSizeWithDpi(obj, 0x48, 0x48, scaleX, scaleY, outScaleX, outScaleY); +} + +int APS5_VABI LibraryComputeLayout(FontObj* obj, const StyleStateBlock* style, std::uint8_t* outWords) { + if (!obj || !style || !outWords) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face || !face->size) return SCE_FONT_ERROR_FATAL; + const float effectWidth = style->effect_weight_x; + const float effectHeight = style->effect_weight_y; + const float slant = style->slant_ratio; + const auto yScale = static_cast(face->size->metrics.y_scale); + const auto xScale = static_cast(face->size->metrics.x_scale); + const std::int64_t yShift = obj->shift_cache_y; + const std::int64_t xShift = obj->shift_cache_x; + const auto unitsPerEm = static_cast(static_cast(face->units_per_EM)); + std::int32_t yMinPx = RoundMulFixed(static_cast(face->bbox.yMin) + yShift, yScale); + std::int32_t yMaxPx = RoundMulFixed(static_cast(face->bbox.yMax) + yShift, yScale); + std::int32_t halfEffectWidthPx = 0; + std::int32_t leftAdjustPx = 0; + if (effectWidth != 0.0f) { + const std::int32_t unitsScaledX = TruncFixed(unitsPerEm * xScale); + halfEffectWidthPx = TruncateFloatToInt(effectWidth * static_cast(unitsScaledX)) / 2; + leftAdjustPx = -halfEffectWidthPx; + } + float outEffectHeight = 0.0f; + if (effectHeight != 0.0f) { + const std::int32_t unitsScaledY = TruncFixed(unitsPerEm * yScale); + const std::int32_t halfEffectHeightPx = TruncateFloatToInt(effectHeight * static_cast(unitsScaledY)) / 2; + outEffectHeight = static_cast(halfEffectHeightPx) * ONE_OVER_64; + yMinPx -= halfEffectHeightPx; + yMaxPx += halfEffectHeightPx; + } + if (slant != 0.0f) { + const auto shear = static_cast(TruncateFloatToInt(slant * 65536.0f)); + leftAdjustPx += RoundMulFixed(yMinPx, shear); + halfEffectWidthPx += RoundMulFixed(yMaxPx, shear); + } + StoreFloat(outWords, HORIZONTAL_EFFECT_HEIGHT, outEffectHeight); + StoreFloat(outWords, HORIZONTAL_LEFT_ADJUST, static_cast(leftAdjustPx) * ONE_OVER_64); + StoreFloat(outWords, HORIZONTAL_HALF_EFFECT_WIDTH, static_cast(halfEffectWidthPx) * ONE_OVER_64); + const std::int32_t xMinPx = RoundMulFixed(static_cast(face->bbox.xMin) + xShift, xScale); + const std::int32_t xMaxPx = RoundMulFixed(static_cast(face->bbox.xMax) + xShift, xScale); + StoreFloat(outWords, HORIZONTAL_LINE_ADVANCE, static_cast(yMaxPx - yMinPx) * ONE_OVER_64); + StoreFloat(outWords, HORIZONTAL_BASELINE, static_cast(yMaxPx) * ONE_OVER_64); + StoreFloat(outWords, HORIZONTAL_X_BOUND_LO, static_cast(xMinPx + leftAdjustPx) * ONE_OVER_64); + StoreFloat(outWords, HORIZONTAL_X_BOUND_HI, static_cast(xMaxPx + halfEffectWidthPx) * ONE_OVER_64); + const std::int32_t maxAdvanceWidthPx = RoundMulFixed(static_cast(face->max_advance_width) + xShift, xScale); + StoreFloat(outWords, HORIZONTAL_MAX_ADVANCE_WIDTH, static_cast(maxAdvanceWidthPx) * ONE_OVER_64); + float caretRiseAdjust = 0.0f; + if (const auto* hhea = static_cast(FT_Get_Sfnt_Table(face, FT_SFNT_HHEA))) { + const std::int64_t caretRiseUnits = xShift + static_cast(hhea->caret_Slope_Rise); + const std::int32_t caretRisePx = TruncFixed(caretRiseUnits * xScale); + caretRiseAdjust = static_cast(caretRisePx - halfEffectWidthPx) * ONE_OVER_64; + } + StoreFloat(outWords, HORIZONTAL_CARET_RISE_ADJUST, caretRiseAdjust); + return SCE_FONT_OK; +} + +int APS5_VABI LibraryComputeLayoutAlt(FontObj* obj, const StyleStateBlock* style, std::uint8_t* outWords) { + if (!obj || !style || !outWords) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face || !face->size) return SCE_FONT_ERROR_FATAL; + const float effectWidth = style->effect_weight_x; + const float effectHeight = style->effect_weight_y; + const float slant = style->slant_ratio; + const auto xScale = static_cast(face->size->metrics.x_scale); + const auto yScale = static_cast(face->size->metrics.y_scale); + const auto* vhea = static_cast(FT_Get_Sfnt_Table(face, FT_SFNT_VHEA)); + const std::int64_t yShift = obj->shift_cache_y; + const std::int64_t xShift = obj->shift_cache_x; + const auto units = static_cast(face->units_per_EM); + std::int64_t yAscender = 0; + std::int64_t yDescender = 0; + if (vhea) { + yAscender = static_cast(vhea->Ascender) + yShift; + yDescender = static_cast(vhea->Descender) + yShift; + } + const std::int32_t scaledLeft = RoundMulFixed(static_cast(face->bbox.xMin) + xShift, xScale); + const std::int32_t scaledRight = RoundMulFixed(static_cast(face->bbox.xMax) + xShift, xScale); + std::int32_t xMinScaled = scaledLeft; + std::int32_t xAbsMax = -xMinScaled; + if (xAbsMax < scaledRight) { + xAbsMax = scaledRight; + xMinScaled = -scaledRight; + } + float outEffectWidth = 0.0f; + if (effectWidth != 0.0f) { + const auto unitsScaled = static_cast(TruncMulUnits(xScale, units)); + const std::int32_t effect = TruncateFloatToInt(effectWidth * static_cast(unitsScaled)) / 2; + outEffectWidth = static_cast(effect) * ONE_OVER_64; + } + if (effectHeight != 0.0f) { + const auto unitsScaled = static_cast(TruncMulUnits(yScale, units)); + const std::int32_t delta = TruncateFloatToInt(effectHeight * static_cast(unitsScaled)) / 2; + yAscender -= delta; + yDescender += delta; + } + float outSlantA = 0.0f; + float outSlantB = 0.0f; + if (slant != 0.0f) { + const auto shear = static_cast(TruncateFloatToInt(slant * 65536.0f)); + const std::int64_t signAdjustMin = yAscender < 1 ? 0x10000LL : 0LL; + std::int64_t roundedMin = signAdjustMin + (-yAscender) * shear - 0x8000LL; + if (roundedMin < 0) roundedMin = signAdjustMin + 0x7FFFLL + (-yAscender) * shear; + const std::int64_t signAdjustMax = yDescender >= 0 ? 0x10000LL : 0LL; + std::int64_t roundedMax = signAdjustMax + yDescender * shear - 0x8000LL; + if (roundedMax < 0) roundedMax = signAdjustMax + 0x7FFFLL + yDescender * shear; + const auto roundedMaxBits = static_cast(roundedMax); + const auto roundedMinBits = static_cast(roundedMin); + const std::uint64_t roundedMaxHigh = roundedMaxBits >> 16; + const bool maxRoundBitClear = ((roundedMaxBits >> 15) & 1u) == 0; + std::uint64_t selectedHigh = roundedMaxHigh; + auto selectedLow = static_cast(roundedMinBits >> 16); + if (maxRoundBitClear && roundedMaxHigh != 0) { + selectedHigh = roundedMinBits >> 16; + selectedLow = static_cast(roundedMaxBits >> 16); + } + outSlantA = static_cast(selectedLow) * ONE_OVER_64; + outSlantB = static_cast(static_cast(selectedHigh)) * ONE_OVER_64; + } + StoreFloat(outWords, VERTICAL_COLUMN_ADVANCE, static_cast(xAbsMax - xMinScaled) * ONE_OVER_64); + StoreFloat(outWords, VERTICAL_BASELINE_OFFSET_X, static_cast(-xAbsMax) * ONE_OVER_64); + StoreFloat(outWords, VERTICAL_METRICS_0X08, static_cast(RoundMulFixed(yAscender, xScale)) * ONE_OVER_64); + StoreFloat(outWords, VERTICAL_METRICS_0X0C, static_cast(RoundMulFixed(yDescender, xScale)) * ONE_OVER_64); + const std::int32_t advanceHeight = RoundMulFixed(static_cast(face->max_advance_height) + yShift, yScale); + StoreFloat(outWords, VERTICAL_ADVANCE_HEIGHT, static_cast(advanceHeight) * ONE_OVER_64); + StoreFloat(outWords, VERTICAL_DECORATION_SPAN, outEffectWidth); + StoreFloat(outWords, VERTICAL_DECORATION_0X08, outSlantB); + StoreFloat(outWords, VERTICAL_DECORATION_0X0C, outSlantA); + return SCE_FONT_OK; +} + +void StoreSeedVector(FontObj* obj) { + const auto seedLow = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const auto seedHigh = static_cast(static_cast(obj->layout_seed_pair >> 32)); + obj->layout_seed_vec[0] = static_cast(static_cast(seedLow)); + obj->layout_seed_vec[1] = static_cast(static_cast(seedHigh)); +} + +void WriteOutlineSizes(FT_GlyphSlot slot, std::uint64_t* outWords) { + const auto contours = static_cast(slot->outline.n_contours); + const auto points = static_cast(slot->outline.n_points); + const std::uint32_t size4 = points * 0x10u + 0x68u + ((points + 0x0Fu + contours * 2u) & 0xFFFFFFF0u); + const std::uint32_t size8 = ((points + 0x2Bu + contours * 2u) & 0xFFFFFFFCu) + 0x10u + points * 4u; + auto* out = reinterpret_cast(outWords); + WriteU16(out, 0, static_cast(contours)); + WriteU16(out, 2, static_cast(points)); + WriteU32(out, 4, size4); + WriteU32(out, 8, size8); +} + +int APS5_VABI LibraryLoadGlyphCached(FontObj* obj, std::uint32_t glyphIndex, std::int32_t mode, std::uint64_t* outWords) { + if (outWords) { + outWords[0] = 0; + outWords[1] = 0; + } + if (!obj || !outWords) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face || !face->glyph) return SCE_FONT_ERROR_FATAL; + const auto unitsPerEm = static_cast(face->units_per_EM); + const bool cachedMatch = obj->cached_glyph_index_0x64 == static_cast(glyphIndex) && obj->cached_units_x_0x68 == unitsPerEm && obj->cached_units_y_0x70 == obj->cached_units_x_0x68 && mode == 0; + if (cachedMatch) { + obj->shift_cache_x = obj->shift_units_x; + obj->shift_cache_y = obj->shift_units_y; + StoreSeedVector(obj); + WriteOutlineSizes(face->glyph, outWords); + return SCE_FONT_OK; + } + const FT_Int32 loadFlags = (mode == 0 ? FT_LOAD_NO_SCALE : 0) | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT; + const FT_Error error = FT_Load_Glyph(face, glyphIndex, loadFlags); + if (error != 0) { + obj->cached_glyph_index_0x64 = 0; + obj->cached_units_x_0x68 = 0; + obj->cached_units_y_0x70 = 0; + obj->layout_seed_vec = {}; + obj->layout_scale_vec = {}; + return error == FT_Err_Out_Of_Memory ? SCE_FONT_ERROR_ALLOCATION_FAILED : SCE_FONT_ERROR_NO_SUPPORT_GLYPH; + } + if (!face->glyph) return SCE_FONT_ERROR_FATAL; + obj->cached_glyph_index_0x64 = static_cast(glyphIndex); + if (mode == 0) { + obj->cached_units_x_0x68 = unitsPerEm; + obj->cached_units_y_0x70 = unitsPerEm; + obj->shift_cache_x = obj->shift_units_x; + obj->shift_cache_y = obj->shift_units_y; + StoreSeedVector(obj); + obj->layout_scale_vec[0] = 0x10000; + obj->layout_scale_vec[1] = 0x10000; + } else { + if (!face->size) return SCE_FONT_ERROR_FATAL; + const auto xScale = static_cast(face->size->metrics.x_scale); + const auto yScale = static_cast(face->size->metrics.y_scale); + obj->cached_units_x_0x68 = static_cast(TruncMulUnits(xScale, unitsPerEm)); + obj->cached_units_y_0x70 = static_cast(TruncMulUnits(yScale, unitsPerEm)); + obj->shift_cache_x = RoundMulFixed(obj->shift_units_x, xScale); + obj->shift_cache_y = RoundMulFixed(obj->shift_units_y, yScale); + const auto seedLow = static_cast(static_cast(obj->layout_seed_pair & 0xFFFFFFFFu)); + const auto seedHigh = static_cast(static_cast(obj->layout_seed_pair >> 32)); + obj->layout_seed_vec[0] = static_cast(static_cast(RoundMulFixed(seedLow, xScale))); + obj->layout_seed_vec[1] = static_cast(static_cast(RoundMulFixed(seedHigh, yScale))); + obj->layout_scale_vec[0] = static_cast(xScale); + obj->layout_scale_vec[1] = static_cast(yScale); + } + WriteOutlineSizes(face->glyph, outWords); + return SCE_FONT_OK; +} + +std::int64_t RoundMulFixed64(std::int64_t value, std::int64_t fixed16) { + return static_cast(RoundMulFixed(value, fixed16)); +} + +int APS5_VABI LibraryGetGlyphMetrics(FontObj* obj, std::uint32_t* optParam2, std::uint8_t mode, std::uint8_t* outParams, FontGlyphMetrics* outMetrics) { + if (!obj || !outParams || !outMetrics) return SCE_FONT_ERROR_FATAL; + const FT_Face face = FaceOf(obj); + if (!face || !face->glyph || !face->size) return SCE_FONT_ERROR_FATAL; + const FT_GlyphSlot slot = face->glyph; + std::memset(outParams, 0, 0x48); + const void* outlineBlob = &slot->outline; + outParams[0] = 0xF2; + WritePointer(outParams, 0x08, slot); + if (optParam2) { + outlineBlob = optParam2; + outParams[0] = 0xF0; + WritePointer(outParams, 0x08, optParam2); + } + const auto faceFlagBit = static_cast((static_cast(face->face_flags) >> 5) & 1u); + const bool hasSeedVector = obj->layout_seed_vec[0] != 0 || obj->layout_seed_vec[1] != 0; + outParams[2] = static_cast(0xF0u | (hasSeedVector ? 0x08u : 0u) | faceFlagBit); + outParams[3] = 0; + WriteU16(outParams, 0x04, static_cast(face->units_per_EM)); + WritePointer(outParams, 0x10, outlineBlob); + WritePointer(outParams, 0x18, outMetrics); + float styleShiftX = 0.0f; + float styleShiftY = 0.0f; + if (obj->reserved_0x04 != 0) { + styleShiftX = static_cast(slot->metrics.vertBearingX - slot->metrics.horiBearingX); + styleShiftY = static_cast(-(slot->metrics.horiBearingY + slot->metrics.vertBearingY)); + outParams[1] = 1; + } else { + outParams[1] = 0; + } + StoreFloat(outParams, 0x20, styleShiftX); + StoreFloat(outParams, 0x24, styleShiftY); + StoreFloat(outParams, 0x28, 1.0f); + const auto shiftX = static_cast(obj->shift_cache_x); + const auto shiftY = static_cast(obj->shift_cache_y); + const auto seedX = static_cast(obj->layout_seed_vec[0]); + const auto seedY = static_cast(obj->layout_seed_vec[1]); + WriteS32(outParams, 0x2C, shiftX); + WriteS32(outParams, 0x30, shiftY); + WriteS32(outParams, 0x34, seedX); + WriteS32(outParams, 0x38, seedY); + StoreFloat(outParams, 0x3C, static_cast(static_cast(obj->cached_units_x_0x68)) * ONE_OVER_64); + StoreFloat(outParams, 0x40, static_cast(static_cast(obj->cached_units_y_0x70)) * ONE_OVER_64); + WriteU32(outParams, 0x44, 0); + auto xScale = static_cast(face->size->metrics.x_scale); + auto yScale = static_cast(face->size->metrics.y_scale); + const auto objScaleX = static_cast(obj->layout_scale_vec[0]); + const auto objScaleY = static_cast(obj->layout_scale_vec[1]); + if (objScaleX != 0 && objScaleX != 0x10000) xScale = (xScale << 16) / objScaleX; + if (objScaleY != 0 && objScaleY != 0x10000) yScale = (yScale << 16) / objScaleY; + const bool applyScale = (mode & 0x0Fu) != 0; + const auto scaleIf = [&](std::int64_t value, std::int64_t scale) { + return applyScale ? RoundMulFixed64(value, scale) : value; + }; + const auto widthRaw = static_cast(slot->metrics.width); + const auto heightRaw = static_cast(slot->metrics.height); + const auto horiBearingXRaw = static_cast(slot->metrics.horiBearingX); + const auto horiAdvanceRaw = static_cast(slot->metrics.horiAdvance); + const auto vertBearingXRaw = static_cast(slot->metrics.vertBearingX); + const auto vertBearingYRaw = static_cast(slot->metrics.vertBearingY); + const auto vertAdvanceRaw = static_cast(slot->metrics.vertAdvance); + const std::int64_t horiAdvanceUnscaled = seedX + horiAdvanceRaw; + const std::int64_t vertAdvanceUnscaled = seedY + vertAdvanceRaw; + slot->metrics.horiAdvance = static_cast(horiAdvanceUnscaled); + slot->metrics.vertAdvance = static_cast(vertAdvanceUnscaled); + if (widthRaw == 0 && heightRaw == 0) { + *outMetrics = {}; + outMetrics->Horizontal.advance = FromF26Dot6(scaleIf(horiAdvanceUnscaled, xScale)); + outMetrics->Vertical.advance = FromF26Dot6(scaleIf(vertAdvanceUnscaled, yScale)); + return SCE_FONT_OK; + } + const std::int64_t topShift = heightRaw == 0 ? 0 : shiftY; + std::int64_t left = horiBearingXRaw + shiftX; + std::int64_t top = vertBearingYRaw + topShift; + std::int64_t right = left + widthRaw; + std::int64_t bottom = top - heightRaw; + left = scaleIf(left, xScale); + top = scaleIf(top, yScale); + right = scaleIf(right, xScale); + bottom = scaleIf(bottom, yScale); + const std::int64_t horiAdvance = scaleIf(horiAdvanceUnscaled, xScale); + const std::int64_t vertAdvance = scaleIf(vertAdvanceUnscaled, yScale); + const std::int64_t outWidth = right - left; + const std::int64_t outHeight = top - bottom; + std::int64_t vertBearingX; + std::int64_t vertBearingY; + if ((static_cast(face->face_flags) & 0x20u) == 0) { + vertBearingX = left - horiAdvance / 2; + vertBearingY = (vertAdvance - outHeight) / 2; + } else { + if (seedX == 0) { + vertBearingX = scaleIf(shiftX + vertBearingXRaw, xScale); + } else { + slot->metrics.vertBearingX = static_cast(horiBearingXRaw - horiAdvanceUnscaled / 2); + vertBearingX = left - horiAdvance / 2; + StoreFloat(outParams, 0x20, static_cast(-(horiAdvanceUnscaled / 2))); + } + vertBearingY = scaleIf(topShift + vertBearingYRaw, yScale); + } + outMetrics->width = FromF26Dot6(outWidth); + outMetrics->height = FromF26Dot6(outHeight); + outMetrics->Horizontal.bearingX = FromF26Dot6(left); + outMetrics->Horizontal.bearingY = FromF26Dot6(top); + outMetrics->Horizontal.advance = FromF26Dot6(horiAdvance); + outMetrics->Vertical.bearingX = FromF26Dot6(vertBearingX); + outMetrics->Vertical.bearingY = FromF26Dot6(vertBearingY); + outMetrics->Vertical.advance = FromF26Dot6(vertAdvance); + if ((mode & 0x06u) != 0 && obj->font_handle) { + const auto* handle = reinterpret_cast(obj->font_handle); + const StyleStateBlock* style = (mode & 0x04u) ? &handle->cached_style.state : &handle->style; + const float slantRatio = style->slant_ratio; + if ((slantRatio != 0.0f || std::isnan(slantRatio)) && slot->outline.points && slot->outline.n_points > 0) { + const auto shear = static_cast(TruncateFloatToInt(slantRatio * 65536.0f)); + std::int64_t minX = std::numeric_limits::max(); + std::int64_t maxX = std::numeric_limits::min(); + for (int i = 0; i < slot->outline.n_points; ++i) { + const FT_Vector point = slot->outline.points[i]; + const std::int64_t px = scaleIf(static_cast(point.x) + shiftX, xScale); + const std::int64_t py = scaleIf(static_cast(point.y) + shiftY, yScale); + const std::int64_t slantedX = px + RoundMulFixed64(py, shear); + minX = std::min(minX, slantedX); + maxX = std::max(maxX, slantedX); + } + if (minX <= maxX) { + outMetrics->width = FromF26Dot6(maxX - minX); + outMetrics->Horizontal.bearingX = FromF26Dot6(minX); + } + } + const float effectX = style->effect_weight_x; + const float effectY = style->effect_weight_y; + if (effectX != 0.0f || std::isnan(effectX) || effectY != 0.0f || std::isnan(effectY)) { + const float dx = effectX * obj->scale_x_0x50 * 0.5f; + const float dy = effectY * obj->scale_y_0x54 * 0.5f; + outMetrics->width = outMetrics->width + dx + dx; + outMetrics->height = outMetrics->height + dy + dy; + outMetrics->Horizontal.bearingX -= dx; + outMetrics->Horizontal.bearingY += dy; + outMetrics->Vertical.bearingX -= dx; + outMetrics->Vertical.bearingY -= dy; + if (outMetrics->Horizontal.advance != 0.0f || std::isnan(outMetrics->Horizontal.advance)) outMetrics->Horizontal.advance += dx; + if (outMetrics->Vertical.advance != 0.0f || std::isnan(outMetrics->Vertical.advance)) outMetrics->Vertical.advance += dy; + } + } + return SCE_FONT_OK; +} + +int APS5_VABI LibraryApplyGlyphAdjust(FontObj* obj, std::uint32_t p2, std::uint32_t glyphIndex, std::int32_t p4, std::int32_t p5, std::uint32_t* inoutGlyphIndex) { + (void)p2; + if (!obj || !inoutGlyphIndex) return SCE_FONT_ERROR_FATAL; + *inoutGlyphIndex = glyphIndex; + obj->shift_units_x = p4; + obj->shift_units_y = p5; + obj->layout_seed_pair = 0; + return SCE_FONT_OK; +} + +int APS5_VABI LibraryConfigureGlyph(FontObj* obj, std::uint32_t* inParams, std::int32_t mode, std::uint32_t* inoutState) { + if (!obj || !inParams || !inoutState) return SCE_FONT_ERROR_FATAL; + if (*inParams != 0) reinterpret_cast(inoutState)[3] |= 0x80; + obj->glyph_cfg_word_0x130 = *inParams; + obj->glyph_cfg_mode_0x134 = static_cast(mode); + obj->glyph_cfg_byte_0x136 = 0; + obj->glyph_cfg_byte_0x135 = 0; + return SCE_FONT_OK; +} + +int APS5_VABI RendererCreate(RendererNative* renderer) { + if (!renderer) return SCE_FONT_ERROR_INVALID_RENDERER; + auto* ft = reinterpret_cast(renderer); + ft->ft_backend.renderer_header_0x10 = &ft->base.mem_kind; + ft->ft_backend.unknown_0x08 = 0; + ft->ft_backend.unknown_0x10 = 0; + ft->ft_backend.unknown_0x18 = 0; + ft->ft_backend.initialized_marker = ft; + return SCE_FONT_OK; +} + +int APS5_VABI RendererDestroy(RendererNative* renderer) { + if (!renderer) return SCE_FONT_ERROR_INVALID_RENDERER; + reinterpret_cast(renderer)->ft_backend.initialized_marker = nullptr; + return SCE_FONT_OK; +} + +std::uint64_t APS5_VABI RendererQuery(RendererNative* renderer, std::uint8_t* params, std::int64_t* outPtr, std::uint8_t* outVector) { + (void)renderer; + (void)params; + if (outVector) std::memset(outVector, 0, 16); + if (outPtr) *outPtr = 0; + return 0; +} + +SysDriver MakeDriverTable() { + SysDriver driver{}; + driver.pixel_resolution = &LibraryGetPixelResolution; + driver.init = &LibraryInit; + driver.term = &LibraryTerm; + driver.support_formats = &LibrarySupport; + driver.open = &LibraryOpenFont; + driver.close = &LibraryCloseFont; + driver.scale = &LibraryGetFaceScale; + driver.metric = &LibraryGetFaceMetric; + driver.glyph_index = &LibraryGetGlyphIndex; + driver.set_char_with_dpi = &LibrarySetCharSizeWithDpi; + driver.set_char_default_dpi = &LibrarySetCharSizeDefaultDpi; + driver.compute_layout = &LibraryComputeLayout; + driver.compute_layout_alt = &LibraryComputeLayoutAlt; + driver.load_glyph_cached = &LibraryLoadGlyphCached; + driver.get_glyph_metrics = &LibraryGetGlyphMetrics; + driver.apply_glyph_adjust = &LibraryApplyGlyphAdjust; + driver.configure_glyph = &LibraryConfigureGlyph; + return driver; +} + +const SysDriver driverTable = MakeDriverTable(); +const RendererSelection rendererTable{0, sizeof(RendererFt), &RendererCreate, &RendererDestroy, &RendererQuery}; + +} + +const Font::SysDriver* FontFt::DriverTable() { + return &driverTable; +} + +const Font::RendererSelection* FontFt::RendererTable() { + return &rendererTable; +} diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp new file mode 100644 index 000000000..36d1fbf9c --- /dev/null +++ b/core/libs/tests/GuestFont.cpp @@ -0,0 +1,75 @@ +#include "prx/libSceFont/include/FontTypes.hpp" +#include +#include +#include + +extern "C" { +int APS5_VABI sceFontMemoryInit(FontMemory*, void*, std::uint32_t, const FontMemoryInterface*, void*, FontMemoryDestroyFunction, void*); +int APS5_VABI sceFontMemoryTerm(FontMemory*); +int APS5_VABI sceFontCreateLibrary(const FontMemory*, const void*, FontLibrary*); +int APS5_VABI sceFontDestroyLibrary(FontLibrary*); +int APS5_VABI sceFontCreateRenderer(const FontMemory*, const void*, FontRenderer*); +int APS5_VABI sceFontDestroyRenderer(FontRenderer*); +int APS5_VABI sceFontSupportSystemFonts(FontLibrary); +int APS5_VABI sceFontSupportExternalFonts(FontLibrary, std::uint32_t, std::uint32_t); +int APS5_VABI sceFontOpenFontSet(FontLibrary, std::uint32_t, std::uint32_t, const FontOpenDetail*, FontHandle*); +int APS5_VABI sceFontOpenFontMemory(FontLibrary, const void*, std::uint32_t, const FontOpenDetail*, FontHandle*); +const void* APS5_VABI sceFontSelectLibraryFt(int); +const void* APS5_VABI sceFontSelectRendererFt(int); +} + +static void Check(bool value, int line) { + if (!value) { + std::fprintf(stderr, "Font check failed at line %d\n", line); + std::abort(); + } +} +#define Require(value) Check((value), __LINE__) + +static int allocations = 0; +static void* APS5_VABI Allocate(void*, std::uint32_t size) { + ++allocations; + return std::malloc(size); +} +static void APS5_VABI Release(void*, void* pointer) { + if (pointer) --allocations; + std::free(pointer); +} + +int main() { + constexpr std::uint32_t SystemFontSet = 0x18070043u; + const FontMemoryInterface iface{Allocate, Release, nullptr, nullptr, nullptr, nullptr}; + FontMemory memory{}; + Require(sceFontMemoryInit(&memory, nullptr, 0, &iface, nullptr, nullptr, nullptr) == SCE_FONT_OK); + Require(sceFontSelectLibraryFt(0) != nullptr && sceFontSelectLibraryFt(1) == nullptr); + Require(sceFontSelectRendererFt(0) != nullptr && sceFontSelectRendererFt(1) == nullptr); + + FontLibrary library = nullptr; + Require(sceFontCreateLibrary(&memory, nullptr, &library) == SCE_FONT_ERROR_INVALID_PARAMETER && library == nullptr); + Require(sceFontCreateLibrary(&memory, sceFontSelectLibraryFt(0), &library) == SCE_FONT_OK && library != nullptr); + + FontHandle font = reinterpret_cast(&memory); + Require(sceFontOpenFontSet(library, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FUNCTION && font == nullptr); + Require(sceFontSupportSystemFonts(library) == SCE_FONT_OK); + Require(sceFontOpenFontSet(library, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_FONT_OPEN_FAILED && font == nullptr); + Require(sceFontOpenFontSet(library, 0x12345678u, 1, nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FONTSET); + Require(sceFontOpenFontSet(library, SystemFontSet, 7, nullptr, &font) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontOpenFontSet(nullptr, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_INVALID_LIBRARY); + const unsigned char notAFont[64] = {1, 2, 3, 4}; + Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FUNCTION && font == nullptr); + Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_OK); + Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_ERROR_ALREADY_SPECIFIED); + Require(sceFontOpenFontMemory(library, nullptr, 0, nullptr, &font) == SCE_FONT_ERROR_INVALID_PARAMETER && font == nullptr); + Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FORMAT && font == nullptr); + + FontRenderer renderer = nullptr; + Require(sceFontCreateRenderer(&memory, sceFontSelectRendererFt(0), &renderer) == SCE_FONT_OK && renderer != nullptr); + Require(sceFontDestroyRenderer(&renderer) == SCE_FONT_OK && renderer == nullptr); + Require(sceFontDestroyRenderer(&renderer) == SCE_FONT_ERROR_INVALID_RENDERER); + + Require(sceFontDestroyLibrary(&library) == SCE_FONT_OK && library == nullptr); + Require(sceFontDestroyLibrary(&library) == SCE_FONT_ERROR_INVALID_LIBRARY); + Require(allocations == 0); + Require(sceFontMemoryTerm(&memory) == SCE_FONT_OK); + Require(sceFontMemoryTerm(&memory) == SCE_FONT_ERROR_INVALID_MEMORY); +}