diff --git a/include/mgba-util/image.h b/include/mgba-util/image.h index b302ef239..5558910dc 100644 --- a/include/mgba-util/image.h +++ b/include/mgba-util/image.h @@ -159,6 +159,7 @@ 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 0e99f5d7d..070d2fa4d 100644 --- a/res/scripts/demos/text-demo.lua +++ b/res/scripts/demos/text-demo.lua @@ -6,6 +6,8 @@ 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:setFillColor(0xFFFFFFFF) @@ -24,8 +26,9 @@ state.painter:drawText("Center right", state.width, state.height / 2, C.ALIGN.VC state.painter:setFillColor(0xFF0000FF) state.painter:drawText("Bottom left", 0, state.height, C.ALIGN.BOTTOM | C.ALIGN.LEFT) -state.painter:setFillColor(0xFF000000) -state.painter:drawText("Bottom center", state.width / 2, state.height, C.ALIGN.BOTTOM | C.ALIGN.HCENTER) state.painter:setFillColor(0xFF808080) +state.painter:drawText("Bottom center", 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.overlay:update() diff --git a/src/util/image.c b/src/util/image.c index b5eee0dc9..dc2a4e371 100644 --- a/src/util/image.c +++ b/src/util/image.c @@ -904,6 +904,40 @@ 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 03717874c..8c144150f 100644 --- a/src/util/image/font.c +++ b/src/util/image/font.c @@ -11,6 +11,7 @@ #include #include FT_FREETYPE_H +#include FT_MODULE_H #define DPI 100 @@ -42,6 +43,9 @@ struct mFont* mFontOpen(const char* path) { if (FT_Init_FreeType(&library)) { return NULL; } + + FT_Int spread = 5; + FT_Property_Set(library, "sdf", "spread", &spread); } FT_Face face; @@ -131,6 +135,42 @@ void mPainterDrawText(struct mPainter* painter, const char* text, int x, int y, break; } + 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; + } + + painter->fillColor = fillColor; + lastGlyph = 0; + } + while (*text) { uint32_t glyph = utf8Char((const char**) &text, NULL);