From 8b5e75323501f48de788197f29905095f734e2ff Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 19 Jan 2016 19:23:58 +0100 Subject: [PATCH] Add shared system font parsing code. --- libctru/include/3ds.h | 1 + libctru/include/3ds/font.h | 205 +++++++++++++++++++++++++++++ libctru/include/3ds/services/apt.h | 6 + libctru/source/font.c | 134 +++++++++++++++++++ libctru/source/services/apt.c | 13 ++ 5 files changed, 359 insertions(+) create mode 100644 libctru/include/3ds/font.h create mode 100644 libctru/source/font.c diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 48adc16..a8d3f65 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -68,6 +68,7 @@ extern "C" { #include <3ds/sdmc.h> #include <3ds/romfs.h> +#include <3ds/font.h> #ifdef __cplusplus } diff --git a/libctru/include/3ds/font.h b/libctru/include/3ds/font.h new file mode 100644 index 0000000..c747023 --- /dev/null +++ b/libctru/include/3ds/font.h @@ -0,0 +1,205 @@ +/** + * @file font.h + * @brief Shared font support. + */ +#pragma once +#include <3ds/types.h> + +///@name Data types +///@{ + +/// Character width information structure. +typedef struct +{ + s8 left; ///< Horizontal offset to draw the glyph with. + u8 glyphWidth; ///< Width of the glyph. + u8 charWidth; ///< Width of the character, that is, horizontal distance to advance. +} charWidthInfo_s; + +/// Font texture sheet information. +typedef struct +{ + u8 cellWidth; ///< Width of a glyph cell. + u8 cellHeight; ///< Height of a glyph cell. + u8 baselinePos; ///< Vertical position of the baseline. + u8 maxCharWidth; ///< Maximum character width. + + u32 sheetSize; ///< Size in bytes of a texture sheet. + u16 nSheets; ///< Number of texture sheets. + u16 sheetFmt; ///< GPU texture format (GPU_TEXCOLOR). + + u16 nRows; ///< Number of glyphs per row per sheet. + u16 nLines; ///< Number of glyph rows per sheet. + + u16 sheetWidth; ///< Texture sheet width. + u16 sheetHeight; ///< Texture sheet height. + u8* sheetData; ///< Pointer to texture sheet data. +} TGLP_s; + +/// Font character width information block type. +typedef struct tag_CWDH_s CWDH_s; + +/// Font character width information block structure. +struct tag_CWDH_s +{ + u16 startIndex; ///< First Unicode codepoint the block applies to. + u16 endIndex; ///< Last Unicode codepoint the block applies to. + CWDH_s* next; ///< Pointer to the next block. + + charWidthInfo_s widths[0]; ///< Table of character width information structures. +}; + +/// Font character map methods. +enum +{ + CMAP_TYPE_DIRECT = 0, ///< Identity mapping. + CMAP_TYPE_TABLE = 1, ///< Mapping using a table. + CMAP_TYPE_SCAN = 2, ///< Mapping using a list of mapped characters. +}; + +/// Font character map type. +typedef struct tag_CMAP_s CMAP_s; + +/// Font character map structure. +struct tag_CMAP_s +{ + u16 codeBegin; ///< First Unicode codepoint the block applies to. + u16 codeEnd; ///< Last Unicode codepoint the block applies to. + u16 mappingMethod; ///< Mapping method. + u16 reserved; + CMAP_s* next; ///< Pointer to the next map. + + union + { + u16 indexOffset; ///< For CMAP_TYPE_DIRECT: index of the first glyph. + u16 indexTable[0]; ///< For CMAP_TYPE_TABLE: table of glyph indices. + /// For CMAP_TYPE_SCAN: Mapping data. + struct + { + u16 nScanEntries; ///< Number of pairs. + /// Mapping pairs. + struct + { + u16 code; ///< Unicode codepoint. + u16 glyphIndex; ///< Mapped glyph index. + } scanEntries[0]; + }; + }; +}; + +/// Font information structure. +typedef struct +{ + u32 signature; ///< Signature (FINF). + u32 sectionSize; ///< Section size. + + u8 fontType; ///< Font type + u8 lineFeed; ///< Line feed vertical distance. + u16 alterCharIndex; ///< Glyph index of the replacement character. + charWidthInfo_s defaultWidth; ///< Default character width information. + u8 encoding; ///< Font encoding (?) + + TGLP_s* tglp; ///< Pointer to texture sheet information. + CWDH_s* cwdh; ///< Pointer to the first character width information block. + CMAP_s* cmap; ///< Pointer to the first character map. + + u8 height; ///< Font height. + u8 width; ///< Font width. + u8 ascent; ///< Font ascent. + u8 padding; +} FINF_s; + +/// Font structure. +typedef struct +{ + u32 signature; ///< Signature (CFNU). + u16 endianness; ///< Endianness constant (0xFEFF). + u16 headerSize; ///< Header size. + u32 version; ///< Format version. + u32 fileSize; ///< File size. + u32 nBlocks; ///< Number of blocks. + + FINF_s finf; ///< Font information. +} CFNT_s; + +/// Font glyph position structure. +typedef struct +{ + int sheetIndex; ///< Texture sheet index to use to render the glyph. + float xOffset; ///< Horizontal offset to draw the glyph width. + float xAdvance; ///< Horizontal distance to advance after drawing the glyph. + float width; ///< Glyph width. + ///< Texture coordinates to use to render the glyph. + struct + { + float left, top, right, bottom; + } texcoord; + ///< Vertex coordinates to use to render the glyph. + struct + { + float left, top, right, bottom; + } vtxcoord; +} fontGlyphPos_s; + +/// Flags for use with fontCalcGlyphPos. +enum +{ + GLYPH_POS_CALC_VTXCOORD = BIT(0), ///< Calculates vertex coordinates in addition to texture coordinates. + GLYPH_POS_AT_BASELINE = BIT(1), ///< Position the glyph at the baseline instead of at the top-left corner. + GLYPH_POS_Y_POINTS_UP = BIT(2), ///< Indicates that the Y axis points up instead of down. +}; + +///@} + +///@name Initialization and basic operations +///@{ + +/// Ensures the shared system font is mapped. +Result fontEnsureMapped(void); + +/// Retrieves the font information structure of the shared system font. +static inline FINF_s* fontGetInfo(void) +{ + extern CFNT_s* g_sharedFont; + return &g_sharedFont->finf; +} + +/// Retrieves the texture sheet information of the shared system font. +static inline TGLP_s* fontGetGlyphInfo(void) +{ + return fontGetInfo()->tglp; +} + +/** + * @brief Retrieves the pointer to texture data for the specified texture sheet. + * @param sheetIndex Index of the texture sheet. + */ +static inline void* fontGetGlyphSheetTex(int sheetIndex) +{ + TGLP_s* tglp = fontGetGlyphInfo(); + return &tglp->sheetData[sheetIndex*tglp->sheetSize]; +} + +/** + * @brief Retrieves the glyph index of the specified Unicode codepoint. + * @param codePoint Unicode codepoint. + */ +int fontGlyphIndexFromCodePoint(u32 codePoint); + +/** + * @brief Retrieves character width information of the specified glyph. + * @param glyphIndex Index of the glyph. + */ +charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex); + +/** + * @brief Calculates position information for the specified glyph. + * @param out Output structure in which to write the information. + * @param glyphIndex Index of the glyph. + * @param flags Calculation flags (see GLYPH_POS_* flags). + * @param scaleX Scale factor to apply horizontally. + * @param scaleY Scale factor to apply vertically. + */ +void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY); + +///@} diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index 3b102bf..d73dd16 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -444,3 +444,9 @@ Result APT_PrepareToStartSystemApplet(NS_APPID appID); */ Result APT_StartSystemApplet(NS_APPID appID, u32 bufSize, Handle applHandle, u8 *buf); +/** + * @brief Retrieves the shared system font. + * @brief fontHandle Pointer to write the handle of the system font memory block to. + * @brief mapAddr Pointer to write the mapping address of the system font memory block to. + */ +Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr); diff --git a/libctru/source/font.c b/libctru/source/font.c new file mode 100644 index 0000000..8f3f675 --- /dev/null +++ b/libctru/source/font.c @@ -0,0 +1,134 @@ +#include +#include +#include <3ds/font.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> +#include <3ds/result.h> +#include <3ds/services/apt.h> + +CFNT_s* g_sharedFont; +static u32 sharedFontAddr; +static int charPerSheet; + +Result fontEnsureMapped(void) +{ + if (g_sharedFont) return 0; + Result res = 0; + Handle hSharedFont = 0; + + aptOpenSession(); + res = APT_GetSharedFont(&hSharedFont, &sharedFontAddr); + aptCloseSession(); + if (R_FAILED(res)) return res; + + // Map the shared font if it's not already mapped. + res = svcMapMemoryBlock(hSharedFont, 0, MEMPERM_READ, MEMPERM_DONTCARE); + svcCloseHandle(hSharedFont); + if (R_FAILED(res) && res != 0xE0A01BF5) + return res; + + g_sharedFont = (CFNT_s*)(sharedFontAddr+0x80); + charPerSheet = g_sharedFont->finf.tglp->nRows * g_sharedFont->finf.tglp->nLines; + return 0; +} + +int fontGlyphIndexFromCodePoint(u32 codePoint) +{ + int ret = g_sharedFont->finf.alterCharIndex; + if (codePoint < 0x10000) + { + CMAP_s* cmap; + for (cmap = g_sharedFont->finf.cmap; cmap; cmap = cmap->next) + { + if (codePoint < cmap->codeBegin || codePoint > cmap->codeEnd) + continue; + + if (cmap->mappingMethod == CMAP_TYPE_DIRECT) + { + ret = cmap->indexOffset + (codePoint - cmap->codeBegin); + break; + } + + if (cmap->mappingMethod == CMAP_TYPE_TABLE) + { + ret = cmap->indexTable[codePoint - cmap->codeBegin]; + break; + } + + int j; + for (j = 0; j < cmap->nScanEntries; j ++) + if (cmap->scanEntries[j].code == codePoint) + break; + if (j < cmap->nScanEntries) + { + ret = cmap->scanEntries[j].glyphIndex; + break; + } + } + } + return ret; +} + +charWidthInfo_s* fontGetCharWidthInfo(int glyphIndex) +{ + charWidthInfo_s* info = NULL; + CWDH_s* cwdh; + for (cwdh = g_sharedFont->finf.cwdh; cwdh && !info; cwdh = cwdh->next) + { + if (glyphIndex < cwdh->startIndex || glyphIndex > cwdh->endIndex) + continue; + info = &cwdh->widths[glyphIndex - cwdh->startIndex]; + } + if (!info) + info = &g_sharedFont->finf.defaultWidth; + return info; +} + +void fontCalcGlyphPos(fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY) +{ + FINF_s* finf = &g_sharedFont->finf; + TGLP_s* tglp = finf->tglp; + charWidthInfo_s* cwi = fontGetCharWidthInfo(glyphIndex); + + int sheetId = glyphIndex / charPerSheet; + int glInSheet = glyphIndex % charPerSheet; + out->sheetIndex = sheetId; + out->xOffset = scaleX*cwi->left; + out->xAdvance = scaleX*cwi->charWidth; + out->width = scaleX*cwi->glyphWidth; + + int lineId = glInSheet / tglp->nRows; + int rowId = glInSheet % tglp->nRows; + + float tx = (float)(rowId*(tglp->cellWidth+1)+1) / tglp->sheetWidth; + float ty = 1.0f - (float)((lineId+1)*(tglp->cellHeight+1)+1) / tglp->sheetHeight; + float tw = (float)cwi->glyphWidth / tglp->sheetWidth; + float th = (float)tglp->cellHeight / tglp->sheetHeight; + out->texcoord.left = tx; + out->texcoord.top = ty+th; + out->texcoord.right = tx+tw; + out->texcoord.bottom = ty; + + if (flags & GLYPH_POS_CALC_VTXCOORD) + { + float vx = out->xOffset; + float vy = (flags & GLYPH_POS_AT_BASELINE) ? (scaleY*tglp->baselinePos) : 0; + float vw = out->width; + float vh = scaleY*tglp->cellHeight; + if (flags & GLYPH_POS_Y_POINTS_UP) + { + vy = -(vh-vy); + out->vtxcoord.left = vx; + out->vtxcoord.top = vy+vh; + out->vtxcoord.right = vx+vw; + out->vtxcoord.bottom = vy; + } else + { + vy = -vy; + out->vtxcoord.left = vx; + out->vtxcoord.top = vy; + out->vtxcoord.right = vx+vw; + out->vtxcoord.bottom = vy+vh; + } + } +} diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index 21ed31e..d3554ce 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -1342,3 +1342,16 @@ Result APT_StartSystemApplet(NS_APPID appID, u32 bufSize, Handle applHandle, u8 return cmdbuf[1]; } +Result APT_GetSharedFont(Handle* fontHandle, u32* mapAddr) +{ + u32* cmdbuf=getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x44,0,0); // 0x00440000 + + Result ret=0; + if(R_FAILED(ret=svcSendSyncRequest(aptuHandle)))return ret; + + if(fontHandle) *fontHandle = cmdbuf[4]; + if(mapAddr) *mapAddr = cmdbuf[2]; + + return cmdbuf[1]; +}