Merge pull request #10892 from EagleBearerYS/feat/scefont-glyph-render-image-v2

feat(libSceFont): render generated glyphs with sceFontGlyphRenderImage
This commit is contained in:
Adrià authored and GitHub committed 2026-10-27 07:59:02 +01:00
commit 63d5eb00e4
6 files changed
+213 -40

No files matched your search

@@ -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);
+40 -24
View File
@@ -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<const RenderSurfaceSystemUse*>(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<const RenderSurfaceSystemUse*>(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<FontLibNative*>(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);
+52
View File
@@ -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<const RendererNative*>(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<std::int16_t>(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
@@ -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;
+116
View File
@@ -4,6 +4,7 @@
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <initializer_list>
@@ -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<std::uint8_t> pixels = std::vector<std::uint8_t>(128 * 128);
FontGlyphMetrics metrics{};
FontRenderOutput result{};
};
template<typename TRender> 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<std::uintptr_t>(image.pixels.data());
const auto address = reinterpret_cast<std::uintptr_t>(surface.address);
const std::uintptr_t span = rect.w == 0 || rect.h == 0 ? 0 : static_cast<std::uintptr_t>(rect.h - 1) * surface.widthByte + static_cast<std::uintptr_t>(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 : {&notAFrame, &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<FontGlyph>(nullptr), &notAGlyph}) {
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;
+2 -1
View File
@@ -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.