mirror of https://gitlab.com/nakst/essence
bitmap font renderer
This commit is contained in:
parent
eb877e864e
commit
a16658eed0
|
@ -139,7 +139,7 @@ int FontPreviewMessage(EsElement *element, EsMessage *message) {
|
|||
EsElementGetTextStyle(element, &runs[0].style);
|
||||
runs[0].style.font.family = element->userData.u;
|
||||
runs[0].style.font.weight = element->instance->fontVariant % 10;
|
||||
runs[0].style.font.italic = element->instance->fontVariant / 10;
|
||||
if (element->instance->fontVariant / 10 == 1) runs[0].style.font.flags |= ES_FONT_ITALIC;
|
||||
runs[0].style.size = element->instance->fontSize;
|
||||
runs[1].offset = element->instance->previewTextBytes;
|
||||
EsTextPlan *plan = EsTextPlanCreate(element, &properties, bounds, element->instance->previewText, runs, 1);
|
||||
|
|
|
@ -248,7 +248,7 @@ void CreateStyledTextDisplay(Instance *instance, EsStyle *style, uint64_t flags
|
|||
runs[i].offset = instance->spans[i].offset;
|
||||
EsElementGetTextStyle(display, &runs[i].style);
|
||||
if (instance->spans[i].link) { runs[i].style.decorations |= ES_TEXT_DECORATION_UNDERLINE; runs[i].style.color = COLOR_TEXT_LINK; }
|
||||
if (instance->spans[i].em) runs[i].style.font.italic = true;
|
||||
if (instance->spans[i].em) runs[i].style.font.flags |= ES_FONT_ITALIC;
|
||||
if (instance->spans[i].strong) runs[i].style.font.weight = 7;
|
||||
if (instance->spans[i].monospaced) runs[i].style.font.family = ES_FONT_MONOSPACED;
|
||||
runs[i].style.decorationsColor = runs[i].style.color;
|
||||
|
|
|
@ -261,7 +261,8 @@ const void *const apiTable[] = {
|
|||
};
|
||||
|
||||
#ifdef PAUSE_ON_USERLAND_CRASH
|
||||
ES_EXTERN_C uintptr_t APISyscallCheckForCrash(uintptr_t argument0, uintptr_t argument1, uintptr_t argument2, uintptr_t unused, uintptr_t argument3, uintptr_t argument4) {
|
||||
__attribute__((no_instrument_function))
|
||||
uintptr_t APISyscallCheckForCrash(uintptr_t argument0, uintptr_t argument1, uintptr_t argument2, uintptr_t unused, uintptr_t argument3, uintptr_t argument4) {
|
||||
uintptr_t returnValue = _APISyscall(argument0, argument1, argument2, unused, argument3, argument4);
|
||||
EsProcessState state;
|
||||
_APISyscall(ES_SYSCALL_PROCESS_GET_STATE, ES_CURRENT_PROCESS, (uintptr_t) &state, 0, 0, 0);
|
||||
|
@ -1486,28 +1487,26 @@ extern "C" void _start(EsProcessStartupInformation *_startupInformation) {
|
|||
EsMemoryCopy(ES_API_BASE, apiTable, sizeof(apiTable));
|
||||
}
|
||||
|
||||
{
|
||||
// Initialise the API.
|
||||
// Initialise the API.
|
||||
|
||||
_init();
|
||||
EsRandomSeed(ProcessorReadTimeStamp());
|
||||
ThreadInitialise(&threadLocalStorage);
|
||||
EsMessageMutexAcquire();
|
||||
_init();
|
||||
EsRandomSeed(ProcessorReadTimeStamp());
|
||||
ThreadInitialise(&threadLocalStorage);
|
||||
EsMessageMutexAcquire();
|
||||
|
||||
api.global = (GlobalData *) EsMemoryMapObject(api.startupInformation->globalDataRegion,
|
||||
0, sizeof(GlobalData), isDesktop ? ES_MEMORY_MAP_OBJECT_READ_WRITE : ES_MEMORY_MAP_OBJECT_READ_ONLY);
|
||||
theming.scale = api.global->uiScale; // We'll receive ES_MSG_UI_SCALE_CHANGED when this changes.
|
||||
}
|
||||
api.global = (GlobalData *) EsMemoryMapObject(api.startupInformation->globalDataRegion,
|
||||
0, sizeof(GlobalData), isDesktop ? ES_MEMORY_MAP_OBJECT_READ_WRITE : ES_MEMORY_MAP_OBJECT_READ_ONLY);
|
||||
theming.scale = api.global->uiScale; // We'll receive ES_MSG_UI_SCALE_CHANGED when this changes.
|
||||
|
||||
#ifdef PROFILE_DESKTOP_FUNCTIONS
|
||||
size_t profilingBufferSize = 64 * 1024 * 1024;
|
||||
GfProfilingInitialise((ProfilingEntry *) EsHeapAllocate(profilingBufferSize, true),
|
||||
profilingBufferSize / sizeof(ProfilingEntry), api.startupInformation->timeStampTicksPerMs);
|
||||
#endif
|
||||
|
||||
if (isDesktop) {
|
||||
EsPrint("Reached Desktop process.\n");
|
||||
|
||||
#ifdef PROFILE_DESKTOP_FUNCTIONS
|
||||
size_t profilingBufferSize = 64 * 1024 * 1024;
|
||||
GfProfilingInitialise((ProfilingEntry *) EsHeapAllocate(profilingBufferSize, true),
|
||||
profilingBufferSize / sizeof(ProfilingEntry), api.startupInformation->timeStampTicksPerMs);
|
||||
#endif
|
||||
|
||||
// Process messages until we find the boot file system.
|
||||
|
||||
while (!api.foundBootFileSystem) {
|
||||
|
|
|
@ -567,14 +567,19 @@ define ES_LIST_VIEW_GROUP_COLLAPSABLE (1 << 3) // The group can be collapsed.
|
|||
define ES_MENU_AT_CURSOR (1 << 0)
|
||||
define ES_MENU_MAXIMUM_HEIGHT (1 << 1)
|
||||
|
||||
// Standard font families:
|
||||
define ES_FONT_SANS (0xFFFF)
|
||||
define ES_FONT_SERIF (0xFFFE)
|
||||
define ES_FONT_MONOSPACED (0xFFFD)
|
||||
|
||||
// Font weights:
|
||||
define ES_FONT_REGULAR (4)
|
||||
define ES_FONT_SEMIBOLD (6)
|
||||
define ES_FONT_BOLD (7)
|
||||
|
||||
// Font flags:
|
||||
define ES_FONT_ITALIC (1 << 0)
|
||||
|
||||
define ES_TEXT_FIGURE_DEFAULT (0)
|
||||
define ES_TEXT_FIGURE_OLD (1)
|
||||
define ES_TEXT_FIGURE_TABULAR (2)
|
||||
|
@ -1370,7 +1375,7 @@ include desktop/styles.header
|
|||
struct EsFont {
|
||||
EsFontFamily family;
|
||||
uint8_t weight;
|
||||
bool italic;
|
||||
uint8_t flags;
|
||||
}
|
||||
|
||||
struct EsTextStyle {
|
||||
|
|
392
desktop/text.cpp
392
desktop/text.cpp
|
@ -5,6 +5,8 @@
|
|||
// TODO If the font size is sufficiently large disable subpixel anti-aliasing.
|
||||
// TODO Variable font support.
|
||||
|
||||
#include <shared/bitmap_font.h>
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
#include <harfbuzz/hb.h>
|
||||
#include <harfbuzz/hb-ft.h>
|
||||
|
@ -24,10 +26,20 @@
|
|||
#define FALLBACK_SCRIPT (0x4C61746E) // "Latn"
|
||||
|
||||
struct Font {
|
||||
#define FONT_TYPE_BITMAP (1)
|
||||
#define FONT_TYPE_FREETYPE_AND_HARFBUZZ (2)
|
||||
uintptr_t type;
|
||||
|
||||
union {
|
||||
const void *bitmapData; // All data has been validated to load the font.
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
FT_Face ft;
|
||||
hb_font_t *hb;
|
||||
struct {
|
||||
FT_Face ft;
|
||||
hb_font_t *hb;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
struct GlyphCacheKey {
|
||||
|
@ -182,8 +194,6 @@ struct {
|
|||
size_t bufferPosition, bufferAllocated;
|
||||
} iconManagement;
|
||||
|
||||
Font FontGet(EsFont key);
|
||||
|
||||
// --------------------------------- Glyph cache.
|
||||
|
||||
void GlyphCacheFreeEntry() {
|
||||
|
@ -226,141 +236,291 @@ GlyphCacheEntry *LookupGlyphCacheEntry(GlyphCacheKey key) {
|
|||
// --------------------------------- Font backend abstraction layer.
|
||||
|
||||
bool FontLoad(Font *font, const void *data, size_t dataBytes) {
|
||||
if (dataBytes > sizeof(BitmapFontHeader)) {
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) data;
|
||||
|
||||
if (header->signature == BITMAP_FONT_SIGNATURE && (size_t) header->headerBytes >= sizeof(BitmapFontHeader)
|
||||
&& (size_t) header->headerBytes < 0x80 && (size_t) header->glyphBytes < 0x80 && (size_t) header->glyphCount < 0x8000
|
||||
&& dataBytes > (size_t) header->headerBytes + (size_t) header->glyphCount * (size_t) header->glyphBytes
|
||||
&& header->glyphCount >= 1 /* index 0 is used as a fallback glyph, which must be present */) {
|
||||
for (uintptr_t i = 0; i < header->glyphCount; i++) {
|
||||
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *) data + header->headerBytes + i * header->glyphBytes));
|
||||
|
||||
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
|
||||
size_t bitsStorage = (size_t) glyph->bitsHeight * bytesPerRow;
|
||||
size_t kerningStorage = (size_t) glyph->kerningEntryCount * sizeof(BitmapFontKerningEntry);
|
||||
|
||||
if (glyph->bitsWidth > 0x4000 || glyph->bitsHeight > 0x4000 || glyph->kerningEntryCount > 0x4000
|
||||
|| glyph->bitsOffset >= dataBytes
|
||||
|| bitsStorage > dataBytes - glyph->bitsOffset
|
||||
|| kerningStorage > dataBytes - glyph->bitsOffset - bitsStorage) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
font->bitmapData = data;
|
||||
font->type = FONT_TYPE_BITMAP;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
if (!fontManagement.freetypeLibrary) {
|
||||
FT_Init_FreeType(&fontManagement.freetypeLibrary);
|
||||
}
|
||||
|
||||
if (FT_New_Memory_Face(fontManagement.freetypeLibrary, (uint8_t *) data, dataBytes, 0, &font->ft)) {
|
||||
return false;
|
||||
}
|
||||
if (!FT_New_Memory_Face(fontManagement.freetypeLibrary, (uint8_t *) data, dataBytes, 0, &font->ft)) {
|
||||
font->hb = hb_ft_font_create(font->ft, nullptr);
|
||||
|
||||
font->hb = hb_ft_font_create(font->ft, nullptr);
|
||||
if (font->hb) {
|
||||
font->type = FONT_TYPE_FREETYPE_AND_HARFBUZZ;
|
||||
return true;
|
||||
} else {
|
||||
FT_Done_Face(font->ft);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontSetSize(Font *font, uint32_t size) {
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
FT_Set_Char_Size(font->ft, 0, size * FREETYPE_UNIT_SCALE, 100, 100);
|
||||
hb_ft_font_changed(font->hb);
|
||||
#endif
|
||||
}
|
||||
if (font->type == FONT_TYPE_BITMAP) {
|
||||
(void) size;
|
||||
// Ignored.
|
||||
}
|
||||
|
||||
uint32_t FontCodepointToGlyphIndex(Font *font, uint32_t codepoint) {
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
return FT_Get_Char_Index(font->ft, codepoint);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FontGetGlyphMetrics(Font *font, uint32_t glyphIndex, uint32_t *xAdvance, uint32_t *yAdvance, uint32_t *xOffset, uint32_t *yOffset) {
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
FT_Load_Glyph(font->ft, glyphIndex, 0);
|
||||
*xAdvance = font->ft->glyph->advance.x;
|
||||
*yAdvance = font->ft->glyph->advance.y;
|
||||
*xOffset = *yOffset = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t FontGetKerning(Font *font, uint32_t previous, uint32_t next) {
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
FT_Vector kerning = {};
|
||||
if (previous) FT_Get_Kerning(font->ft, previous, next, 0, &kerning);
|
||||
return kerning.x;
|
||||
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
FT_Set_Char_Size(font->ft, 0, size * FREETYPE_UNIT_SCALE, 100, 100);
|
||||
hb_ft_font_changed(font->hb);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t FontGetAscent(Font *font) {
|
||||
if (font->type == FONT_TYPE_BITMAP) {
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
|
||||
return header->yAscent * FREETYPE_UNIT_SCALE;
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
return font->ft->size->metrics.ascender;
|
||||
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
return font->ft->size->metrics.ascender;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t FontGetDescent(Font *font) {
|
||||
if (font->type == FONT_TYPE_BITMAP) {
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
|
||||
return header->yDescent * -FREETYPE_UNIT_SCALE;
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
return font->ft->size->metrics.descender;
|
||||
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
return font->ft->size->metrics.descender;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t FontGetEmWidth(Font *font) {
|
||||
if (font->type == FONT_TYPE_BITMAP) {
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
|
||||
return header->xEmWidth * FREETYPE_UNIT_SCALE;
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
return font->ft->size->metrics.x_ppem;
|
||||
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
return font->ft->size->metrics.x_ppem;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FontRenderGlyph(GlyphCacheKey key, GlyphCacheEntry *entry) {
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
FT_Load_Glyph(key.font.ft, key.glyphIndex, FT_LOAD_DEFAULT);
|
||||
FT_Outline_Translate(&key.font.ft->glyph->outline, key.fractionalPosition, 0);
|
||||
if (key.font.type == FONT_TYPE_BITMAP) {
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) key.font.bitmapData;
|
||||
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *) key.font.bitmapData + header->headerBytes + key.glyphIndex * header->glyphBytes));
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int xoff;
|
||||
int yoff;
|
||||
uint8_t *output;
|
||||
entry->width = glyph->bitsWidth;
|
||||
entry->height = glyph->bitsHeight;
|
||||
entry->xoff = -glyph->xOrigin;
|
||||
entry->yoff = -glyph->yOrigin;
|
||||
entry->dataBytes = sizeof(uint8_t) * 4 * glyph->bitsWidth * glyph->bitsHeight;
|
||||
entry->data = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
|
||||
|
||||
FT_Render_Glyph(key.font.ft->glyph, FT_RENDER_MODE_LCD);
|
||||
|
||||
FT_Bitmap *bitmap = &key.font.ft->glyph->bitmap;
|
||||
width = bitmap->width / 3;
|
||||
height = bitmap->rows;
|
||||
xoff = key.font.ft->glyph->bitmap_left;
|
||||
yoff = -key.font.ft->glyph->bitmap_top;
|
||||
|
||||
entry->dataBytes = 1 /*stupid hack for whitespace*/ + width * height * 4;
|
||||
output = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
|
||||
|
||||
if (!output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int32_t r = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 0];
|
||||
int32_t g = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 1];
|
||||
int32_t b = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 2];
|
||||
|
||||
// Reduce how noticible the colour fringes are.
|
||||
// TODO Make this adjustable?
|
||||
int32_t average = (r + g + b) / 3;
|
||||
r -= (r - average) / 3;
|
||||
g -= (g - average) / 3;
|
||||
b -= (b - average) / 3;
|
||||
|
||||
output[(x + y * width) * 4 + 0] = (uint8_t) r;
|
||||
output[(x + y * width) * 4 + 1] = (uint8_t) g;
|
||||
output[(x + y * width) * 4 + 2] = (uint8_t) b;
|
||||
output[(x + y * width) * 4 + 3] = 0xFF;
|
||||
|
||||
// EsPrint("\tPixel %d, %d: red %X, green %X, blue %X\n", x, y, r, g, b);
|
||||
if (!entry->data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
|
||||
|
||||
for (uintptr_t i = 0; i < glyph->bitsHeight; i++) {
|
||||
const uint8_t *row = (const uint8_t *) key.font.bitmapData + glyph->bitsOffset + bytesPerRow * i;
|
||||
|
||||
for (uintptr_t j = 0; j < glyph->bitsWidth; j++) {
|
||||
// TODO More efficient storage.
|
||||
uint8_t byte = (row[j / 8] & (1 << (j % 8))) ? 0xFF : 0x00;
|
||||
uint32_t copy = (byte << 24) | (byte << 16) | (byte << 8) | byte;
|
||||
((uint32_t *) entry->data)[i * entry->width + j] = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output) {
|
||||
entry->data = output;
|
||||
entry->width = width;
|
||||
entry->height = height;
|
||||
entry->xoff = xoff;
|
||||
entry->yoff = yoff;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
if (key.font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
FT_Load_Glyph(key.font.ft, key.glyphIndex, FT_LOAD_DEFAULT);
|
||||
FT_Outline_Translate(&key.font.ft->glyph->outline, key.fractionalPosition, 0);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int xoff;
|
||||
int yoff;
|
||||
uint8_t *output;
|
||||
|
||||
FT_Render_Glyph(key.font.ft->glyph, FT_RENDER_MODE_LCD);
|
||||
|
||||
FT_Bitmap *bitmap = &key.font.ft->glyph->bitmap;
|
||||
width = bitmap->width / 3;
|
||||
height = bitmap->rows;
|
||||
xoff = key.font.ft->glyph->bitmap_left;
|
||||
yoff = -key.font.ft->glyph->bitmap_top;
|
||||
|
||||
entry->dataBytes = 1 /*stupid hack for whitespace*/ + width * height * 4;
|
||||
output = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
|
||||
|
||||
if (!output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int32_t r = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 0];
|
||||
int32_t g = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 1];
|
||||
int32_t b = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 2];
|
||||
|
||||
// Reduce how noticible the colour fringes are.
|
||||
// TODO Make this adjustable?
|
||||
int32_t average = (r + g + b) / 3;
|
||||
r -= (r - average) / 3;
|
||||
g -= (g - average) / 3;
|
||||
b -= (b - average) / 3;
|
||||
|
||||
output[(x + y * width) * 4 + 0] = (uint8_t) r;
|
||||
output[(x + y * width) * 4 + 1] = (uint8_t) g;
|
||||
output[(x + y * width) * 4 + 2] = (uint8_t) b;
|
||||
output[(x + y * width) * 4 + 3] = 0xFF;
|
||||
|
||||
// EsPrint("\tPixel %d, %d: red %X, green %X, blue %X\n", x, y, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
if (output) {
|
||||
entry->data = output;
|
||||
entry->width = width;
|
||||
entry->height = height;
|
||||
entry->xoff = xoff;
|
||||
entry->yoff = yoff;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontShapeTextDone(EsTextPlan *plan, uint32_t glyphCount, TextGlyphInfo *_glyphInfos, TextGlyphPosition *_glyphPositions) {
|
||||
if (plan->font.type == FONT_TYPE_BITMAP) {
|
||||
(void) glyphCount;
|
||||
Array<TextGlyphInfo> glyphInfos = { .array = _glyphInfos };
|
||||
Array<TextGlyphPosition> glyphPositions = { .array = _glyphPositions };
|
||||
glyphInfos.Free();
|
||||
glyphPositions.Free();
|
||||
}
|
||||
}
|
||||
|
||||
void FontShapeText(EsTextPlan *plan, const char *string, size_t stringBytes,
|
||||
uintptr_t sectionOffsetBytes, size_t sectionCountBytes,
|
||||
TextFeature *features, size_t featureCount,
|
||||
uint32_t *glyphCount, TextGlyphInfo **glyphInfos, TextGlyphPosition **glyphPositions) {
|
||||
uint32_t *glyphCount, TextGlyphInfo **_glyphInfos, TextGlyphPosition **_glyphPositions) {
|
||||
if (plan->font.type == FONT_TYPE_BITMAP) {
|
||||
(void) features;
|
||||
(void) featureCount;
|
||||
(void) stringBytes;
|
||||
|
||||
Array<TextGlyphInfo> glyphInfos = {};
|
||||
Array<TextGlyphPosition> glyphPositions = {};
|
||||
|
||||
Font *font = &plan->font;
|
||||
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
|
||||
|
||||
const char *text = string + sectionOffsetBytes;
|
||||
|
||||
while (text < string + sectionOffsetBytes + sectionCountBytes) {
|
||||
TextGlyphInfo info = {};
|
||||
TextGlyphPosition position = {};
|
||||
info.cluster = text - (string + sectionOffsetBytes);
|
||||
uint32_t codepoint = utf8_value(text, string + sectionOffsetBytes + sectionCountBytes - text, nullptr);
|
||||
if (!codepoint) break;
|
||||
text = utf8_advance(text);
|
||||
|
||||
bool glyphFound = false;
|
||||
uint32_t glyphIndex = 0;
|
||||
ES_MACRO_SEARCH(header->glyphCount, result = codepoint - ((const BitmapFontGlyph *) ((const uint8_t *)
|
||||
font->bitmapData + header->headerBytes + index * header->glyphBytes))->codepoint;, glyphIndex, glyphFound);
|
||||
info.codepoint = glyphFound ? glyphIndex : 0;
|
||||
|
||||
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *)
|
||||
font->bitmapData + header->headerBytes + info.codepoint * header->glyphBytes));
|
||||
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
|
||||
position.x_advance = glyph->xAdvance * FREETYPE_UNIT_SCALE;
|
||||
|
||||
if (glyph->kerningEntryCount && text < string + sectionOffsetBytes + sectionCountBytes) {
|
||||
uint32_t nextCodepoint = utf8_value(text, string + sectionOffsetBytes + sectionCountBytes - text, nullptr);
|
||||
|
||||
for (uintptr_t i = 0; i < glyph->kerningEntryCount; i++) {
|
||||
BitmapFontKerningEntry entry;
|
||||
EsMemoryCopy(&entry, (const uint8_t *) font->bitmapData + glyph->bitsOffset
|
||||
+ bytesPerRow * glyph->bitsHeight + sizeof(BitmapFontKerningEntry) * i, sizeof(BitmapFontKerningEntry));
|
||||
|
||||
if (entry.rightCodepoint == nextCodepoint) {
|
||||
position.x_advance += entry.xOffset * FREETYPE_UNIT_SCALE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glyphInfos.Add(info);
|
||||
glyphPositions.Add(position);
|
||||
}
|
||||
|
||||
*glyphCount = glyphInfos.Length();
|
||||
*_glyphInfos = &glyphInfos[0];
|
||||
*_glyphPositions = &glyphPositions[0];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
hb_buffer_clear_contents(plan->buffer);
|
||||
hb_buffer_set_segment_properties(plan->buffer, &plan->segmentProperties);
|
||||
hb_buffer_add_utf8(plan->buffer, string, stringBytes, sectionOffsetBytes, sectionCountBytes);
|
||||
hb_shape(plan->font.hb, plan->buffer, features, featureCount);
|
||||
*glyphInfos = hb_buffer_get_glyph_infos(plan->buffer, glyphCount);
|
||||
*glyphPositions = hb_buffer_get_glyph_positions(plan->buffer, glyphCount);
|
||||
if (plan->font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
hb_buffer_clear_contents(plan->buffer);
|
||||
hb_buffer_set_segment_properties(plan->buffer, &plan->segmentProperties);
|
||||
hb_buffer_add_utf8(plan->buffer, string, stringBytes, sectionOffsetBytes, sectionCountBytes);
|
||||
hb_shape(plan->font.hb, plan->buffer, features, featureCount);
|
||||
*_glyphInfos = hb_buffer_get_glyph_infos(plan->buffer, glyphCount);
|
||||
*_glyphPositions = hb_buffer_get_glyph_positions(plan->buffer, glyphCount);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -377,9 +537,11 @@ uint32_t FontGetScriptFromCodepoint(uint32_t codepoint, bool *inheritingScript)
|
|||
uint32_t script = hb_unicode_script(unicodeFunctions, codepoint);
|
||||
*inheritingScript = script == HB_SCRIPT_COMMON || script == HB_SCRIPT_INHERITED;
|
||||
return script;
|
||||
#endif
|
||||
|
||||
#else
|
||||
(void) codepoint;
|
||||
*inheritingScript = false;
|
||||
return FALLBACK_SCRIPT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FontInitialiseShaping(EsTextPlan *plan) {
|
||||
|
@ -389,6 +551,8 @@ void FontInitialiseShaping(EsTextPlan *plan) {
|
|||
plan->segmentProperties.direction = (plan->properties.flags & ES_TEXT_PLAN_RTL) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
|
||||
plan->segmentProperties.script = (TextScript) FALLBACK_SCRIPT;
|
||||
plan->segmentProperties.language = hb_language_from_string(plan->properties.cLanguage ?: FALLBACK_SCRIPT_LANGUAGE, -1);
|
||||
#else
|
||||
(void) plan;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -396,6 +560,8 @@ void FontDestroyShaping(EsTextPlan *plan) {
|
|||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
hb_buffer_destroy(plan->buffer);
|
||||
plan->buffer = nullptr;
|
||||
#else
|
||||
(void) plan;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -681,7 +847,7 @@ Font FontGet(EsFont key) {
|
|||
if (entry->files[i]) {
|
||||
int weight = (i % 9) + 1;
|
||||
bool italic = i >= 9;
|
||||
int distance = ((italic != key.italic) ? 10 : 0) + AbsoluteInteger(weight - key.weight);
|
||||
int distance = ((italic != ((key.flags & ES_FONT_ITALIC) ? true : false)) ? 10 : 0) + AbsoluteInteger(weight - key.weight);
|
||||
|
||||
if (distance < matchDistance) {
|
||||
matchDistance = distance;
|
||||
|
@ -691,8 +857,9 @@ Font FontGet(EsFont key) {
|
|||
}
|
||||
|
||||
if (!file) {
|
||||
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
|
||||
return {};
|
||||
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
|
||||
key.family = fontManagement.fallback;
|
||||
return FontGet(key);
|
||||
}
|
||||
|
||||
// EsPrint("Loading font from '%z' (f%d/w%d/i%d).\n", file, key.family, key.weight, key.italic);
|
||||
|
@ -701,15 +868,17 @@ Font FontGet(EsFont key) {
|
|||
void *data = EsFileStoreMap(file, &size, ES_MEMORY_MAP_OBJECT_READ_ONLY);
|
||||
|
||||
if (!data) {
|
||||
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
|
||||
return {};
|
||||
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
|
||||
key.family = fontManagement.fallback;
|
||||
return FontGet(key);
|
||||
}
|
||||
|
||||
Font font = {};
|
||||
|
||||
if (!FontLoad(&font, data, size)) {
|
||||
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
|
||||
return {};
|
||||
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
|
||||
key.family = fontManagement.fallback;
|
||||
return FontGet(key);
|
||||
}
|
||||
|
||||
*fontManagement.loaded.Put(&key) = font;
|
||||
|
@ -724,9 +893,16 @@ void FontDatabaseFree() {
|
|||
for (uintptr_t i = 0; i < fontManagement.loaded.Count(); i++) {
|
||||
// TODO Unmap file store data.
|
||||
Font font = fontManagement.loaded[i];
|
||||
|
||||
if (font.type == FONT_TYPE_BITMAP) {
|
||||
// Nothing to be done.
|
||||
}
|
||||
|
||||
#ifdef USE_FREETYPE_AND_HARFBUZZ
|
||||
hb_font_destroy(font.hb);
|
||||
FT_Done_Face(font.ft);
|
||||
if (font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
|
||||
hb_font_destroy(font.hb);
|
||||
FT_Done_Face(font.ft);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1378,7 +1554,7 @@ TextStyleDifference CompareTextStyles(const EsTextStyle *style1, const EsTextSty
|
|||
if (!style1) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->font.family != style2->font.family) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->font.weight != style2->font.weight) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->font.italic != style2->font.italic) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->font.flags != style2->font.flags) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->size != style2->size) return TEXT_STYLE_NEW_FONT;
|
||||
if (style1->baselineOffset != style2->baselineOffset) return TEXT_STYLE_NEW_SHAPE;
|
||||
if (style1->tracking != style2->tracking) return TEXT_STYLE_NEW_SHAPE;
|
||||
|
@ -1656,6 +1832,8 @@ void TextAddEllipsis(EsTextPlan *plan, int32_t maximumLineWidth, bool needFinalE
|
|||
line->ellipsisPieceIndex = plan->pieces.Length();
|
||||
plan->pieces.Add(piece);
|
||||
}
|
||||
|
||||
FontShapeTextDone(plan, glyphCount, glyphInfos, glyphPositions);
|
||||
}
|
||||
|
||||
void TextItemizeByScript(EsTextPlan *plan, const EsTextRun *runs, size_t runCount, float sizeScaleFactor) {
|
||||
|
@ -1865,6 +2043,8 @@ int32_t TextBuildTextPieces(EsTextPlan *plan, uintptr_t sectionStart, uintptr_t
|
|||
// Go to the next run.
|
||||
|
||||
plan->textRunPosition++;
|
||||
|
||||
FontShapeTextDone(plan, glyphCount, glyphInfos, glyphPositions);
|
||||
}
|
||||
|
||||
plan->textRunPosition--;
|
||||
|
@ -2414,7 +2594,7 @@ void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes,
|
|||
i++; if (i >= inStringBytes || inString[i] == ']') goto parsedFormat;
|
||||
textRun->style.font.weight = inString[i] - '0';
|
||||
} else if (c == 'i' /* italic */) {
|
||||
textRun->style.font.italic = true;
|
||||
textRun->style.font.flags |= ES_FONT_ITALIC;
|
||||
} else if (c == 's' /* size */) {
|
||||
textRun->style.size = 0;
|
||||
|
||||
|
|
|
@ -1307,7 +1307,7 @@ const char *GetConstantString(const char *cKey) {
|
|||
void ThemeStyleCopyInlineMetrics(UIStyle *style) {
|
||||
style->font.family = style->metrics->fontFamily;
|
||||
style->font.weight = style->metrics->fontWeight;
|
||||
style->font.italic = style->metrics->isItalic;
|
||||
style->font.flags = style->metrics->isItalic ? ES_FONT_ITALIC : ES_FLAGS_DEFAULT;
|
||||
style->preferredWidth = style->metrics->preferredWidth;
|
||||
style->preferredHeight = style->metrics->preferredHeight;
|
||||
style->gapMajor = style->metrics->gapMajor;
|
||||
|
|
|
@ -777,7 +777,7 @@ static bool ControlTransferWrapper(KUSBDevice *_device, uint8_t flags, uint8_t r
|
|||
|
||||
void XHCIController::OnPortEnable(uintptr_t port) {
|
||||
uint32_t speed = (RD_REGISTER_PORTSC(port) >> 10) & 0xF;
|
||||
uint32_t maximumPacketSize = speed == SPEED_LOW ? 8 : speed == SPEED_FULL ? 8 : speed == SPEED_HIGH ? 64 : speed == 4 ? SPEED_SUPER : 0;
|
||||
uint32_t maximumPacketSize = speed == SPEED_LOW ? 8 : speed == SPEED_FULL ? 8 : speed == SPEED_HIGH ? 64 : speed == SPEED_SUPER ? 512 : 0;
|
||||
ports[port].speed = speed;
|
||||
ports[port].controlEndpoint.maximumPacketSize = maximumPacketSize;
|
||||
|
||||
|
|
|
@ -155,8 +155,6 @@ struct WindowManager {
|
|||
|
||||
WindowManager windowManager;
|
||||
|
||||
void SendMessageToWindow(Window *window, EsMessage *message);
|
||||
|
||||
#else
|
||||
|
||||
void HIDeviceUpdateIndicators() {
|
||||
|
@ -272,9 +270,14 @@ void SendMessageToWindow(Window *window, EsMessage *message) {
|
|||
window->owner->messageQueue.SendMessage(window->apiWindow, message);
|
||||
}
|
||||
} else if (message->type == ES_MSG_KEY_DOWN || message->type == ES_MSG_KEY_UP) {
|
||||
// TODO Only send certain key messages to the container, like modifiers keys and global shortcuts.
|
||||
window->embed->owner->messageQueue.SendMessage(window->embed->apiWindow, message);
|
||||
window->owner->messageQueue.SendMessage(window->apiWindow, message);
|
||||
|
||||
if (message->keyboard.modifiers & (ES_MODIFIER_CTRL | ES_MODIFIER_ALT | ES_MODIFIER_FLAG | ES_MODIFIER_ALT_GR)) {
|
||||
// Additionally send the keyboard input message to the embed parent
|
||||
// if Ctrl, Alt or Flag were held.
|
||||
// (Otherwise don't, to avoid increasing typing latency.)
|
||||
window->owner->messageQueue.SendMessage(window->apiWindow, message);
|
||||
}
|
||||
} else if (message->type == ES_MSG_MOUSE_EXIT) {
|
||||
window->embed->owner->messageQueue.SendMessage(window->embed->apiWindow, message);
|
||||
window->owner->messageQueue.SendMessage(window->apiWindow, message);
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,25 @@
|
|||
#define BITMAP_FONT_SIGNATURE (0xF67DD870) // (Random number.)
|
||||
|
||||
typedef struct BitmapFontHeader {
|
||||
uint32_t signature;
|
||||
uint16_t glyphCount;
|
||||
uint8_t headerBytes, glyphBytes;
|
||||
uint16_t yAscent, yDescent;
|
||||
uint16_t xEmWidth;
|
||||
uint16_t _unused0;
|
||||
// Followed by glyphCount copies of BitmapFontGlyph, sorted by codepoint.
|
||||
} BitmapFontHeader;
|
||||
|
||||
typedef struct BitmapFontKerningEntry {
|
||||
uint32_t rightCodepoint;
|
||||
int16_t xOffset;
|
||||
uint16_t _unused0;
|
||||
} BitmapFontKerningEntry;
|
||||
|
||||
typedef struct BitmapFontGlyph {
|
||||
uint32_t bitsOffset; // Stored one row after another; each row is padded to a multiple of 8 bits.
|
||||
uint32_t codepoint;
|
||||
int16_t xOrigin, yOrigin, xAdvance;
|
||||
uint16_t kerningEntryCount; // Stored after the bits. Not necessarily aligned!
|
||||
uint16_t bitsWidth, bitsHeight;
|
||||
} BitmapFontGlyph;
|
|
@ -105,6 +105,11 @@ BuildFont fonts[] = {
|
|||
{},
|
||||
} },
|
||||
|
||||
{ "Bitmap Sans", "", "Sans", "Latn", (FontFile []) {
|
||||
{ "4", "Bitmap Sans Regular 9.font" },
|
||||
{},
|
||||
} },
|
||||
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -578,7 +578,7 @@ void BuildDesktop(Application *application) {
|
|||
ExecuteForApp(application, toolchainStrip, "-o", "bin/Stripped Executables/Desktop", "--strip-all", "bin/Desktop");
|
||||
|
||||
for (uintptr_t i = 0; i < arrlenu(fontLines); i++) {
|
||||
if (fontLines[i].key[0] == '.' || 0 == strcmp(fontLines[i].key, "license")) {
|
||||
if ((fontLines[i].key[0] == '.' || 0 == strcmp(fontLines[i].key, "license")) && fontLines[i].value[0]) {
|
||||
snprintf(buffer, sizeof(buffer), "res/Fonts/%s", fontLines[i].value);
|
||||
ADD_BUNDLE_INPUT(strdup(buffer), fontLines[i].value, 16);
|
||||
}
|
||||
|
@ -811,7 +811,7 @@ void OutputSystemConfiguration() {
|
|||
"temporary=0:/" SYSTEM_FOLDER_NAME "/Temporary\n"
|
||||
"default_settings=0:/" SYSTEM_FOLDER_NAME "/Settings\n"
|
||||
"\n[ui_fonts]\n"
|
||||
"fallback=Inter\n"
|
||||
"fallback=Bitmap Sans\n"
|
||||
"sans=Inter\n"
|
||||
"serif=Inter\n"
|
||||
"mono=Hack\n");
|
||||
|
|
|
@ -6,31 +6,12 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../shared/bitmap_font.h"
|
||||
|
||||
#define BITS_WIDTH (50)
|
||||
#define BITS_HEIGHT (50)
|
||||
#define ZOOM (18)
|
||||
|
||||
typedef struct FileHeader {
|
||||
uint16_t glyphCount;
|
||||
uint8_t headerBytes, glyphHeaderBytes;
|
||||
uint16_t yAscent, yDescent;
|
||||
// Followed by glyphCount copies of FileGlyphHeader, sorted by codepoint.
|
||||
} FileHeader;
|
||||
|
||||
typedef struct FileKerningEntry {
|
||||
uint32_t rightCodepoint;
|
||||
int16_t xOffset;
|
||||
uint16_t _unused0;
|
||||
} FileKerningEntry;
|
||||
|
||||
typedef struct FileGlyphHeader {
|
||||
uint32_t bitsOffset; // Stored one row after another; each row is padded to a multiple of 8 bits.
|
||||
uint32_t codepoint;
|
||||
int16_t xOrigin, yOrigin, xAdvance;
|
||||
uint16_t kerningEntryCount; // Stored after the bits. Not necessarily aligned!
|
||||
uint16_t bitsWidth, bitsHeight;
|
||||
} FileGlyphHeader;
|
||||
|
||||
typedef struct Kerning {
|
||||
int number, xOffset;
|
||||
} Kerning;
|
||||
|
@ -49,34 +30,36 @@ UIWindow *window;
|
|||
UITabPane *tabPane;
|
||||
UIElement *editor;
|
||||
UIElement *kerning;
|
||||
UITextbox *previewText, *yAscentTextbox, *yDescentTextbox;
|
||||
UITextbox *previewText, *yAscentTextbox, *yDescentTextbox, *xEmWidthTextbox;
|
||||
UIScrollBar *kerningHScroll, *kerningVScroll;
|
||||
Glyph *glyphsArray;
|
||||
size_t glyphCount;
|
||||
intptr_t selectedGlyph = -1;
|
||||
int selectedPixelX, selectedPixelY;
|
||||
int selectedPairX, selectedPairY, selectedPairI, selectedPairJ;
|
||||
int yAscent, yDescent;
|
||||
int yAscent, yDescent, xEmWidth;
|
||||
char *path;
|
||||
|
||||
void Save(void *cp) {
|
||||
FILE *f = fopen(path, "wb");
|
||||
|
||||
if (f) {
|
||||
FileHeader header = {
|
||||
BitmapFontHeader header = {
|
||||
.signature = BITMAP_FONT_SIGNATURE,
|
||||
.glyphCount = glyphCount,
|
||||
.headerBytes = sizeof(FileHeader),
|
||||
.glyphHeaderBytes = sizeof(FileGlyphHeader),
|
||||
.headerBytes = sizeof(BitmapFontHeader),
|
||||
.glyphBytes = sizeof(BitmapFontGlyph),
|
||||
.yAscent = yAscent,
|
||||
.yDescent = yDescent,
|
||||
.xEmWidth = xEmWidth,
|
||||
};
|
||||
|
||||
fwrite(&header, 1, sizeof(header), f);
|
||||
|
||||
uint32_t bitsOffset = sizeof(FileHeader) + glyphCount * sizeof(FileGlyphHeader);
|
||||
uint32_t bitsOffset = sizeof(BitmapFontHeader) + glyphCount * sizeof(BitmapFontGlyph);
|
||||
|
||||
for (uintptr_t i = 0; i < glyphCount; i++) {
|
||||
FileGlyphHeader glyphHeader = {
|
||||
BitmapFontGlyph glyphHeader = {
|
||||
.bitsOffset = bitsOffset,
|
||||
.codepoint = glyphsArray[i].number,
|
||||
.xOrigin = glyphsArray[i].xOrigin - glyphsArray[i].x0,
|
||||
|
@ -88,7 +71,7 @@ void Save(void *cp) {
|
|||
};
|
||||
|
||||
fwrite(&glyphHeader, 1, sizeof(glyphHeader), f);
|
||||
bitsOffset += ((glyphHeader.bitsWidth + 7) >> 3) * glyphHeader.bitsHeight + sizeof(FileKerningEntry) * glyphHeader.kerningEntryCount;
|
||||
bitsOffset += ((glyphHeader.bitsWidth + 7) >> 3) * glyphHeader.bitsHeight + sizeof(BitmapFontKerningEntry) * glyphHeader.kerningEntryCount;
|
||||
}
|
||||
|
||||
for (uintptr_t i = 0; i < glyphCount; i++) {
|
||||
|
@ -109,10 +92,10 @@ void Save(void *cp) {
|
|||
}
|
||||
|
||||
for (uintptr_t i = 0; i < g->kerningCount; i++) {
|
||||
FileKerningEntry entry = { 0 };
|
||||
BitmapFontKerningEntry entry = { 0 };
|
||||
entry.rightCodepoint = g->kerningArray[i].number;
|
||||
entry.xOffset = g->kerningArray[i].xOffset;
|
||||
fwrite(&entry, 1, sizeof(FileKerningEntry), f);
|
||||
fwrite(&entry, 1, sizeof(BitmapFontKerningEntry), f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,18 +109,18 @@ void Load() {
|
|||
FILE *f = fopen(path, "rb");
|
||||
|
||||
if (f) {
|
||||
FileHeader header = { 0 };
|
||||
fread(&header, 1, 4, f);
|
||||
BitmapFontHeader header = { 0 };
|
||||
fread(&header, 1, 8, f);
|
||||
if (ferror(f)) goto end;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(&header, 1, header.headerBytes > sizeof(FileHeader) ? sizeof(FileHeader) : header.headerBytes, f);
|
||||
fread(&header, 1, header.headerBytes > sizeof(BitmapFontHeader) ? sizeof(BitmapFontHeader) : header.headerBytes, f);
|
||||
fseek(f, header.headerBytes, SEEK_SET);
|
||||
glyphCount = header.glyphCount;
|
||||
glyphsArray = (Glyph *) calloc(glyphCount, sizeof(Glyph));
|
||||
if (!glyphsArray) goto end;
|
||||
|
||||
for (uintptr_t i = 0; i < glyphCount; i++) {
|
||||
FileGlyphHeader glyphHeader;
|
||||
BitmapFontGlyph glyphHeader;
|
||||
fread(&glyphHeader, 1, sizeof(glyphHeader), f);
|
||||
if (ferror(f)) goto end;
|
||||
if (glyphHeader.bitsWidth >= BITS_WIDTH || glyphHeader.bitsHeight >= BITS_HEIGHT) goto end;
|
||||
|
@ -168,8 +151,8 @@ void Load() {
|
|||
}
|
||||
|
||||
for (uintptr_t i = 0; i < g->kerningCount; i++) {
|
||||
FileKerningEntry entry;
|
||||
fread(&entry, 1, sizeof(FileKerningEntry), f);
|
||||
BitmapFontKerningEntry entry;
|
||||
fread(&entry, 1, sizeof(BitmapFontKerningEntry), f);
|
||||
if (ferror(f)) goto end;
|
||||
g->kerningArray[i].number = entry.rightCodepoint;
|
||||
g->kerningArray[i].xOffset = entry.xOffset;
|
||||
|
@ -185,6 +168,8 @@ void Load() {
|
|||
UITextboxReplace(yAscentTextbox, buffer, -1, true);
|
||||
snprintf(buffer, sizeof(buffer), "%d", header.yDescent);
|
||||
UITextboxReplace(yDescentTextbox, buffer, -1, true);
|
||||
snprintf(buffer, sizeof(buffer), "%d", header.xEmWidth);
|
||||
UITextboxReplace(xEmWidthTextbox, buffer, -1, true);
|
||||
|
||||
end:;
|
||||
|
||||
|
@ -598,6 +583,11 @@ int main(int argc, char **argv) {
|
|||
yDescentTextbox->e.cp = &yDescent;
|
||||
yDescentTextbox->e.messageUser = NumberTextboxMessage;
|
||||
UILabelCreate(0, 0, "The sum of the ascent and descent determine the line height.", -1);
|
||||
|
||||
UILabelCreate(0, 0, "X em width:", -1);
|
||||
xEmWidthTextbox = UITextboxCreate(&UIPanelCreate(0, UI_PANEL_HORIZONTAL)->e, 0);
|
||||
xEmWidthTextbox->e.cp = &xEmWidth;
|
||||
xEmWidthTextbox->e.messageUser = NumberTextboxMessage;
|
||||
UIParentPop();
|
||||
UIParentPop();
|
||||
|
||||
|
|
Loading…
Reference in New Issue