mirror of https://github.com/mgba-emu/mgba.git
Util: Add text metrics functions
This commit is contained in:
parent
467768d414
commit
7fa4acf1c7
|
@ -10,6 +10,8 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/geometry.h>
|
||||
|
||||
#ifdef COLOR_16_BIT
|
||||
typedef uint16_t mColor;
|
||||
#define BYTES_PER_PIXEL 2
|
||||
|
@ -112,6 +114,16 @@ struct mPainter {
|
|||
uint32_t fillColor;
|
||||
};
|
||||
|
||||
#ifdef USE_FREETYPE
|
||||
#define mFONT_FRACT_BITS 6
|
||||
|
||||
struct mTextRunMetrics {
|
||||
int height;
|
||||
int baseline;
|
||||
int width;
|
||||
};
|
||||
#endif
|
||||
|
||||
enum mAlignment {
|
||||
mALIGN_LEFT = 0x01,
|
||||
mALIGN_HCENTER = 0x02,
|
||||
|
@ -170,7 +182,8 @@ void mFontDestroy(struct mFont*);
|
|||
|
||||
unsigned mFontSize(const struct mFont*);
|
||||
void mFontSetSize(struct mFont*, unsigned px);
|
||||
int mFontSpanWidth(struct mFont*, const char* text);
|
||||
const char* mFontRunMetrics(struct mFont*, const char* text, struct mTextRunMetrics* out);
|
||||
void mFontTextBoxSize(struct mFont*, const char* text, int lineSpacing, struct mSize* out);
|
||||
|
||||
void mPainterDrawText(struct mPainter*, const char* text, int x, int y, enum mAlignment);
|
||||
#endif
|
||||
|
|
|
@ -11,24 +11,24 @@ state.painter:setStrokeColor(0xFF000000)
|
|||
state.painter:setBlend(true)
|
||||
|
||||
state.painter:setFillColor(0xFFFFFFFF)
|
||||
state.painter:drawText("Top left", 0, 0, C.ALIGN.TOP | C.ALIGN.LEFT)
|
||||
state.painter:drawText("Top\nleft", 0, 0, C.ALIGN.TOP | C.ALIGN.LEFT)
|
||||
state.painter:setFillColor(0xFF00FFFF)
|
||||
state.painter:drawText("Top center", state.width / 2, 0, C.ALIGN.TOP | C.ALIGN.HCENTER)
|
||||
state.painter:drawText("Top\ncenter", state.width / 2, 0, C.ALIGN.TOP | C.ALIGN.HCENTER)
|
||||
state.painter:setFillColor(0xFFFF00FF)
|
||||
state.painter:drawText("Top right", state.width, 0, C.ALIGN.TOP | C.ALIGN.RIGHT)
|
||||
state.painter:drawText("Top\nright", state.width, 0, C.ALIGN.TOP | C.ALIGN.RIGHT)
|
||||
|
||||
state.painter:setFillColor(0xFFFFFF00)
|
||||
state.painter:drawText("Center left", 0, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.LEFT)
|
||||
state.painter:drawText("Center\nleft", 0, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.LEFT)
|
||||
state.painter:setFillColor(0xFFFF0000)
|
||||
state.painter:drawText("Center", state.width / 2, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.HCENTER)
|
||||
state.painter:drawText("Center\ncenter", state.width / 2, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.HCENTER)
|
||||
state.painter:setFillColor(0xFF00FF00)
|
||||
state.painter:drawText("Center right", state.width, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.RIGHT)
|
||||
state.painter:drawText("Center\nright", state.width, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.RIGHT)
|
||||
|
||||
state.painter:setFillColor(0xFF0000FF)
|
||||
state.painter:drawText("Bottom left", 0, state.height, C.ALIGN.BOTTOM | C.ALIGN.LEFT)
|
||||
state.painter:drawText("Bottom\nleft", 0, state.height, C.ALIGN.BOTTOM | C.ALIGN.LEFT)
|
||||
state.painter:setFillColor(0xFF808080)
|
||||
state.painter:drawText("Bottom center", state.width / 2, state.height, C.ALIGN.BOTTOM | C.ALIGN.HCENTER)
|
||||
state.painter:drawText("Bottom\ncenter", state.width / 2, state.height, C.ALIGN.BOTTOM | C.ALIGN.HCENTER)
|
||||
state.painter:setStrokeColor(0xFFFFFFFF)
|
||||
state.painter:setFillColor(0xFF000000)
|
||||
state.painter:drawText("Bottom right", state.width, state.height, C.ALIGN.BOTTOM | C.ALIGN.RIGHT)
|
||||
state.painter:drawText("Bottom\nright", state.width, state.height, C.ALIGN.BOTTOM | C.ALIGN.RIGHT)
|
||||
state.overlay:update()
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
local state = {}
|
||||
state.width = canvas:screenWidth()
|
||||
state.height = canvas:screenHeight()
|
||||
state.overlay = canvas:newLayer(state.width, state.height)
|
||||
state.painter = image.newPainter(state.overlay.image)
|
||||
state.painter:loadFont(script.dir .. "/SourceSans3-Regular.otf")
|
||||
state.painter:setFontSize(10.5)
|
||||
state.painter:setFill(true)
|
||||
state.painter:setBlend(true)
|
||||
|
||||
state.text = "Sphinx of black quartz judge my vow"
|
||||
state.metrics = state.painter:textRunMetrics(state.text)
|
||||
state.painter:setFillColor(0x80FF0000)
|
||||
state.painter:drawRectangle(0, 0, state.metrics:width(), state.metrics:ascender())
|
||||
state.painter:setFillColor(0x800000FF)
|
||||
state.painter:drawRectangle(0, state.metrics:ascender(), state.metrics:width(), state.metrics:descender())
|
||||
state.painter:setFillColor(0xFFFFFFFF)
|
||||
state.painter:drawText(state.text, 0, 0)
|
||||
|
||||
state.overlay:update()
|
|
@ -13,6 +13,9 @@ struct mScriptPainter {
|
|||
|
||||
mSCRIPT_DECLARE_STRUCT(mPainter);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptPainter);
|
||||
#ifdef USE_FREETYPE
|
||||
mSCRIPT_DECLARE_STRUCT(mTextRunMetrics);
|
||||
#endif
|
||||
|
||||
static struct mScriptValue* _mImageNew(unsigned width, unsigned height) {
|
||||
// For various reasons, it's probably a good idea to limit the maximum image size scripts can make
|
||||
|
@ -140,15 +143,27 @@ void _mPainterSetFontSize(struct mPainter* painter, float pt) {
|
|||
if (!painter->font) {
|
||||
return;
|
||||
}
|
||||
mFontSetSize(painter->font, pt * 64);
|
||||
mFontSetSize(painter->font, pt * (1 << mFONT_FRACT_BITS));
|
||||
}
|
||||
|
||||
struct mScriptValue* _mPainterTextRunMetrics(struct mPainter* painter, const char* text) {
|
||||
struct mTextRunMetrics* metrics = malloc(sizeof(*metrics));
|
||||
mFontRunMetrics(painter->font, text, metrics);
|
||||
|
||||
float _mPainterTextSpanWidth(struct mPainter* painter, const char* text) {
|
||||
if (!painter->font) {
|
||||
return 0;
|
||||
}
|
||||
return mFontSpanWidth(painter->font, text) / 64.f;
|
||||
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mTextRunMetrics));
|
||||
result->value.opaque = metrics;
|
||||
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct mScriptValue* _mPainterTextBoxSize(struct mPainter* painter, const char* text) {
|
||||
struct mSize* size = malloc(sizeof(*size));
|
||||
mFontTextBoxSize(painter->font, text, 0, size);
|
||||
|
||||
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize));
|
||||
result->value.opaque = size;
|
||||
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -185,10 +200,19 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawLine, mPainterDrawLine, 4, S32,
|
|||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawCircle, mPainterDrawCircle, 3, S32, x, S32, y, S32, diameter);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawMask, mPainterDrawMask, 3, CS(mImage), mask, S32, x, S32, y);
|
||||
#ifdef USE_FREETYPE
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, drawText, mPainterDrawText, 4, CHARP, text, S32, x, S32, y, S32, alignment);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mPainter, drawText, mPainterDrawText, 4, CHARP, text, S32, x, S32, y, S32, alignment);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, loadFont, _mPainterLoadFont, 1, CHARP, path);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, setFontSize, _mPainterSetFontSize, 1, U32, pt);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mPainter, F32, textSpanWidth, _mPainterTextSpanWidth, 1, CHARP, text);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mPainter, setFontSize, _mPainterSetFontSize, 1, F32, pt);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mPainter, W(mTextRunMetrics), textRunMetrics, _mPainterTextRunMetrics, 1, CHARP, text);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mPainter, W(mSize), textBoxSize, _mPainterTextBoxSize, 1, CHARP, text);
|
||||
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mPainter, drawText)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_S32(mALIGN_TOP | mALIGN_LEFT)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
#endif
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mPainter)
|
||||
|
@ -225,8 +249,16 @@ mSCRIPT_DEFINE_STRUCT(mPainter)
|
|||
mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, loadFont)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the font size")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, setFontSize)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the pixel width of a span of text in the current font")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, textSpanWidth)
|
||||
mSCRIPT_DEFINE_DOCSTRING(
|
||||
"Get the struct::mTextRunMetrics for the first line of a given string rendered in the current "
|
||||
"font. If you want the bounding box for multiple lines, use struct::mPainter.textBoxSize instead."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, textRunMetrics)
|
||||
mSCRIPT_DEFINE_DOCSTRING(
|
||||
"Get the bounding box size for the given string rendered in the current font. This "
|
||||
"will take into account line breaks, unlike struct::mPainter.textRunMetrics."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mPainter, textBoxSize)
|
||||
#endif
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
|
@ -240,6 +272,43 @@ mSCRIPT_DEFINE_STRUCT(mScriptPainter)
|
|||
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptPainter, S(mPainter), _painter)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
#ifdef USE_FREETYPE
|
||||
float _mTextRunHeight(const struct mTextRunMetrics* metrics) {
|
||||
return metrics->height / (float) (1 << mFONT_FRACT_BITS);
|
||||
}
|
||||
|
||||
float _mTextRunWidth(const struct mTextRunMetrics* metrics) {
|
||||
return metrics->width / (float) (1 << mFONT_FRACT_BITS);
|
||||
}
|
||||
|
||||
float _mTextRunDescender(const struct mTextRunMetrics* metrics) {
|
||||
return metrics->baseline / (float) (1 << mFONT_FRACT_BITS);
|
||||
}
|
||||
|
||||
float _mTextRunAscender(const struct mTextRunMetrics* metrics) {
|
||||
return (metrics->height - metrics->baseline) / (float) (1 << mFONT_FRACT_BITS);
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mTextRunMetrics, F32, height, _mTextRunHeight, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mTextRunMetrics, F32, width, _mTextRunWidth, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mTextRunMetrics, F32, descender, _mTextRunDescender, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mTextRunMetrics, F32, ascender, _mTextRunAscender, 0);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mTextRunMetrics)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"Metrics for the size of a run of text. Generally, a run will represent up to a single line of text."
|
||||
)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the height of the run of text, in pixels")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mTextRunMetrics, height)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the width of the run of text, in pixels")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mTextRunMetrics, width)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the distance from the baseline to the bottom of the line, in pixels")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mTextRunMetrics, descender)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the distance from the baseline to the top of the line, in pixels")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mTextRunMetrics, ascender)
|
||||
mSCRIPT_DEFINE_END;
|
||||
#endif
|
||||
|
||||
void mScriptContextAttachImage(struct mScriptContext* context) {
|
||||
mScriptContextExportNamespace(context, "image", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_KV_PAIR(new, &mImageNew_Binding),
|
||||
|
|
|
@ -56,7 +56,7 @@ struct mFont* mFontOpen(const char* path) {
|
|||
|
||||
struct mFont* font = calloc(1, sizeof(*font));
|
||||
font->face = face;
|
||||
mFontSetSize(font, 8 * 64);
|
||||
mFontSetSize(font, 8 << mFONT_FRACT_BITS);
|
||||
return font;
|
||||
}
|
||||
|
||||
|
@ -80,31 +80,121 @@ void mFontSetSize(struct mFont* font, unsigned pt) {
|
|||
}
|
||||
|
||||
int mFontSpanWidth(struct mFont* font, const char* text) {
|
||||
struct mTextRunMetrics metrics;
|
||||
mFontRunMetrics(font, text, &metrics);
|
||||
return metrics.width;
|
||||
}
|
||||
|
||||
const char* mFontRunMetrics(struct mFont* font, const char* text, struct mTextRunMetrics* out) {
|
||||
FT_Face face = font->face;
|
||||
out->height = face->size->metrics.ascender - face->size->metrics.descender;
|
||||
out->baseline = -face->size->metrics.descender;
|
||||
|
||||
uint32_t lastGlyph = 0;
|
||||
int width = 0;
|
||||
|
||||
while (*text) {
|
||||
uint32_t glyph = utf8Char((const char**) &text, NULL);
|
||||
uint32_t codepoint = utf8Char((const char**) &text, NULL);
|
||||
|
||||
if (FT_Load_Char(face, glyph, FT_LOAD_DEFAULT)) {
|
||||
if (codepoint == '\n') {
|
||||
out->width = width;
|
||||
return text;
|
||||
}
|
||||
|
||||
if (FT_Load_Char(face, codepoint, FT_LOAD_DEFAULT)) {
|
||||
lastGlyph = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_Vector kerning = {0};
|
||||
FT_Get_Kerning(face, lastGlyph, glyph, FT_KERNING_DEFAULT, &kerning);
|
||||
FT_Get_Kerning(face, lastGlyph, codepoint, FT_KERNING_DEFAULT, &kerning);
|
||||
width += kerning.x;
|
||||
width += face->glyph->advance.x;
|
||||
|
||||
lastGlyph = glyph;
|
||||
lastGlyph = codepoint;
|
||||
}
|
||||
|
||||
return width;
|
||||
out->width = width;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mFontTextBoxSize(struct mFont* font, const char* text, int lineSpacing, struct mSize* out) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
do {
|
||||
struct mTextRunMetrics metrics;
|
||||
text = mFontRunMetrics(font, text, &metrics);
|
||||
if (metrics.width > width) {
|
||||
width = metrics.width;
|
||||
}
|
||||
height += metrics.height + lineSpacing;
|
||||
} while (text);
|
||||
|
||||
out->width = width;
|
||||
out->height = height;
|
||||
}
|
||||
|
||||
static const char* mPainterDrawTextRun(struct mPainter* painter, const char* text, int x, int y, enum mAlignment alignment, uint8_t sdfThreshold) {
|
||||
FT_Face face = painter->font->face;
|
||||
struct mTextRunMetrics metrics;
|
||||
mFontRunMetrics(painter->font, text, &metrics);
|
||||
|
||||
switch (alignment & mALIGN_HORIZONTAL) {
|
||||
case mALIGN_LEFT:
|
||||
default:
|
||||
break;
|
||||
case mALIGN_HCENTER:
|
||||
x -= metrics.width >> 1;
|
||||
break;
|
||||
case mALIGN_RIGHT:
|
||||
x -= metrics.width;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t lastGlyph = 0;
|
||||
while (*text) {
|
||||
uint32_t codepoint = utf8Char((const char**) &text, NULL);
|
||||
|
||||
if (codepoint == '\n') {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (FT_Load_Char(face, codepoint, FT_LOAD_DEFAULT)) {
|
||||
lastGlyph = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FT_Render_Glyph(face->glyph, sdfThreshold ? FT_RENDER_MODE_SDF : FT_RENDER_MODE_NORMAL)) {
|
||||
lastGlyph = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
struct mImage image;
|
||||
_makeTemporaryImage(&image, &face->glyph->bitmap);
|
||||
|
||||
FT_Vector kerning = {0};
|
||||
FT_Get_Kerning(face, lastGlyph, codepoint, FT_KERNING_DEFAULT, &kerning);
|
||||
x += kerning.x;
|
||||
y += kerning.y;
|
||||
|
||||
if (sdfThreshold) {
|
||||
mPainterDrawSDFMask(painter, &image, (x >> 6) + face->glyph->bitmap_left, (y >> 6) - face->glyph->bitmap_top, 0x70);
|
||||
} else {
|
||||
mPainterDrawMask(painter, &image, (x >> 6) + face->glyph->bitmap_left, (y >> 6) - face->glyph->bitmap_top);
|
||||
}
|
||||
x += face->glyph->advance.x;
|
||||
y += face->glyph->advance.y;
|
||||
|
||||
lastGlyph = codepoint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mPainterDrawText(struct mPainter* painter, const char* text, int x, int y, enum mAlignment alignment) {
|
||||
FT_Face face = painter->font->face;
|
||||
uint32_t lastGlyph = 0;
|
||||
struct mSize size;
|
||||
mFontTextBoxSize(painter->font, text, 0, &size);
|
||||
|
||||
x <<= 6;
|
||||
y <<= 6;
|
||||
|
@ -116,89 +206,33 @@ void mPainterDrawText(struct mPainter* painter, const char* text, int x, int y,
|
|||
case mALIGN_BASELINE:
|
||||
break;
|
||||
case mALIGN_VCENTER:
|
||||
y += face->size->metrics.ascender - face->size->metrics.height / 2;
|
||||
y += face->size->metrics.ascender - size.height / 2;
|
||||
break;
|
||||
case mALIGN_BOTTOM:
|
||||
default:
|
||||
y += face->size->metrics.descender;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (alignment & mALIGN_HORIZONTAL) {
|
||||
case mALIGN_LEFT:
|
||||
default:
|
||||
break;
|
||||
case mALIGN_HCENTER:
|
||||
x -= mFontSpanWidth(painter->font, text) >> 1;
|
||||
break;
|
||||
case mALIGN_RIGHT:
|
||||
x -= mFontSpanWidth(painter->font, text);
|
||||
y += face->size->metrics.ascender - size.height;
|
||||
break;
|
||||
}
|
||||
|
||||
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 11
|
||||
if (painter->strokeWidth) {
|
||||
int xx = x;
|
||||
int yy = y;
|
||||
const char* ltext = text;
|
||||
uint32_t fillColor = painter->fillColor;
|
||||
painter->fillColor = painter->strokeColor;
|
||||
while (*ltext) {
|
||||
uint32_t glyph = utf8Char((const char**) <ext, NULL);
|
||||
|
||||
if (FT_Load_Char(face, glyph, FT_LOAD_DEFAULT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct mImage image;
|
||||
_makeTemporaryImage(&image, &face->glyph->bitmap);
|
||||
|
||||
FT_Vector kerning = {0};
|
||||
FT_Get_Kerning(face, lastGlyph, glyph, FT_KERNING_DEFAULT, &kerning);
|
||||
xx += kerning.x;
|
||||
yy += kerning.y;
|
||||
|
||||
mPainterDrawSDFMask(painter, &image, (xx >> 6) + face->glyph->bitmap_left, (yy >> 6) - face->glyph->bitmap_top, 0x70);
|
||||
xx += face->glyph->advance.x;
|
||||
yy += face->glyph->advance.y;
|
||||
|
||||
lastGlyph = glyph;
|
||||
}
|
||||
do {
|
||||
ltext = mPainterDrawTextRun(painter, ltext, x, yy, alignment, 0x70);
|
||||
yy += face->size->metrics.height;
|
||||
} while (ltext);
|
||||
|
||||
painter->fillColor = fillColor;
|
||||
lastGlyph = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (*text) {
|
||||
uint32_t glyph = utf8Char((const char**) &text, NULL);
|
||||
|
||||
if (FT_Load_Char(face, glyph, FT_LOAD_DEFAULT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct mImage image;
|
||||
_makeTemporaryImage(&image, &face->glyph->bitmap);
|
||||
|
||||
FT_Vector kerning = {0};
|
||||
FT_Get_Kerning(face, lastGlyph, glyph, FT_KERNING_DEFAULT, &kerning);
|
||||
x += kerning.x;
|
||||
y += kerning.y;
|
||||
|
||||
mPainterDrawMask(painter, &image, (x >> 6) + face->glyph->bitmap_left, (y >> 6) - face->glyph->bitmap_top);
|
||||
x += face->glyph->advance.x;
|
||||
y += face->glyph->advance.y;
|
||||
|
||||
lastGlyph = glyph;
|
||||
}
|
||||
do {
|
||||
text = mPainterDrawTextRun(painter, text, x, y, alignment, 0);
|
||||
y += face->size->metrics.height;
|
||||
} while (text);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue