diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp index 41f19e62e..8164ebff9 100644 --- a/src/core/libraries/font/font.cpp +++ b/src/core/libraries/font/font.cpp @@ -2,11 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include #include -#include #include #include #include @@ -38,23 +38,21 @@ struct FontState { float scale_w = 16.0f; float scale_h = 16.0f; Libraries::Font::OrbisFontLib library = nullptr; - bool face_ready = false; - std::vector face_data; - stbtt_fontinfo face{}; - float scale_for_height = 0.0f; - int ascent = 0, descent = 0, lineGap = 0; bool ext_face_ready = false; std::vector ext_face_data; stbtt_fontinfo ext_face{}; float ext_scale_for_height = 0.0f; int ext_ascent = 0, ext_descent = 0, ext_lineGap = 0; std::unordered_map ext_cache; - std::unordered_map sys_cache; - std::unordered_map ext_kern_cache; - std::unordered_map sys_kern_cache; std::vector scratch; bool logged_ext_use = false; - bool logged_sys_use = false; + // Cached layout (PS4-style) for GetHorizontalLayout + float cached_baseline_y = 0.0f; + bool layout_cached = false; + // Renderer binding state (PS4 requires a bound renderer for render-scale queries) + Libraries::Font::OrbisFontRenderer bound_renderer = nullptr; + // Mark when the game asked for PS4 internal/system fonts (via OpenFontSet) + bool system_requested = false; }; static std::unordered_map g_font_state; @@ -83,55 +81,86 @@ static void LogExternalFormatSupport(u32 formats_mask) { LOG_INFO(Lib_Font, "ExternalFormatsMask=0x{:X}", formats_mask); } -static bool ReadFileBinary(const std::string& path, std::vector& out) { - std::ifstream f(path, std::ios::binary); - if (!f) - return false; - f.seekg(0, std::ios::end); - std::streampos sz = f.tellg(); - if (sz <= 0) - return false; - out.resize(static_cast(sz)); - f.seekg(0, std::ios::beg); - f.read(reinterpret_cast(out.data()), sz); - return f.good(); +enum class ScaleMode { + AscenderHeight, + EmSquare, +}; + +static ScaleMode GetScaleMode() { + static int inited = 0; + static ScaleMode mode = ScaleMode::AscenderHeight; + if (!inited) { + inited = 1; + if (const char* env = std::getenv("SHADPS4_FONT_SCALE_MODE")) { + std::string v = env; + for (auto& c : v) + c = static_cast(std::tolower(c)); + if (v == "em" || v == "emsquare" || v == "em_square") { + mode = ScaleMode::EmSquare; + } + } + LOG_INFO(Lib_Font, "FontScaleMode: {}", + mode == ScaleMode::EmSquare ? "em-square" : "ascender-height"); + } + return mode; } -static bool EnsureSystemFace(FontState& st) { - if (st.face_ready) - return true; - std::vector candidates; - if (const char* env_dir = std::getenv("SHADPS4_FONTS_DIR"); env_dir && *env_dir) { - candidates.emplace_back(std::string(env_dir) + "/NotoSansJP-Regular.ttf"); - candidates.emplace_back(std::string(env_dir) + "/ProggyVector-Regular.ttf"); - } - static const char* rel_roots[] = {".", "..", "../..", "../../..", "../../../.."}; - for (auto* root : rel_roots) { - candidates.emplace_back(std::string(root) + - "/src/imgui/renderer/fonts/NotoSansJP-Regular.ttf"); - candidates.emplace_back(std::string(root) + - "/src/imgui/renderer/fonts/ProggyVector-Regular.ttf"); - } - for (const auto& path : candidates) { - st.face_data.clear(); - if (ReadFileBinary(path, st.face_data)) { - if (stbtt_InitFont(&st.face, st.face_data.data(), 0)) { - st.face_ready = true; - stbtt_GetFontVMetrics(&st.face, &st.ascent, &st.descent, &st.lineGap); - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO(Lib_Font, - "SystemFace: loaded '{}' (ascent={}, descent={}, lineGap={}, scale={})", - path, st.ascent, st.descent, st.lineGap, st.scale_for_height); - return true; - } else { - LOG_WARNING(Lib_Font, "SystemFace: stbtt_InitFont failed for '{}'", path); +static float GetScaleBias() { + static int inited = 0; + static float bias = 1.0f; + if (!inited) { + inited = 1; + if (const char* env = std::getenv("SHADPS4_FONT_SCALE_BIAS")) { + try { + bias = std::max(0.01f, std::min(10.0f, std::stof(env))); + } catch (...) { + bias = 1.0f; } - } else { - LOG_DEBUG(Lib_Font, "SystemFace: could not open '{}'", path); + } + if (bias != 1.0f) { + LOG_INFO(Lib_Font, "FontScaleBias: {}", bias); } } - LOG_WARNING(Lib_Font, "SystemFace: no font file found; using placeholder rectangles"); + return bias; +} + +static float ComputeScale(const stbtt_fontinfo* face, float pixel_h) { + if (!face) + return 0.0f; + float s = 0.0f; + if (GetScaleMode() == ScaleMode::EmSquare) + s = stbtt_ScaleForMappingEmToPixels(face, pixel_h); + else + s = stbtt_ScaleForPixelHeight(face, pixel_h); + return s * GetScaleBias(); +} + +// External face scale: keep legacy ascender-height mapping to avoid regressions +// in titles that ship their own fonts (e.g., CUSA47038). No EM toggle, no bias. +static float ComputeScaleExt(const stbtt_fontinfo* face, float pixel_h) { + if (!face) + return 0.0f; + return stbtt_ScaleForPixelHeight(face, pixel_h); +} + +static void UpdateCachedLayout(FontState& st) { + if (!st.ext_face_ready) { + return; + } + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + const float scale = st.ext_scale_for_height; + st.cached_baseline_y = static_cast(asc) * scale; + st.layout_cached = true; +} + +static bool ReportSystemFaceRequest(FontState& st) { + if (!st.system_requested) { + st.system_requested = true; + LOG_ERROR(Lib_Font, "SystemFace: internal PS4 font requested but not available"); + } return false; } @@ -170,8 +199,8 @@ s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(OrbisFontLib library, void* buff } s32 PS4_SYSV_ABI sceFontBindRenderer(OrbisFontHandle fontHandle, OrbisFontRenderer renderer) { - LOG_DEBUG(Lib_Font, "sceFontBindRenderer fontHandle={} renderer={}", - static_cast(fontHandle), static_cast(renderer)); + auto& st = GetState(fontHandle); + st.bound_renderer = renderer; return ORBIS_OK; } @@ -421,17 +450,14 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code auto& st = GetState(fontHandle); const stbtt_fontinfo* face = nullptr; float scale = 0.0f; - bool use_ext = false; - int ext_glyph = 0; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (ext_glyph > 0) { + const int glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (glyph > 0) { face = &st.ext_face; scale = st.ext_scale_for_height; - use_ext = true; if (!st.logged_ext_use) { LOG_INFO(Lib_Font, "RenderFace: handle={} source=external (game font)", static_cast(fontHandle)); @@ -439,33 +465,16 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code } } } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (sys_glyph > 0) { - face = &st.face; - scale = st.scale_for_height; - use_ext = false; - if (!st.logged_sys_use) { - LOG_INFO(Lib_Font, "RenderFace: handle={} source=system (fallback)", - static_cast(fontHandle)); - st.logged_sys_use = true; - } - } - } + + if (!face) + ReportSystemFaceRequest(st); + if (face) { const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); GlyphEntry* ge = nullptr; - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } if (!ge) { GlyphEntry entry{}; @@ -477,14 +486,10 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code entry.h = std::max(0, entry.y1 - entry.y0); entry.advance = static_cast(aw) * scale; entry.bearingX = static_cast(lsb) * scale; - if (use_ext) { - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } else { - ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; - } + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; } - metrics->w = ge->w > 0 ? (float)ge->w : st.scale_w; - metrics->h = ge->h > 0 ? (float)ge->h : st.scale_h; + metrics->w = ge->w > 0 ? static_cast(ge->w) : st.scale_w; + metrics->h = ge->h > 0 ? static_cast(ge->h) : st.scale_h; metrics->h_bearing_x = ge->bearingX; metrics->h_bearing_y = static_cast(-ge->y0); metrics->h_adv = ge->advance > 0.0f ? ge->advance : st.scale_w; @@ -492,10 +497,10 @@ s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(OrbisFontHandle fontHandle, u32 code metrics->v_bearing_y = 0.0f; metrics->v_adv = 0.0f; LOG_TRACE(Lib_Font, - "GetCharGlyphMetrics: code=U+{:04X} src={} size=({}, {}) adv={} bearing=({}, {}) " - "box={}x{}", - code, use_ext ? "external" : "system", st.scale_w, st.scale_h, metrics->h_adv, - metrics->h_bearing_x, metrics->h_bearing_y, metrics->w, metrics->h); + "GetCharGlyphMetrics: code=U+{:04X} src=external size=({}, {}) adv={} " + "bearing=({}, {}) box={}x{}", + code, st.scale_w, st.scale_h, metrics->h_adv, metrics->h_bearing_x, + metrics->h_bearing_y, metrics->w, metrics->h); return ORBIS_OK; } metrics->w = st.scale_w; @@ -551,8 +556,37 @@ s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { - LOG_DEBUG(Lib_Font, "GetHorizontalLayout: default layout (no effects)"); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, + OrbisFontHorizontalLayout* layout) { + if (!layout) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + const float scale = st.ext_scale_for_height; + layout->baselineOffset = static_cast(asc) * scale; + layout->lineAdvance = static_cast(asc - desc + gap) * scale; + layout->decorationExtent = 0.0f; + LOG_INFO( + Lib_Font, + "GetHorizontalLayout: handle={} ext_ready={} baselineOffset={} lineAdvance={} scale={}", + static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, + layout->lineAdvance, scale); + return ORBIS_OK; + } + ReportSystemFaceRequest(st); + layout->baselineOffset = st.scale_h * 0.8f; + layout->lineAdvance = st.scale_h; + layout->decorationExtent = 0.0f; + LOG_INFO( + Lib_Font, + "GetHorizontalLayout: fallback handle={} ext_ready={} baselineOffset={} lineAdvance={}", + static_cast(fontHandle), st.ext_face_ready, layout->baselineOffset, + layout->lineAdvance); return ORBIS_OK; } @@ -563,39 +597,24 @@ s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 return ORBIS_FONT_ERROR_INVALID_PARAMETER; } auto& st = GetState(fontHandle); - const stbtt_fontinfo* face = nullptr; - float scale = 0.0f; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); int g1 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(preCode)); int g2 = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); if (g1 > 0 && g2 > 0) { - face = &st.ext_face; - scale = st.ext_scale_for_height; + const int kern = stbtt_GetCodepointKernAdvance(&st.ext_face, static_cast(preCode), + static_cast(code)); + const float kx = static_cast(kern) * st.ext_scale_for_height; + kerning->dx = kx; + kerning->dy = 0.0f; + kerning->px = 0.0f; + kerning->py = 0.0f; + LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); + return ORBIS_OK; } } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int g1 = stbtt_FindGlyphIndex(&st.face, static_cast(preCode)); - int g2 = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (g1 > 0 && g2 > 0) { - face = &st.face; - scale = st.scale_for_height; - } - } - if (face) { - const int kern = - stbtt_GetCodepointKernAdvance(face, static_cast(preCode), static_cast(code)); - const float kx = static_cast(kern) * scale; - kerning->dx = kx; - kerning->dy = 0.0f; - kerning->px = 0.0f; - kerning->py = 0.0f; - LOG_TRACE(Lib_Font, "GetKerning: pre=U+{:04X} code=U+{:04X} dx={}", preCode, code, kx); - return ORBIS_OK; - } + ReportSystemFaceRequest(st); kerning->dx = 0.0f; kerning->dy = 0.0f; kerning->px = 0.0f; @@ -638,14 +657,25 @@ s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h) { + auto& st = GetState(fontHandle); + if (w) + *w = st.scale_w; + if (h) + *h = st.scale_h; + if (!st.bound_renderer) { + LOG_DEBUG(Lib_Font, + "GetRenderScalePixel: no renderer bound; returning configured scale w={} h={}", + w ? *w : -1.0f, h ? *h : -1.0f); + } else { + LOG_DEBUG(Lib_Font, "GetRenderScalePixel: handle={} -> w={}, h={}", + static_cast(fontHandle), w ? *w : -1.0f, h ? *h : -1.0f); + } return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h) { + return sceFontGetRenderScalePixel(fontHandle, w, h); } s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { @@ -681,8 +711,26 @@ s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFontGetVerticalLayout() { - LOG_ERROR(Lib_Font, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, + OrbisFontVerticalLayout* layout) { + if (!layout) { + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + auto& st = GetState(fontHandle); + if (st.ext_face_ready) { + if (st.ext_scale_for_height == 0.0f) + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + int asc = 0, desc = 0, gap = 0; + stbtt_GetFontVMetrics(&st.ext_face, &asc, &desc, &gap); + layout->baselineOffsetX = 0.0f; + layout->columnAdvance = static_cast(asc - desc + gap) * st.ext_scale_for_height; + layout->decorationSpan = 0.0f; + return ORBIS_OK; + } + ReportSystemFaceRequest(st); + layout->baselineOffsetX = 0.0f; + layout->columnAdvance = st.scale_h; + layout->decorationSpan = 0.0f; return ORBIS_OK; } @@ -1106,7 +1154,7 @@ s32 PS4_SYSV_ABI sceFontOpenFontMemory(OrbisFontLib library, const void* fontAdd (fontSize >= 3 ? (char)p[2] : '?'), (fontSize >= 4 ? (char)p[3] : '?'), is_otf_cff); if (is_otf_cff) { - LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; fallback to system font"); + LOG_WARNING(Lib_Font, "Stubbed: CFF outlines not implemented; system font unavailable"); } } return ORBIS_OK; @@ -1125,15 +1173,14 @@ s32 PS4_SYSV_ABI sceFontOpenFontSet(OrbisFontLib library, u32 fontSetType, u32 o *pFontHandle = f; auto& st = GetState(f); st.library = library; - EnsureSystemFace(st); - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO( - Lib_Font, - "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system face={})", - static_cast(library), fontSetType, openMode, - static_cast(open_params), static_cast(*pFontHandle), - EnsureSystemFace(GetState(f))); + const bool system_ok = ReportSystemFaceRequest(st); + + LOG_INFO(Lib_Font, + "OpenFontSet: lib={} fontSetType={} openMode={} open_params={} handle={} (system " + "available={})", + static_cast(library), fontSetType, openMode, + static_cast(open_params), static_cast(*pFontHandle), + system_ok); return ORBIS_OK; } @@ -1185,47 +1232,33 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl int g_x0 = 0, g_y0 = 0, g_x1 = 0, g_y1 = 0; const stbtt_fontinfo* face = nullptr; float scale = 0.0f; - bool use_ext = false; - int ext_glyph = 0; if (st.ext_face_ready) { if (st.ext_scale_for_height == 0.0f) st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - ext_glyph = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); - if (ext_glyph > 0) { + const int glyph_index = stbtt_FindGlyphIndex(&st.ext_face, static_cast(code)); + if (glyph_index > 0) { face = &st.ext_face; scale = st.ext_scale_for_height; - use_ext = true; - } - } - if (!face && EnsureSystemFace(st)) { - if (st.scale_for_height == 0.0f) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - int sys_glyph = stbtt_FindGlyphIndex(&st.face, static_cast(code)); - if (sys_glyph > 0) { - face = &st.face; - scale = st.scale_for_height; - use_ext = false; } } + + if (!face) + ReportSystemFaceRequest(st); + + const float frac_x = x - std::floor(x); + const float frac_y = y - std::floor(y); + const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + if (face) { - LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src={}", - static_cast(fontHandle), code, use_ext ? "external" : "system"); - const float frac_x = x - std::floor(x); - const float frac_y = y - std::floor(y); - const bool use_subpixel = (frac_x != 0.0f) || (frac_y != 0.0f); + LOG_DEBUG(Lib_Font, "RenderGlyphSrc(H): handle={} code=U+{:04X} src=external", + static_cast(fontHandle), code); const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); GlyphEntry* ge = nullptr; if (!use_subpixel) { - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } } if (!ge) { @@ -1248,16 +1281,10 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl entry.bitmap.resize(static_cast(entry.w * entry.h)); stbtt_MakeCodepointBitmap(face, entry.bitmap.data(), entry.w, entry.h, entry.w, scale, scale, static_cast(code)); - } - if (!use_subpixel) { - if (use_ext) { - ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; - } else { - ge = &st.sys_cache.emplace(key, std::move(entry)).first->second; - } - } else { - fw = (float)entry.w; - fh = (float)entry.h; + ge = &st.ext_cache.emplace(key, std::move(entry)).first->second; + } else if (use_subpixel) { + fw = static_cast(entry.w); + fh = static_cast(entry.h); g_x0 = entry.x0; g_y0 = entry.y0; g_x1 = entry.x1; @@ -1265,8 +1292,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl } } if (ge) { - fw = (float)ge->w; - fh = (float)ge->h; + fw = static_cast(ge->w); + fh = static_cast(ge->h); g_x0 = ge->x0; g_y0 = ge->y0; g_x1 = ge->x1; @@ -1305,8 +1332,22 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl return ORBIS_OK; } - int ix = static_cast(std::floor(x)); - int iy = static_cast(std::floor(y)); + if (!st.layout_cached) { + UpdateCachedLayout(st); + } + float layout_baseline = st.layout_cached ? st.cached_baseline_y : 0.0f; + + float adjusted_y = y; + if (face && layout_baseline > 0.0f) { + const float top_expect = adjusted_y + static_cast(g_y0); + const float bottom_expect = top_expect + fh; + if (bottom_expect <= 0.0f || top_expect < 0.0f) { + adjusted_y += layout_baseline; + } + } + + int ix = static_cast(x); + int iy = static_cast(adjusted_y); int left = ix; int top = iy; if (face) { @@ -1346,6 +1387,12 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl int cw = std::max(0, rx1 - rx0); int ch = std::max(0, ry1 - ry0); + if (ch == 0 && face) { + LOG_DEBUG(Lib_Font, + "RenderGlyph(H): zero-height glyph code=U+{:04X} top={} ih={} g_y0={} g_y1={}" + " y={}, g_adv={} scale={} src=external", + code, top, ih, g_y0, g_y1, y, metrics ? metrics->h_adv : -1.0f, scale); + } if (cw > 0 && ch > 0) { const int bpp = std::max(1, static_cast(surf->pixelSizeByte)); @@ -1362,14 +1409,8 @@ s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(OrbisFontHandle fontHandl const int pixel_h = std::max(1, (int)std::lround(st.scale_h)); const std::uint64_t key = MakeGlyphKey(code, pixel_h); const GlyphEntry* ge = nullptr; - if (use_ext) { - auto it = st.ext_cache.find(key); - if (it != st.ext_cache.end()) - ge = &it->second; - } else { - auto it = st.sys_cache.find(key); - if (it != st.sys_cache.end()) - ge = &it->second; + if (auto it = st.ext_cache.find(key); it != st.ext_cache.end()) { + ge = &it->second; } const float frac_x = x - std::floor(x); const float frac_y = y - std::floor(y); @@ -1614,16 +1655,13 @@ s32 PS4_SYSV_ABI sceFontSetScalePixel(OrbisFontHandle fontHandle, float w, float auto& st = GetState(fontHandle); st.scale_w = w; st.scale_h = h; - const bool sys_ok = EnsureSystemFace(st); if (st.ext_face_ready) - st.ext_scale_for_height = stbtt_ScaleForPixelHeight(&st.ext_face, st.scale_h); - if (sys_ok) - st.scale_for_height = stbtt_ScaleForPixelHeight(&st.face, st.scale_h); - LOG_INFO( - Lib_Font, - "SetScalePixel: handle={} w={} h={} ext_scale={} sys_scale={} ext_ready={} sys_ready={}", - static_cast(fontHandle), w, h, st.ext_scale_for_height, st.scale_for_height, - st.ext_face_ready, sys_ok); + st.ext_scale_for_height = ComputeScaleExt(&st.ext_face, st.scale_h); + ReportSystemFaceRequest(st); + LOG_INFO(Lib_Font, "SetScalePixel: handle={} w={} h={} ext_scale={} ext_ready={}", + static_cast(fontHandle), w, h, st.ext_scale_for_height, + st.ext_face_ready); + st.layout_cached = false; return ORBIS_OK; } diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h index 11da25331..503663cb8 100644 --- a/src/core/libraries/font/font.h +++ b/src/core/libraries/font/font.h @@ -1,24 +1,19 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #pragma once - #include #include "common/types.h" - namespace Core::Loader { class SymbolsResolver; } - namespace Libraries::Font { -using OrbisFontLib = void*; - struct OrbisFontHandleOpaque { u32 reserved[64]; }; -using OrbisFontHandle = OrbisFontHandleOpaque*; +using OrbisFontLib = void*; +using OrbisFontHandle = OrbisFontHandleOpaque*; using OrbisFontRendererCreateParams = void*; using OrbisFontRenderer = void*; using OrbisFontLibCreateParams = void*; @@ -32,9 +27,6 @@ struct OrbisFontOpenParams { const void* reserved_ptr2; const void* reserved_ptr1; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); -#endif struct OrbisFontGlyphMetrics { float w; @@ -46,9 +38,6 @@ struct OrbisFontGlyphMetrics { float v_bearing_y; float v_adv; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); -#endif struct OrbisFontKerning { float dx; @@ -56,9 +45,6 @@ struct OrbisFontKerning { float px; float py; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); -#endif struct OrbisFontGlyphImageMetrics { float bearing_x; @@ -162,10 +148,6 @@ struct OrbisFontRenderSurface { u64 reserved_q[11]; }; -#if INTPTR_MAX == INT64_MAX -static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); -#endif - struct OrbisFontStyleFrame { /*0x00*/ u16 magic; // Expected to be 0xF09 /*0x02*/ u16 flags; @@ -180,7 +162,23 @@ struct OrbisFontStyleFrame { /*0x24*/ }; + +struct OrbisFontHorizontalLayout { + float baselineOffset; + float lineAdvance; + float decorationExtent; +}; +struct OrbisFontVerticalLayout { + float baselineOffsetX; + float columnAdvance; + float decorationSpan; +}; + #if INTPTR_MAX == INT64_MAX +static_assert(sizeof(OrbisFontOpenParams) == 32, "OrbisFontOpenParams size"); +static_assert(sizeof(OrbisFontGlyphMetrics) == 32, "OrbisFontGlyphMetrics size"); +static_assert(sizeof(OrbisFontKerning) == 16, "OrbisFontKerning size"); +static_assert(sizeof(OrbisFontRenderSurface) == 128, "OrbisFontRenderSurface ABI size"); static_assert(sizeof(OrbisFontGlyphImageMetrics) == 24, "OrbisFontGlyphImageMetrics ABI size"); static_assert(sizeof(OrbisFontRenderOutput) == 64, "OrbisFontRenderOutput ABI size"); static_assert(sizeof(OrbisFontMem) == 64, "OrbisFontMem ABI size"); @@ -241,7 +239,8 @@ s32 PS4_SYSV_ABI sceFontGetFontMetrics(); s32 PS4_SYSV_ABI sceFontGetFontResolution(); s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); -s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(OrbisFontHandle fontHandle, + OrbisFontHorizontalLayout* layout); s32 PS4_SYSV_ABI sceFontGetKerning(OrbisFontHandle fontHandle, u32 preCode, u32 code, OrbisFontKerning* kerning); s32 PS4_SYSV_ABI sceFontGetLibrary(OrbisFontHandle fontHandle, OrbisFontLib* pLibrary); @@ -250,14 +249,15 @@ s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(); -s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(OrbisFontHandle fontHandle, float* w, float* h); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(OrbisFontHandle fontHandle, float* w, float* h); s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); s32 PS4_SYSV_ABI sceFontGetScalePixel(OrbisFontHandle fontHandle, float* w, float* h); s32 PS4_SYSV_ABI sceFontGetScalePoint(); s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); -s32 PS4_SYSV_ABI sceFontGetVerticalLayout(); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(OrbisFontHandle fontHandle, + OrbisFontVerticalLayout* layout); s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm();