mirror of https://github.com/mgba-emu/mgba.git
Util: Use FreeType's stroker instead of an SDF
This commit is contained in:
parent
7fa4acf1c7
commit
9a4c4e720f
|
@ -171,7 +171,6 @@ void mPainterDrawRectangle(struct mPainter*, int x, int y, int width, int height
|
|||
void mPainterDrawLine(struct mPainter*, int x1, int y1, int x2, int y2);
|
||||
void mPainterDrawCircle(struct mPainter*, int x, int y, int diameter);
|
||||
void mPainterDrawMask(struct mPainter*, const struct mImage* mask, int x, int y);
|
||||
void mPainterDrawSDFMask(struct mPainter*, const struct mImage* mask, int x, int y, uint8_t upper);
|
||||
|
||||
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to);
|
||||
uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to);
|
||||
|
|
|
@ -6,28 +6,36 @@ state.painter = image.newPainter(state.overlay.image)
|
|||
state.painter:loadFont(script.dir .. "/SourceSans3-Regular.otf")
|
||||
state.painter:setFontSize(9)
|
||||
state.painter:setFill(true)
|
||||
state.painter:setStrokeWidth(1)
|
||||
state.painter:setStrokeColor(0xFF000000)
|
||||
state.painter:setBlend(true)
|
||||
|
||||
state.painter:setStrokeWidth(1)
|
||||
state.painter:setFillColor(0xFFFFFFFF)
|
||||
state.painter:drawText("Top\nleft", 0, 0, C.ALIGN.TOP | C.ALIGN.LEFT)
|
||||
state.painter:setStrokeWidth(2)
|
||||
state.painter:setFillColor(0xFF00FFFF)
|
||||
state.painter:drawText("Top\ncenter", state.width / 2, 0, C.ALIGN.TOP | C.ALIGN.HCENTER)
|
||||
state.painter:setStrokeWidth(1)
|
||||
state.painter:setFillColor(0xFFFF00FF)
|
||||
state.painter:drawText("Top\nright", state.width, 0, C.ALIGN.TOP | C.ALIGN.RIGHT)
|
||||
|
||||
state.painter:setStrokeWidth(2)
|
||||
state.painter:setFillColor(0xFFFFFF00)
|
||||
state.painter:drawText("Center\nleft", 0, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.LEFT)
|
||||
state.painter:setStrokeWidth(3)
|
||||
state.painter:setFillColor(0xFFFF0000)
|
||||
state.painter:drawText("Center\ncenter", state.width / 2, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.HCENTER)
|
||||
state.painter:setStrokeWidth(2)
|
||||
state.painter:setFillColor(0xFF00FF00)
|
||||
state.painter:drawText("Center\nright", state.width, state.height / 2, C.ALIGN.VCENTER | C.ALIGN.RIGHT)
|
||||
|
||||
state.painter:setStrokeWidth(1)
|
||||
state.painter:setFillColor(0xFF0000FF)
|
||||
state.painter:drawText("Bottom\nleft", 0, state.height, C.ALIGN.BOTTOM | C.ALIGN.LEFT)
|
||||
state.painter:setStrokeWidth(2)
|
||||
state.painter:setFillColor(0xFF808080)
|
||||
state.painter:drawText("Bottom\ncenter", state.width / 2, state.height, C.ALIGN.BOTTOM | C.ALIGN.HCENTER)
|
||||
state.painter:setStrokeWidth(1)
|
||||
state.painter:setStrokeColor(0xFFFFFFFF)
|
||||
state.painter:setFillColor(0xFF000000)
|
||||
state.painter:drawText("Bottom\nright", state.width, state.height, C.ALIGN.BOTTOM | C.ALIGN.RIGHT)
|
||||
|
|
|
@ -904,40 +904,6 @@ void mPainterDrawMask(struct mPainter* painter, const struct mImage* mask, int x
|
|||
}
|
||||
}
|
||||
|
||||
void mPainterDrawSDFMask(struct mPainter* painter, const struct mImage* mask, int x, int y, uint8_t upper) {
|
||||
if (mask->format != mCOLOR_L8) {
|
||||
return;
|
||||
}
|
||||
|
||||
COMPOSITE_BOUNDS_INIT(mask, painter->backing);
|
||||
|
||||
for (y = 0; y < srcRect.height; ++y) {
|
||||
uintptr_t dstPixel = (uintptr_t) PIXEL(painter->backing, dstStartX, dstStartY + y);
|
||||
uintptr_t maskPixel = (uintptr_t) PIXEL(mask, srcStartX, srcStartY + y);
|
||||
for (x = 0; x < srcRect.width; ++x, dstPixel += painter->backing->depth, maskPixel += mask->depth) {
|
||||
uint32_t color;
|
||||
GET_PIXEL(color, maskPixel, mask->depth);
|
||||
if (color >= upper) {
|
||||
color = painter->fillColor;
|
||||
} else if (color + 0x10 > upper) {
|
||||
// TODO: Make this spread customizable without too much perf hit
|
||||
color = ((color + 0x10 - upper) * 0x10) << 24;
|
||||
color |= painter->fillColor & 0x00FFFFFF;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (painter->blend || painter->fillColor < 0xFF000000) {
|
||||
uint32_t current;
|
||||
GET_PIXEL(current, dstPixel, painter->backing->depth);
|
||||
current = mColorConvert(current, painter->backing->format, mCOLOR_ARGB8);
|
||||
color = mColorMixARGB8(color, current);
|
||||
}
|
||||
color = mColorConvert(color, mCOLOR_ARGB8, painter->backing->format);
|
||||
PUT_PIXEL(color, dstPixel, painter->backing->depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to) {
|
||||
if (from == to) {
|
||||
return color;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_MODULE_H
|
||||
#include FT_STROKER_H
|
||||
|
||||
#define DPI 100
|
||||
|
||||
|
@ -20,6 +21,7 @@ static FT_Library library;
|
|||
|
||||
struct mFont {
|
||||
FT_Face face;
|
||||
FT_Stroker stroker;
|
||||
unsigned emHeight;
|
||||
};
|
||||
|
||||
|
@ -43,10 +45,6 @@ struct mFont* mFontOpen(const char* path) {
|
|||
if (FT_Init_FreeType(&library)) {
|
||||
return NULL;
|
||||
}
|
||||
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 11
|
||||
FT_Int spread = 5;
|
||||
FT_Property_Set(library, "sdf", "spread", &spread);
|
||||
#endif
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
|
@ -54,14 +52,22 @@ struct mFont* mFontOpen(const char* path) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
FT_Stroker stroker;
|
||||
if (FT_Stroker_New(library, &stroker)) {
|
||||
FT_Done_Face(face);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mFont* font = calloc(1, sizeof(*font));
|
||||
font->face = face;
|
||||
font->stroker = stroker;
|
||||
mFontSetSize(font, 8 << mFONT_FRACT_BITS);
|
||||
return font;
|
||||
}
|
||||
|
||||
void mFontDestroy(struct mFont* font) {
|
||||
FT_Done_Face(font->face);
|
||||
FT_Stroker_Done(font->stroker);
|
||||
free(font);
|
||||
|
||||
size_t opened = --libraryOpen;
|
||||
|
@ -134,7 +140,7 @@ void mFontTextBoxSize(struct mFont* font, const char* text, int lineSpacing, str
|
|||
out->height = height;
|
||||
}
|
||||
|
||||
static const char* mPainterDrawTextRun(struct mPainter* painter, const char* text, int x, int y, enum mAlignment alignment, uint8_t sdfThreshold) {
|
||||
static const char* mPainterDrawTextRun(struct mPainter* painter, const char* text, int x, int y, enum mAlignment alignment, bool stroke) {
|
||||
FT_Face face = painter->font->face;
|
||||
struct mTextRunMetrics metrics;
|
||||
mFontRunMetrics(painter->font, text, &metrics);
|
||||
|
@ -151,7 +157,7 @@ static const char* mPainterDrawTextRun(struct mPainter* painter, const char* tex
|
|||
break;
|
||||
}
|
||||
|
||||
uint32_t lastGlyph = 0;
|
||||
uint32_t lastCodepoint = 0;
|
||||
while (*text) {
|
||||
uint32_t codepoint = utf8Char((const char**) &text, NULL);
|
||||
|
||||
|
@ -160,32 +166,62 @@ static const char* mPainterDrawTextRun(struct mPainter* painter, const char* tex
|
|||
}
|
||||
|
||||
if (FT_Load_Char(face, codepoint, FT_LOAD_DEFAULT)) {
|
||||
lastGlyph = 0;
|
||||
lastCodepoint = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FT_Render_Glyph(face->glyph, sdfThreshold ? FT_RENDER_MODE_SDF : FT_RENDER_MODE_NORMAL)) {
|
||||
lastGlyph = 0;
|
||||
continue;
|
||||
FT_Bitmap* bitmap;
|
||||
FT_Glyph glyph = NULL;
|
||||
int top;
|
||||
int left;
|
||||
|
||||
if (stroke) {
|
||||
FT_Stroker_Set(painter->font->stroker, painter->strokeWidth << mFONT_FRACT_BITS, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
|
||||
|
||||
FT_Get_Glyph(face->glyph, &glyph);
|
||||
if (painter->fill) {
|
||||
FT_Glyph_Stroke(&glyph, painter->font->stroker, 1);
|
||||
} else {
|
||||
FT_Glyph_StrokeBorder(&glyph, painter->font->stroker, 0, 1);
|
||||
}
|
||||
|
||||
if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1)) {
|
||||
FT_Done_Glyph(glyph);
|
||||
lastCodepoint = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph) glyph;
|
||||
bitmap = &bitmapGlyph->bitmap;
|
||||
top = bitmapGlyph->top;
|
||||
left = bitmapGlyph->left;
|
||||
} else {
|
||||
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
|
||||
lastCodepoint = 0;
|
||||
continue;
|
||||
}
|
||||
bitmap = &face->glyph->bitmap;
|
||||
top = face->glyph->bitmap_top;
|
||||
left = face->glyph->bitmap_left;
|
||||
}
|
||||
|
||||
struct mImage image;
|
||||
_makeTemporaryImage(&image, &face->glyph->bitmap);
|
||||
_makeTemporaryImage(&image, bitmap);
|
||||
|
||||
FT_Vector kerning = {0};
|
||||
FT_Get_Kerning(face, lastGlyph, codepoint, FT_KERNING_DEFAULT, &kerning);
|
||||
FT_Get_Kerning(face, lastCodepoint, 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);
|
||||
}
|
||||
mPainterDrawMask(painter, &image, (x >> mFONT_FRACT_BITS) + left, (y >> mFONT_FRACT_BITS) - top);
|
||||
x += face->glyph->advance.x;
|
||||
y += face->glyph->advance.y;
|
||||
|
||||
lastGlyph = codepoint;
|
||||
if (glyph) {
|
||||
FT_Done_Glyph(glyph);
|
||||
}
|
||||
|
||||
lastCodepoint = codepoint;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -221,7 +257,7 @@ void mPainterDrawText(struct mPainter* painter, const char* text, int x, int y,
|
|||
uint32_t fillColor = painter->fillColor;
|
||||
painter->fillColor = painter->strokeColor;
|
||||
do {
|
||||
ltext = mPainterDrawTextRun(painter, ltext, x, yy, alignment, 0x70);
|
||||
ltext = mPainterDrawTextRun(painter, ltext, x + painter->strokeWidth , yy, alignment, true);
|
||||
yy += face->size->metrics.height;
|
||||
} while (ltext);
|
||||
|
||||
|
@ -230,7 +266,7 @@ void mPainterDrawText(struct mPainter* painter, const char* text, int x, int y,
|
|||
#endif
|
||||
|
||||
do {
|
||||
text = mPainterDrawTextRun(painter, text, x, y, alignment, 0);
|
||||
text = mPainterDrawTextRun(painter, text, x, y, alignment, false);
|
||||
y += face->size->metrics.height;
|
||||
} while (text);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue