mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
feat(libSceFreeType): implement FT_Get_Gasp
This commit is contained in:
1 parent
0db47b263d
commit
b13deef0db
4 files changed
+95
-3
No files matched your search
@@ -3,6 +3,8 @@
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
#include FT_GASP_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
@@ -186,6 +188,11 @@ int APS5_VABI FT_Load_Glyph_nid_postfix(GuestFaceRec* face, std::uint32_t glyph_
|
||||
return error;
|
||||
}
|
||||
|
||||
int APS5_VABI FT_Get_Gasp_nid_postfix(GuestFaceRec* face, std::uint32_t ppem) {
|
||||
const GuestFace* owner = FaceOf(face);
|
||||
return FT_Get_Gasp(owner ? owner->host : nullptr, ppem);
|
||||
}
|
||||
|
||||
int APS5_VABI FT_Load_Char_nid_postfix(GuestFaceRec* face, std::uint64_t charCode, std::int32_t loadFlags) {
|
||||
GuestFace* owner = FaceOf(face);
|
||||
const FT_Error error = FT_Load_Char(owner ? owner->host : nullptr, static_cast<FT_ULong>(static_cast<std::uint32_t>(charCode)), loadFlags);
|
||||
|
||||
@@ -20,7 +20,8 @@ static void PutAll16(std::vector<unsigned char>& out, std::initializer_list<int>
|
||||
for (const int value : values) Put16(out, static_cast<std::uint32_t>(static_cast<std::uint16_t>(value)));
|
||||
}
|
||||
|
||||
static std::vector<unsigned char> SquareGlyphFont(std::uint32_t codepoint, int width, bool symbolMap = false) {
|
||||
static std::vector<unsigned char> SquareGlyphFont(std::uint32_t codepoint, int width, bool symbolMap = false,
|
||||
const std::vector<unsigned char>* gasp = nullptr) {
|
||||
std::vector<unsigned char> head;
|
||||
Put32(head, 0x00010000u);
|
||||
Put32(head, 0x00010000u);
|
||||
@@ -53,11 +54,19 @@ static std::vector<unsigned char> SquareGlyphFont(std::uint32_t codepoint, int w
|
||||
const int symbol = static_cast<int>(codepoint + 0xF000);
|
||||
PutAll16(cmap, {4, 32, 0, 4, 4, 1, 0, symbol, 0xFFFF, 0, symbol, 0xFFFF, 1 - symbol, 1, 0, 0});
|
||||
}
|
||||
const std::vector<std::pair<const char*, const std::vector<unsigned char>*>> tables = {
|
||||
std::vector<std::pair<const char*, const std::vector<unsigned char>*>> tables = {
|
||||
{"cmap", &cmap}, {"glyf", &glyf}, {"head", &head}, {"hhea", &hhea}, {"hmtx", &hmtx}, {"loca", &loca}, {"maxp", &maxp}};
|
||||
if (gasp) tables.insert(tables.begin() + 1, {"gasp", gasp});
|
||||
unsigned power = 1;
|
||||
unsigned selector = 0;
|
||||
while (power <= tables.size() / 2) {
|
||||
power *= 2;
|
||||
++selector;
|
||||
}
|
||||
std::vector<unsigned char> font;
|
||||
Put32(font, 0x00010000u);
|
||||
PutAll16(font, {static_cast<int>(tables.size()), 64, 2, static_cast<int>(tables.size()) * 16 - 64});
|
||||
PutAll16(font, {static_cast<int>(tables.size()), static_cast<int>(power * 16), static_cast<int>(selector),
|
||||
static_cast<int>(tables.size() * 16 - power * 16)});
|
||||
std::uint32_t offset = 12 + static_cast<std::uint32_t>(tables.size()) * 16;
|
||||
for (const auto& [tag, data] : tables) {
|
||||
font.insert(font.end(), tag, tag + 4);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GASP_H
|
||||
#include FT_OUTLINE_H
|
||||
|
||||
#include <algorithm>
|
||||
@@ -28,6 +29,7 @@ extern "C" {
|
||||
void APS5_VABI FT_Set_Transform_nid_postfix(GuestFaceRec*, const GuestMatrix*, const GuestVector*);
|
||||
int APS5_VABI FT_Init_FreeType_nid_postfix(void**);
|
||||
int APS5_VABI FT_Done_FreeType_nid_postfix(void*);
|
||||
int APS5_VABI FT_Get_Gasp_nid_postfix(GuestFaceRec*, std::uint32_t);
|
||||
int APS5_VABI FT_New_Face_nid_postfix(void*, const char*, std::int64_t, GuestFaceRec**);
|
||||
int APS5_VABI FT_New_Memory_Face_nid_postfix(void*, const unsigned char*, std::int64_t, std::int64_t, GuestFaceRec**);
|
||||
int APS5_VABI FT_Done_Face_nid_postfix(GuestFaceRec*);
|
||||
@@ -47,6 +49,77 @@ static void Check(bool value, int line) {
|
||||
}
|
||||
#define Require(value) Check((value), __LINE__)
|
||||
|
||||
static void CheckGaspTable(const std::vector<unsigned char>* table, const std::vector<std::pair<unsigned, int>>& ranges,
|
||||
bool versionZero, bool render) {
|
||||
const auto font = SquareGlyphFont('A', 700, false, table);
|
||||
void* library = nullptr;
|
||||
GuestFaceRec* face = nullptr;
|
||||
FT_Library nativeLibrary = nullptr;
|
||||
FT_Face native = nullptr;
|
||||
Require(FT_Init_FreeType_nid_postfix(&library) == 0 && FT_Init_FreeType(&nativeLibrary) == 0);
|
||||
Require(FT_New_Memory_Face_nid_postfix(library, font.data(), font.size(), 0, &face) == 0);
|
||||
Require(FT_New_Memory_Face(nativeLibrary, font.data(), static_cast<FT_Long>(font.size()), 0, &native) == 0);
|
||||
auto* slot = face->glyph;
|
||||
const auto glyphIndex = slot->glyph_index;
|
||||
const auto bitmap = slot->bitmap.buffer;
|
||||
const auto points = slot->outline.points;
|
||||
for (std::uint32_t ppem = 0; ppem <= 65536; ++ppem) {
|
||||
int expected = FT_GASP_NO_TABLE;
|
||||
for (const auto& [maximum, flags] : ranges) {
|
||||
if (ppem > maximum) continue;
|
||||
expected = versionZero ? flags & 3 : flags;
|
||||
break;
|
||||
}
|
||||
Require(FT_Get_Gasp_nid_postfix(face, ppem) == expected);
|
||||
Require(FT_Get_Gasp(native, ppem) == expected);
|
||||
}
|
||||
Require(FT_Get_Gasp_nid_postfix(face, UINT32_MAX) == FT_GASP_NO_TABLE);
|
||||
Require(face->glyph == slot && slot->glyph_index == glyphIndex && slot->bitmap.buffer == bitmap && slot->outline.points == points);
|
||||
if (render) {
|
||||
for (const unsigned ppem : {7u, 8u, 15u, 16u, 31u, 32u, 64u}) {
|
||||
const int flags = FT_Get_Gasp_nid_postfix(face, ppem);
|
||||
const int load = flags & FT_GASP_DO_GRIDFIT ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING;
|
||||
const auto mode = flags & FT_GASP_DO_GRAY ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO;
|
||||
Require(FT_Set_Pixel_Sizes_nid_postfix(face, 0, ppem) == 0 && FT_Set_Pixel_Sizes(native, 0, ppem) == 0);
|
||||
Require(FT_Load_Char_nid_postfix(face, 'A', load) == 0 && FT_Load_Char(native, 'A', load) == 0);
|
||||
Require(FT_Render_Glyph_nid_postfix(face->glyph, mode) == 0 && FT_Render_Glyph(native->glyph, mode) == 0);
|
||||
const auto& actual = face->glyph->bitmap;
|
||||
const auto& expected = native->glyph->bitmap;
|
||||
Require(actual.width > 0 && actual.rows > 0);
|
||||
Require(actual.width == expected.width && actual.rows == expected.rows && actual.pitch == expected.pitch);
|
||||
Require(actual.pixel_mode == expected.pixel_mode && actual.num_grays == expected.num_grays);
|
||||
Require(face->glyph->bitmap_left == native->glyph->bitmap_left && face->glyph->bitmap_top == native->glyph->bitmap_top);
|
||||
Require(std::memcmp(actual.buffer, expected.buffer, static_cast<std::size_t>(actual.rows) * std::abs(actual.pitch)) == 0);
|
||||
}
|
||||
}
|
||||
Require(FT_Done_Face_nid_postfix(face) == 0 && FT_Done_Face(native) == 0);
|
||||
Require(FT_Done_FreeType_nid_postfix(library) == 0 && FT_Done_FreeType(nativeLibrary) == 0);
|
||||
}
|
||||
|
||||
static void CheckGasp() {
|
||||
Require(FT_Get_Gasp_nid_postfix(nullptr, 0) == FT_GASP_NO_TABLE);
|
||||
Require(FT_Get_Gasp_nid_postfix(nullptr, UINT32_MAX) == FT_GASP_NO_TABLE);
|
||||
CheckGaspTable(nullptr, {}, false, false);
|
||||
const std::vector<std::pair<unsigned, int>> ranges{{0, 0x8003}, {7, 2}, {15, 1}, {31, 0}, {65535, 0xFFFF}};
|
||||
for (const unsigned version : {0u, 1u}) {
|
||||
std::vector<unsigned char> table;
|
||||
Put16(table, version);
|
||||
Put16(table, ranges.size());
|
||||
for (const auto& [maximum, flags] : ranges) {
|
||||
Put16(table, maximum);
|
||||
Put16(table, flags);
|
||||
}
|
||||
CheckGaspTable(&table, ranges, version == 0, true);
|
||||
}
|
||||
std::vector<unsigned char> shortRange;
|
||||
PutAll16(shortRange, {1, 1, 48, FT_GASP_DO_GRAY});
|
||||
CheckGaspTable(&shortRange, {{48, FT_GASP_DO_GRAY}}, false, false);
|
||||
for (const std::vector<unsigned char>& table : {std::vector<unsigned char>{}, {0, 1}, {0, 1, 0, 0}, {0, 2, 0, 1, 0, 10, 0, 3}}) {
|
||||
CheckGaspTable(&table, {}, false, false);
|
||||
}
|
||||
std::puts("FreeType gasp: 8 fonts, 524304 queries, null faces and 14 rendered glyphs passed");
|
||||
}
|
||||
|
||||
template <typename TCall> static bool ThrowsWith(TCall call, const std::string& prefix) {
|
||||
try {
|
||||
call();
|
||||
@@ -361,6 +434,7 @@ static void CheckTransforms(const std::vector<unsigned char>& font) {
|
||||
int main() {
|
||||
const auto root = std::filesystem::temp_directory_path() / ("anyps5_guest_freetype-" + std::to_string(std::random_device{}()));
|
||||
std::filesystem::create_directories(root / "app0");
|
||||
CheckGasp();
|
||||
const std::vector<unsigned char> font = SquareGlyphFont('A', 700);
|
||||
{
|
||||
std::ofstream file(root / "app0" / "font.ttf", std::ios::binary);
|
||||
|
||||
@@ -22,3 +22,5 @@
|
||||
|
||||
- [FT_Outline_Get_Orientation](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console ABI/version and large-outline equivalence are unverified. The adapter follows bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/src/base/ftoutln.c) with the existing unsigned guest counts and an LP64 area accumulator, including on Windows.
|
||||
- [FT_Set_Char_Size](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console ABI, FreeType version and error compatibility are unverified. The name and NID `xuY99Qcb108` are in the [PS5 3.20 inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceFreeTypeOt.c); the LP64 signature and behavior follow bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/src/base/ftobjs.c), not a console measurement. After applying its defaults and minimum-size clamp, requests whose `dimension * dpi + 36` exceeds the host's signed `FT_Long` range throw before the native call. This is a 32-bit limit on Windows; larger console requests remain unsupported.
|
||||
|
||||
- [FT_Get_Gasp](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console FreeType version and query-result compatibility are unverified. The guest face adapter follows bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/src/base/ftgasp.c) and its existing LP64 face bridge.
|
||||
Reference in new issue
Block a user