diff --git a/core/libs/prx/libSceFont/include/FontInternal.hpp b/core/libs/prx/libSceFont/include/FontInternal.hpp index da4a70b28a..a4ff9e01ef 100644 --- a/core/libs/prx/libSceFont/include/FontInternal.hpp +++ b/core/libs/prx/libSceFont/include/FontInternal.hpp @@ -121,6 +121,9 @@ 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); +const FontStyleFrame* SurfaceStyleFrame(const FontRenderSurface* surface); +const StyleStateBlock* ApplyStyleFrame(const StyleStateBlock* base, const FontStyleFrame* frame, StyleStateBlock& temporary); +int RenderCodeWithStyle(FontHandle handle, std::uint32_t code, const StyleStateBlock* style, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result); std::uint8_t CachedStyleCacheFlags(const CachedStyle& style); void CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags); diff --git a/core/libs/prx/libSceFont/src/Layout.cpp b/core/libs/prx/libSceFont/src/Layout.cpp index d297840675..d52a854c55 100644 --- a/core/libs/prx/libSceFont/src/Layout.cpp +++ b/core/libs/prx/libSceFont/src/Layout.cpp @@ -68,28 +68,7 @@ void ClearVerticalLayout(std::uint8_t* outWords) { } 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; + return ApplyStyleFrame(cachedState, SurfaceStyleFrame(surface), temporary); } void FillImageMetrics(FontRenderOutput* result, const FontGlyphMetrics* metrics, float x, float y) { @@ -440,6 +419,35 @@ int Font::GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMe return finish(SCE_FONT_OK); } +const FontStyleFrame* Font::SurfaceStyleFrame(const FontRenderSurface* surface) { + const auto* systemUse = reinterpret_cast(surface->reserved_q); + if ((surface->styleFlag & 0x1) == 0 || !ValidStyleFrame(systemUse->styleframe)) return nullptr; + return systemUse->styleframe; +} + +const StyleStateBlock* Font::ApplyStyleFrame(const StyleStateBlock* base, const FontStyleFrame* frame, StyleStateBlock& temporary) { + if (!frame) return base; + temporary = *base; + 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; +} + 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); @@ -451,13 +459,21 @@ int Font::RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRe 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); + StyleStateBlock surfaceStyle{}; + return RenderCodeWithStyle(handle, code, ResolveSurfaceStyleState(&font->cached_style.state, surface, surfaceStyle), surface, x, y, metrics, result); +} + +int Font::RenderCodeWithStyle(FontHandle handle, std::uint32_t code, const StyleStateBlock* style, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) { + const auto fail = [&](int rc) { + ClearRenderOutputs(metrics, result); + return rc; + }; + auto* font = GetNativeFont(handle); 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(FontContext(library, font), font->open_info.ctx_entry_index, modeLow, &obj, &lockWord); diff --git a/core/libs/prx/libSceFont/src/Render.cpp b/core/libs/prx/libSceFont/src/Render.cpp index 0647f2192e..bdc370105e 100644 --- a/core/libs/prx/libSceFont/src/Render.cpp +++ b/core/libs/prx/libSceFont/src/Render.cpp @@ -13,6 +13,7 @@ #include FT_FREETYPE_H #include "prx/libSceFont/include/FontInternal.hpp" +#include "prx/libc/include/General.hpp" namespace { @@ -121,6 +122,45 @@ int RenderDirectional(FontHandle fontHandle, std::uint32_t code, FontRenderSurfa return rc; } +int RenderGlyph(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result, std::uint16_t direction) { + const auto fail = [&](int rc) { + ClearRenderOutputs(metrics, result); + return rc; + }; + auto* generated = TryGetGeneratedGlyph(glyph); + if (!generated) return fail(SCE_FONT_ERROR_INVALID_GLYPH); + if (!renderer || static_cast(renderer)->magic != RENDERER_MAGIC) return fail(SCE_FONT_ERROR_INVALID_RENDERER); + if (!surface || !metrics || !result) return fail(SCE_FONT_ERROR_INVALID_PARAMETER); + if (direction == 2) NotImplemented_nid_no_patch("sceFontGlyphRenderImageVertical"); + const FontHandle owner = generated->owner; + auto* font = TryGetState(owner) ? GetNativeFont(owner) : nullptr; + std::uint32_t fontLock = 0; + if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE); + StyleStateBlock glyphStyle{}; + glyphStyle.scale_w = generated->glyph.scale_x; + glyphStyle.scale_h = generated->glyph.base_scale; + StyleStateBlock framed{}; + const FontStyleFrame* surfaceFrame = SurfaceStyleFrame(surface); + const StyleStateBlock* style = ApplyStyleFrame(&glyphStyle, surfaceFrame ? surfaceFrame : styleFrame, framed); + float yUsed = y; + int rc = SCE_FONT_OK; + if (direction == 0) { + if (static_cast(font->flags) < 0) { + ReleaseFontLock(font, fontLock); + NotImplemented_nid_no_patch("sceFontGlyphRenderImage: a vertical font"); + } + std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {}; + rc = ComputeHorizontalLayout(owner, style, layout); + yUsed = y + LoadFloat(layout, HORIZONTAL_BASELINE); + direction = 1; + } + CachedStyleSetDirectionWord(font->cached_style, direction); + if (rc == SCE_FONT_OK) rc = RenderCodeWithStyle(owner, generated->codepoint, style, surface, x, yUsed, metrics, result); + ReleaseFontLock(font, fontLock); + if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result); + return rc; +} + } #pragma GCC visibility push(default) @@ -559,6 +599,18 @@ FontGlyphOutline* APS5_VABI sceFontGlyphRefersOutline(FontGlyph glyph) { return &generated->outline; } +int APS5_VABI sceFontGlyphRenderImage(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { + return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 0); +} + +int APS5_VABI sceFontGlyphRenderImageHorizontal(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { + return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 1); +} + +int APS5_VABI sceFontGlyphRenderImageVertical(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { + return RenderGlyph(glyph, styleFrame, renderer, surface, x, y, metrics, output, 2); +} + } #pragma GCC visibility pop diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 2ff7bd7da3..fa5b689e1e 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -106,21 +106,6 @@ int APS5_VABI sceFontGetGlyphExpandBufferState(FontHandle font, FontGlyphExpandB return 0; } -int APS5_VABI sceFontGlyphRenderImage(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGlyphRenderImageHorizontal(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceFontGlyphRenderImageVertical(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceFontGraphicsBeginFrame(FontGraphicsFrame* frame, std::uint64_t flags, FontGraphicsResource** resource) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/tests/GuestSystemFont.cpp b/core/libs/tests/GuestSystemFont.cpp index 643b4761e0..f4b5946c3c 100644 --- a/core/libs/tests/GuestSystemFont.cpp +++ b/core/libs/tests/GuestSystemFont.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,120 @@ static int RenderedPixels(FontHandle font, std::uint32_t code, float& advance) { return lit; } +struct GlyphImage { + int rc = SCE_FONT_OK; + std::vector pixels = std::vector(128 * 128); + FontGlyphMetrics metrics{}; + FontRenderOutput result{}; +}; + +template static GlyphImage RenderInto(TRender render) { + GlyphImage image; + FontRenderSurface surface{}; + sceFontRenderSurfaceInit(&surface, image.pixels.data(), 128, 1, 128, 128); + image.rc = render(&surface, &image.metrics, &image.result); + return image; +} + +static bool SurfaceOffset(const GlyphImage& image, std::uintptr_t& offset) { + const auto& surface = image.result.SurfaceImage; + const auto& rect = image.result.UpdateRect; + const auto base = reinterpret_cast(image.pixels.data()); + const auto address = reinterpret_cast(surface.address); + const std::uintptr_t span = rect.w == 0 || rect.h == 0 ? 0 : static_cast(rect.h - 1) * surface.widthByte + static_cast(rect.w) * surface.pixelSizeByte; + if (address < base || address - base > image.pixels.size() || span > image.pixels.size() - (address - base)) return false; + offset = address - base; + return true; +} + +static bool SameImage(const GlyphImage& left, const GlyphImage& right) { + int lit = 0; + for (const std::uint8_t pixel : left.pixels) lit += pixel != 0 ? 1 : 0; + const auto& a = left.result; + const auto& b = right.result; + std::uintptr_t leftOffset = 0; + std::uintptr_t rightOffset = 0; + const bool sameSurface = SurfaceOffset(left, leftOffset) && SurfaceOffset(right, rightOffset) && leftOffset == rightOffset && a.SurfaceImage.widthByte == b.SurfaceImage.widthByte && a.SurfaceImage.pixelSizeByte == b.SurfaceImage.pixelSizeByte && a.SurfaceImage.pixelFormat == b.SurfaceImage.pixelFormat; + const bool sameOutput = a.stage == b.stage && sameSurface && std::memcmp(&a.UpdateRect, &b.UpdateRect, sizeof(a.UpdateRect)) == 0 && std::memcmp(&a.ImageMetrics, &b.ImageMetrics, sizeof(a.ImageMetrics)) == 0; + return left.rc == SCE_FONT_OK && right.rc == SCE_FONT_OK && lit > 0 && left.pixels == right.pixels && std::memcmp(&left.metrics, &right.metrics, sizeof(left.metrics)) == 0 && sameOutput; +} + +static bool Cleared(const GlyphImage& image, int rc) { + const FontGlyphMetrics noMetrics{}; + const FontRenderOutput noResult{}; + return image.rc == rc && std::memcmp(&image.metrics, &noMetrics, sizeof(noMetrics)) == 0 && std::memcmp(&image.result, &noResult, sizeof(noResult)) == 0; +} + +static void CheckGlyphImages(FontHandle font, FontRenderer renderer) { + Require(sceFontBindRenderer(font, renderer) == SCE_FONT_OK); + Require(sceFontSetScalePixel(font, 40.0f, 40.0f) == SCE_FONT_OK); + Require(sceFontSetupRenderScalePixel(font, 40.0f, 40.0f) == SCE_FONT_OK); + FontGlyph glyph = nullptr; + Require(sceFontGenerateCharGlyph(font, 'A', nullptr, &glyph) == SCE_FONT_OK && glyph != nullptr); + + const auto horizontal = RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontRenderCharGlyphImageHorizontal(font, 'A', surface, 10.0f, 100.0f, metrics, result); }); + Require(SameImage(horizontal, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImageHorizontal(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); }))); + const auto written = RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontRenderCharGlyphImage(font, 'A', surface, 10.0f, 20.0f, metrics, result); }); + Require(SameImage(written, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImage(glyph, nullptr, renderer, surface, 10.0f, 20.0f, metrics, result); }))); + + Require(sceFontSetupRenderScalePixel(font, 20.0f, 20.0f) == SCE_FONT_OK); + Require(sceFontSetScalePixel(font, 20.0f, 20.0f) == SCE_FONT_OK); + const auto smaller = RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontRenderCharGlyphImageHorizontal(font, 'A', surface, 10.0f, 100.0f, metrics, result); }); + Require(smaller.rc == SCE_FONT_OK && smaller.pixels != horizontal.pixels); + Require(SameImage(horizontal, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImageHorizontal(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); }))); + FontStyleFrame frame{}; + Require(sceFontStyleFrameInit(&frame) == SCE_FONT_OK && sceFontStyleFrameSetScalePixel(&frame, 20.0f, 20.0f) == SCE_FONT_OK); + Require(SameImage(smaller, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImageHorizontal(glyph, &frame, renderer, surface, 10.0f, 100.0f, metrics, result); }))); + Require(SameImage(smaller, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { + Require(sceFontRenderSurfaceSetStyleFrame(surface, &frame) == SCE_FONT_OK); + return sceFontGlyphRenderImageHorizontal(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); + }))); + FontStyleFrame larger{}; + Require(sceFontStyleFrameInit(&larger) == SCE_FONT_OK && sceFontStyleFrameSetScalePixel(&larger, 30.0f, 30.0f) == SCE_FONT_OK); + const auto surfaceLarger = RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { + Require(sceFontRenderSurfaceSetStyleFrame(surface, &larger) == SCE_FONT_OK); + return sceFontGlyphRenderImageHorizontal(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); + }); + Require(surfaceLarger.rc == SCE_FONT_OK && surfaceLarger.pixels != smaller.pixels && surfaceLarger.pixels != horizontal.pixels); + Require(SameImage(surfaceLarger, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { + Require(sceFontRenderSurfaceSetStyleFrame(surface, &larger) == SCE_FONT_OK); + return sceFontGlyphRenderImageHorizontal(glyph, &frame, renderer, surface, 10.0f, 100.0f, metrics, result); + }))); + Require(SameImage(smaller, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { + Require(sceFontRenderSurfaceSetStyleFrame(surface, &frame) == SCE_FONT_OK); + return sceFontGlyphRenderImageHorizontal(glyph, &larger, renderer, surface, 10.0f, 100.0f, metrics, result); + }))); + FontStyleFrame notAFrame{}; + FontStyleFrame initialized{}; + Require(sceFontStyleFrameInit(&initialized) == SCE_FONT_OK); + for (const FontStyleFrame* unscaled : {¬AFrame, &initialized}) { + Require(SameImage(horizontal, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImageHorizontal(glyph, unscaled, renderer, surface, 10.0f, 100.0f, metrics, result); }))); + Require(SameImage(written, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImage(glyph, unscaled, renderer, surface, 10.0f, 20.0f, metrics, result); }))); + } + + Require(sceFontUnbindRenderer(font) == SCE_FONT_OK); + Require(RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontRenderCharGlyphImageHorizontal(font, 'A', surface, 10.0f, 100.0f, metrics, result); }).rc == SCE_FONT_ERROR_NOT_BOUND_RENDERER); + Require(SameImage(horizontal, RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { return sceFontGlyphRenderImageHorizontal(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); }))); + + const auto filled = [](FontGlyphMetrics* metrics, FontRenderOutput* result) { + std::memset(metrics, 0xA5, sizeof(*metrics)); + std::memset(result, 0xA5, sizeof(*result)); + }; + FontGlyphOpaque notAGlyph{}; + for (const FontGlyph wrong : {static_cast(nullptr), ¬AGlyph}) { + for (const auto render : {sceFontGlyphRenderImage, sceFontGlyphRenderImageHorizontal, sceFontGlyphRenderImageVertical}) { + Require(Cleared(RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { filled(metrics, result); return render(wrong, nullptr, renderer, surface, 10.0f, 100.0f, metrics, result); }), SCE_FONT_ERROR_INVALID_GLYPH)); + } + } + for (const auto render : {sceFontGlyphRenderImage, sceFontGlyphRenderImageHorizontal, sceFontGlyphRenderImageVertical}) { + Require(Cleared(RenderInto([&](FontRenderSurface*, FontGlyphMetrics* metrics, FontRenderOutput* result) { filled(metrics, result); return render(glyph, nullptr, renderer, nullptr, 10.0f, 100.0f, metrics, result); }), SCE_FONT_ERROR_INVALID_PARAMETER)); + Require(Cleared(RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics*, FontRenderOutput* result) { std::memset(result, 0xA5, sizeof(*result)); return render(glyph, nullptr, renderer, surface, 10.0f, 100.0f, nullptr, result); }), SCE_FONT_ERROR_INVALID_PARAMETER)); + Require(Cleared(RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput*) { std::memset(metrics, 0xA5, sizeof(*metrics)); return render(glyph, nullptr, renderer, surface, 10.0f, 100.0f, metrics, nullptr); }), SCE_FONT_ERROR_INVALID_PARAMETER)); + Require(Cleared(RenderInto([&](FontRenderSurface* surface, FontGlyphMetrics* metrics, FontRenderOutput* result) { filled(metrics, result); return render(glyph, nullptr, nullptr, surface, 10.0f, 100.0f, metrics, result); }), SCE_FONT_ERROR_INVALID_RENDERER)); + } + Require(sceFontDeleteGlyph(nullptr, &glyph) == SCE_FONT_OK && glyph == nullptr); +} + int main() { constexpr std::uint32_t EuropeanLight = 0x180700C3u; constexpr std::uint32_t EuropeanBold = 0x180700C7u; @@ -310,6 +425,7 @@ int main() { Require(sceFontGetKerning(mixed, 'A', 0x4E00, &kerning) == SCE_FONT_OK && kerning.offsetX == 0.0f); FontRenderer renderer = nullptr; Require(sceFontCreateRenderer(&memory, sceFontSelectRendererFt(0), &renderer) == SCE_FONT_OK); + CheckGlyphImages(memoryFont, renderer); Require(sceFontBindRenderer(mixed, renderer) == SCE_FONT_OK); Require(sceFontSetupRenderScalePixel(mixed, 100.0f, 100.0f) == SCE_FONT_OK); float advance = 0.0f; diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 69251a837f..1b5466bbaf 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -22,6 +22,7 @@ - [sceFontGraphicsDrawupFillTextureImageObject, sceFontGraphicsDrawupFillTexturePatternObject](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) (libSceFont) - still throwing. Both have bodies in the 4.03 `libSceFont.sprx` (670 and 871 bytes); the signature `(graphicsObject, fillObject, mode, out, x, y, w, h)` with an `int` error return is confirmed two ways. The body's own validation path: `out` is zeroed up front, the graphics object is checked for magic word `0xf53` at +0 and stores the error at +4 (`0x80460084` null object, `0x80460002` invalid arguments, `0x80460001` no renderer, `0x8046005c` unbound buffer, `0x804600a2` empty pool). PPSA20397's `libSceFontGsm.prx` caller confirms the argument construction: it passes the embedded object at `graphicsObject+0x28`, the mode byte at `+0xf`, and its own `out`/rect arguments verbatim after checking `[graphics+0xd]==0x58`, `[graphics+0x38]==1` and the rect against a float bound. On valid input the body takes the renderer's record pool (`graphicsObject+8` -> `+8` -> `+0x160`), allocates a 0x40+0x80 drawup record (command byte 0x40, then the rect floats and mode), enqueues it into the object's record list (+0x28/+0x30 with used/maximum counters at +0x10/+0x14) and returns it through `out`. Implementing it needs the `sceFontGraphics` drawup pipeline it feeds - the record pool, the dequeueing objects and the ~40 sibling `sceFontGraphics*` exports - all of which are stubbed, so a standalone producer would report success for work nothing consumes - [sceFontDefineAttribute, sceFontGetAttribute](../../../core/libs/prx/libSceFont/src/Style.cpp), [sceFontGlyphDefineAttribute, sceFontGlyphGetAttribute](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - the signatures `(handle, int attribute, int* oldAttribute)` and `(handle, int attribute, int* nowAttribute)`, and the same shape for a glyph, follow the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, matching the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1249-L1264), not confirmed on a title. Every handle and every glyph keeps one attribute, an opaque int whose meaning and valid values are unknown: define stores the new value and returns the previous one, and get returns the stored value with its `attribute` argument unused, as in KytyPS5. An attribute never set is 0. The null checks and the invalid-object errors follow the other libSceFont functions and diverge from [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1251-L1265), which returns `OK` for a null handle or glyph after writing 0 to the old-attribute pointer: every other handle and glyph entry point in this library reports the invalid object instead, and a title that passes one is already failing. shadPS4 leaves both exports unimplemented, so there is no second implementation to compare. PPSA01325 calls the glyph define with 0x11 on a glyph from `sceFontGenerateCharGlyph` that it deletes unused and draws with `sceFontRenderCharGlyphImageHorizontal`; the three bindings all read the third argument as an out pointer, not a value, so this call sets the glyph's attribute to 0x11. The attribute changes neither shaping nor rendering. +- [sceFontGlyphRenderImage, sceFontGlyphRenderImageHorizontal, sceFontGlyphRenderImageVertical](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - follow firmware 13.42's module as BunnyFl ran it natively ([comment](https://github.com/boykopovar/AnyPS5/pull/10892#issuecomment-6415399922); one font, 'A', one pixel format, not a console): the glyph's character is drawn as `sceFontRenderCharGlyphImage` and its directional forms draw it, at the glyph's generation scale; the surface's style frame wins over the argument, and a frame that sets no scale keeps that scale. A valid `sceFontGlyphRenderImageVertical` call, and `sceFontGlyphRenderImage` on a vertical font, throw: the module reports advance and stride 40/40 there where the char call gives 90/90 at 40 px, and one case doesn't name the rule. A pointer that isn't a renderer, and `SCE_FONT_ERROR_INVALID_FONT_HANDLE` for a closed owner font, are not measured ### Functional @@ -39,7 +40,7 @@ Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Frame and resource layouts remain opaque; their runtime implementation is still unavailable. -Glyph image rendering uses six pointer arguments and two scalar coordinates, with existing style, surface, metrics and output layouts. Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered; control payload types depend on the command and remain unmodeled. +Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered; control payload types depend on the command and remain unmodeled. Canvas, design, surface texture and fill-pattern construction signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Pattern mode/count semantics and the graphics surface descriptor fields still require reconstruction; distinct opaque types preserve the confirmed object categories.