diff --git a/include/mgba-util/image.h b/include/mgba-util/image.h index 00af9148c..12f7a2ca8 100644 --- a/include/mgba-util/image.h +++ b/include/mgba-util/image.h @@ -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); diff --git a/res/scripts/demos/text-demo.lua b/res/scripts/demos/text-demo.lua index 7875e94c4..10a112358 100644 --- a/res/scripts/demos/text-demo.lua +++ b/res/scripts/demos/text-demo.lua @@ -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) diff --git a/src/util/image.c b/src/util/image.c index dc2a4e371..b5eee0dc9 100644 --- a/src/util/image.c +++ b/src/util/image.c @@ -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; diff --git a/src/util/image/font.c b/src/util/image/font.c index a7689b355..56af2facd 100644 --- a/src/util/image/font.c +++ b/src/util/image/font.c @@ -12,6 +12,7 @@ #include #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); }