diff --git a/CMakeLists.txt b/CMakeLists.txt index 81fdeec3f..b3a07530c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ if(NOT LIBMGBA_ONLY) set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") set(BUILD_DOCGEN OFF CACHE BOOL "Build the scripting API documentation generator") + set(BUILD_MAINTAINER_TOOLS OFF CACHE BOOL "Build tools only useful for maintainers") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages") @@ -93,6 +94,7 @@ if(NOT LIBMGBA_ONLY) mark_as_advanced(WIN32_UNIX_PATHS) endif() mark_as_advanced(BUILD_DOCGEN) + mark_as_advanced(BUILD_MAINTAINER_TOOLS) else() set(DISABLE_FRONTENDS ON) set(DISABLE_DEPS ON) @@ -1045,6 +1047,11 @@ if(ENABLE_SCRIPTING AND BUILD_DOCGEN) target_link_libraries(docgen ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) endif() +if(BUILD_MAINTAINER_TOOLS) + add_executable(font-sdf-tool ${CMAKE_CURRENT_SOURCE_DIR}/src/tools/font-sdf.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/gui/font-metrics.c) + target_link_libraries(font-sdf-tool ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) +endif() + if(BUILD_SDL) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl) endif() diff --git a/include/mgba-util/gui/font-metrics.h b/include/mgba-util/gui/font-metrics.h index c60d6dfc8..c2e6668d4 100644 --- a/include/mgba-util/gui/font-metrics.h +++ b/include/mgba-util/gui/font-metrics.h @@ -6,9 +6,10 @@ #ifndef DEFAULT_FONT_METRICS_H #define DEFAULT_FONT_METRICS_H +#include #include -extern struct GUIFontGlyphMetric defaultFontMetrics[]; -extern struct GUIIconMetric defaultIconMetrics[]; +extern const struct GUIFontGlyphMetric defaultFontMetrics[128]; +extern const struct GUIIconMetric defaultIconMetrics[GUI_ICON_MAX]; #endif diff --git a/res/font-mask.png b/res/font-mask.png new file mode 100644 index 000000000..2e223597b Binary files /dev/null and b/res/font-mask.png differ diff --git a/res/font-sdf.png b/res/font-sdf.png new file mode 100644 index 000000000..95960e26c Binary files /dev/null and b/res/font-sdf.png differ diff --git a/src/tools/font-sdf.c b/src/tools/font-sdf.c new file mode 100644 index 000000000..e28c34eaf --- /dev/null +++ b/src/tools/font-sdf.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2013-2023 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include +#include + +void createSdf(const struct mImage* src, struct mImage* dst, int x, int y, int w, int h) { + int i, j, ii, jj, z; + + static const int kernel[] = { + 11, 9, 8, 9, 11, + 9, 6, 4, 6, 9, + 8, 4, 0, 4, 8, + 9, 6, 4, 6, 9, + 11, 9, 8, 9, 11, + }; + const int* kc = &kernel[12]; + + for (j = y; j < y + h; ++j) { + for (i = x; i < x + w; ++i) { + if (mImageGetPixel(src, i, j) & 0xFFFFFF) { + mImageSetPixelRaw(dst, i, j, 0xFF); + } else { + mImageSetPixelRaw(dst, i, j, 1); + } + } + } + + for (z = 0; z < 16; ++z) { + int left = w * h; + for (j = y; j < y + h; ++j) { + for (i = x; i < x + w; ++i) { + int raw = mImageGetPixelRaw(dst, i, j) - 0x80; + int neighbors = raw; + for (jj = -2; jj < 3; ++jj) { + for (ii = -2; ii < 3; ++ii) { + if (!ii && !jj) { + continue; + } + if (i + ii - x < 0 || j + jj - y < 0) { + continue; + } + if (i + ii - x >= w || j + jj - y >= h) { + continue; + } + int neighbor = mImageGetPixelRaw(dst, i + ii, j + jj) - 0x80; + if (raw > 0) { + if (neighbor < 0) { + if ((!ii && (jj == -1 || jj == 1)) || (!jj && (ii == -1 || ii == 1))) { + neighbors = 1; + jj = 5; + break; + } + continue; + } + if (neighbor == 0x7F) { + continue; + } + if (neighbor + kc[ii + jj * 5] < neighbors) { + neighbors = neighbor + kc[ii + jj * 5]; + } + } else if (raw < 0) { + if (neighbor > 0) { + if ((!ii && (jj == -1 || jj == 1)) || (!jj && (ii == -1 || ii == 1))) { + neighbors = -4; + jj = 5; + break; + } + continue; + } + if (neighbor == -0x7F) { + continue; + } + if (neighbor - kc[ii + jj * 5] > neighbors) { + neighbors = neighbor - kc[ii + jj * 5]; + } + } + } + } + if (neighbors == raw) { + --left; + } else { + mImageSetPixelRaw(dst, i, j, neighbors + 0x80); + } + if (!left) { + break; + } + } + if (!left) { + break; + } + } + if (!left) { + break; + } + } +} + +int main(int argc, char* argv[]) { + struct mImage* source; + struct mImage* output; + + if (argc != 3) { + return 1; + } + source = mImageLoad(argv[1]); + if (!source) { + return 2; + } + output = mImageCreate(source->width, source->height, mCOLOR_L8); + if (!output) { + return 3; + } + + int i; + for (i = 0; i < 128; ++i) { + createSdf(source, output, (i & 0xF) << 6, (i & ~0xF) << 2, 64, 64); + } + for (i = 0; i < GUI_ICON_MAX; ++i) { + struct GUIIconMetric metric = defaultIconMetrics[i]; + metric.width += metric.x & 0xF; + metric.height += metric.y & 0xF; + metric.x &= ~0xF; + metric.y &= ~0xF; + createSdf(source, output, metric.x * 4, metric.y * 4 + 512, toPow2(metric.width) * 4, toPow2(metric.height) * 4); + } + if (!mImageSave(output, argv[2], NULL)) { + return 4; + } + mImageDestroy(source); + mImageDestroy(output); + return 0; +}