diff --git a/Makefile.common b/Makefile.common index 8dabcaa3bb..44df7df399 100644 --- a/Makefile.common +++ b/Makefile.common @@ -339,6 +339,7 @@ endif OBJ += \ gfx/drivers_font_renderer/bitmapfont.o \ gfx/drivers_font_renderer/bitmapfont_10x10.o \ + gfx/drivers_font_renderer/bitmapfont_6x10.o \ tasks/task_autodetect.o \ input/input_autodetect_builtin.o \ input/input_keymaps.o \ diff --git a/gfx/drivers_font_renderer/bitmapfont_6x10.c b/gfx/drivers_font_renderer/bitmapfont_6x10.c new file mode 100644 index 0000000000..73de1c51ad --- /dev/null +++ b/gfx/drivers_font_renderer/bitmapfont_6x10.c @@ -0,0 +1,208 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2020 - Daniel De Matteis + * Copyright (C) 2019-2020 - James Leaver + * Copyright (C) 2020-2022 - trngaje + * Copyright (C) 2022 - Michael Burgardt + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../file_path_special.h" +#include "../../verbosity.h" + +#include "bitmapfont_6x10.h" + +/* MACROS */ +/* extended ASCII: Basic Latin + Latin-1 Supplement */ +#define FONT_6X10_FILE_ENG "bitmap6x10_eng.bin" +#define FONT_6X10_SIZE_ENG 2048 +#define FONT_6X10_GLYPH_MIN_ENG 0x0 +#define FONT_6X10_GLYPH_MAX_ENG 0xFF + +/* Latin Supplement Extended: Latin Extended A + B */ +#define FONT_6X10_FILE_LSE "bitmap6x10_lse.bin" +#define FONT_6X10_SIZE_LSE 2688 +#define FONT_6X10_GLYPH_MIN_LSE 0x100 +#define FONT_6X10_GLYPH_MAX_LSE 0x24F + +/* Loads a font of the specified language + * Returns NULL if language is invalid or + * font file is missing */ +bitmapfont_lut_t *bitmapfont_6x10_load(unsigned language) +{ + char font_dir[PATH_MAX_LENGTH]; + char font_path[PATH_MAX_LENGTH]; + const char *font_file = NULL; + void *bitmap_raw = NULL; + unsigned char *bitmap_char = NULL; + bitmapfont_lut_t *font = NULL; + int64_t font_size = 0; + int64_t len = 0; + size_t glyph_min = 0; + size_t glyph_max = 0; + size_t num_glyphs = 0; + size_t symbol_index; + size_t i, j; + + font_dir[0] = '\0'; + font_path[0] = '\0'; + + /* Get font file associated with + * specified language */ + switch (language) + { + /* Needed individually for any non-Latin languages */ + case RETRO_LANGUAGE_ENGLISH: + { + font_file = FONT_6X10_FILE_ENG; + font_size = FONT_6X10_SIZE_ENG; + glyph_min = FONT_6X10_GLYPH_MIN_ENG; + glyph_max = FONT_6X10_GLYPH_MAX_ENG; + break; + } + + /* All Latin alphabet languages go here */ + case RETRO_LANGUAGE_FRENCH: + case RETRO_LANGUAGE_SPANISH: + case RETRO_LANGUAGE_GERMAN: + case RETRO_LANGUAGE_ITALIAN: + case RETRO_LANGUAGE_DUTCH: + case RETRO_LANGUAGE_PORTUGUESE_BRAZIL: + case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL: + case RETRO_LANGUAGE_ESPERANTO: + case RETRO_LANGUAGE_POLISH: + case RETRO_LANGUAGE_VIETNAMESE: + case RETRO_LANGUAGE_TURKISH: + case RETRO_LANGUAGE_SLOVAK: + case RETRO_LANGUAGE_ASTURIAN: + case RETRO_LANGUAGE_FINNISH: + case RETRO_LANGUAGE_INDONESIAN: + case RETRO_LANGUAGE_SWEDISH: + case RETRO_LANGUAGE_CZECH: + /* These languages are not yet added + case RETRO_LANGUAGE_ROMANIAN: + case RETRO_LANGUAGE_CROATIAN: + case RETRO_LANGUAGE_HUNGARIAN: + case RETRO_LANGUAGE_SERBIAN: + case RETRO_LANGUAGE_WELSH: + */ + { + font_file = FONT_6X10_FILE_LSE; + font_size = FONT_6X10_SIZE_LSE; + glyph_min = FONT_6X10_GLYPH_MIN_LSE; + glyph_max = FONT_6X10_GLYPH_MAX_LSE; + break; + } + + default: + break; + } + + /* Sanity check: should only trigger on bug */ + if (string_is_empty(font_file)) + { + RARCH_WARN("[bitmap 6x10] No font file found for specified language: %u\n", language); + goto error; + } + + /* Get font path */ + fill_pathname_application_special(font_dir, sizeof(font_dir), + APPLICATION_SPECIAL_DIRECTORY_ASSETS_RGUI_FONT); + fill_pathname_join(font_path, font_dir, font_file, + sizeof(font_path)); + + /* Attempt to read bitmap file */ + if (!rzipstream_read_file(font_path, &bitmap_raw, &len)) + { + RARCH_WARN("[bitmap 6x10] Failed to read font file: %s\n", font_path); + goto error; + } + + /* Ensure that we have the correct number + * of bytes */ + if (len != font_size) + { + RARCH_WARN("[bitmap 6x10] Font file has invalid size: %s\n", font_path); + goto error; + } + + bitmap_char = (unsigned char *)bitmap_raw; + num_glyphs = (glyph_max - glyph_min) + 1; + + /* Initialise font struct */ + font = (bitmapfont_lut_t*)calloc(1, sizeof(bitmapfont_lut_t)); + if (!font) + goto error; + + font->glyph_min = glyph_min; + font->glyph_max = glyph_max; + + /* Note: Need to use a calloc() here, otherwise + * we'll get undefined behaviour when calling + * bitmapfont_free_lut() if the following loop fails */ + font->lut = (bool**)calloc(1, num_glyphs * sizeof(bool*)); + if (!font->lut) + goto error; + + /* Loop over all possible characters */ + for (symbol_index = 0; symbol_index < num_glyphs; symbol_index++) + { + /* Allocate memory for current symbol */ + font->lut[symbol_index] = (bool*)malloc(FONT_6X10_WIDTH * + FONT_6X10_HEIGHT * sizeof(bool)); + if (!font->lut[symbol_index]) + goto error; + + for (j = 0; j < FONT_6X10_HEIGHT; j++) + { + for (i = 0; i < FONT_6X10_WIDTH; i++) + { + uint8_t rem = 1 << ((i + j * FONT_6X10_WIDTH) & 7); + unsigned offset = (i + j * FONT_6X10_WIDTH) >> 3; + + /* LUT value is 'true' if specified glyph + * position contains a pixel */ + font->lut[symbol_index][i + (j * FONT_6X10_WIDTH)] = + (bitmap_char[FONT_6X10_OFFSET(symbol_index) + offset] & rem) > 0; + } + } + } + + /* Clean up */ + free(bitmap_raw); + + return font; + +error: + if (bitmap_raw) + free(bitmap_raw); + + if (font) + bitmapfont_free_lut(font); + + return NULL; +} + diff --git a/gfx/drivers_font_renderer/bitmapfont_6x10.h b/gfx/drivers_font_renderer/bitmapfont_6x10.h new file mode 100644 index 0000000000..ca646c3f9b --- /dev/null +++ b/gfx/drivers_font_renderer/bitmapfont_6x10.h @@ -0,0 +1,44 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2020 - Daniel De Matteis + * Copyright (C) 2019-2020 - James Leaver + * Copyright (C) 2020-2022 - trngaje + * Copyright (C) 2022 - Michael Burgardt + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __RARCH_FONT_BITMAP_6X10_H +#define __RARCH_FONT_BITMAP_6X10_H + +#include "bitmap.h" + +#define FONT_6X10_WIDTH 6 +#define FONT_6X10_HEIGHT 10 +/* FONT_HEIGHT_BASELINE_OFFSET: + * Distance in pixels from top of character + * to baseline */ +#define FONT_6X10_HEIGHT_BASELINE_OFFSET 8 +#define FONT_6X10_WIDTH_STRIDE (FONT_6X10_WIDTH) +#define FONT_6X10_HEIGHT_STRIDE (FONT_6X10_HEIGHT + 1) + +#define FONT_6X10_OFFSET(x) ((x) * ((FONT_6X10_HEIGHT * FONT_6X10_WIDTH + 7) / 8)) + +/* Loads a font of the specified language. + * Returned object must be freed using + * bitmapfont_free_lut(). + * Returns NULL if language is invalid or + * font file is missing */ +bitmapfont_lut_t *bitmapfont_6x10_load(unsigned language); + +#endif + diff --git a/griffin/griffin.c b/griffin/griffin.c index 339cb4081a..ec49d76f49 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -583,7 +583,12 @@ FONTS ============================================================ */ #include "../gfx/drivers_font_renderer/bitmapfont.c" + +#ifdef HAVE_LANGEXTRA #include "../gfx/drivers_font_renderer/bitmapfont_10x10.c" +#include "../gfx/drivers_font_renderer/bitmapfont_6x10.c" +#endif + #include "../gfx/font_driver.c" #if defined(HAVE_D3D9) && defined(HAVE_D3DX) diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 5d44a45bcf..74535df4c3 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -52,7 +52,11 @@ #include "../../configuration.h" #include "../../gfx/drivers_font_renderer/bitmap.h" + +#ifdef HAVE_LANGEXTRA #include "../../gfx/drivers_font_renderer/bitmapfont_10x10.h" +#include "../../gfx/drivers_font_renderer/bitmapfont_6x10.h" +#endif /* Thumbnail additions */ #include "../../gfx/gfx_thumbnail_path.h" @@ -1061,11 +1065,16 @@ typedef struct struct { bitmapfont_lut_t *regular; + +#ifdef HAVE_LANGEXTRA + bitmapfont_lut_t *eng_6x10; + bitmapfont_lut_t *lse_6x10; bitmapfont_lut_t *eng_10x10; bitmapfont_lut_t *chn_10x10; bitmapfont_lut_t *jpn_10x10; bitmapfont_lut_t *kor_10x10; bitmapfont_lut_t *rus_10x10; +#endif } fonts; frame_buf_t frame_buf; @@ -1608,6 +1617,19 @@ static void rgui_fonts_free(rgui_t *rgui) rgui->fonts.regular = NULL; } +#ifdef HAVE_LANGEXTRA + if (rgui->fonts.eng_6x10) + { + bitmapfont_free_lut(rgui->fonts.eng_6x10); + rgui->fonts.eng_6x10 = NULL; + } + + if (rgui->fonts.lse_6x10) + { + bitmapfont_free_lut(rgui->fonts.lse_6x10); + rgui->fonts.lse_6x10 = NULL; + } + if (rgui->fonts.eng_10x10) { bitmapfont_free_lut(rgui->fonts.eng_10x10); @@ -1637,6 +1659,7 @@ static void rgui_fonts_free(rgui_t *rgui) bitmapfont_free_lut(rgui->fonts.rus_10x10); rgui->fonts.rus_10x10 = NULL; } +#endif } static bool rgui_fonts_init(rgui_t *rgui) @@ -1648,6 +1671,7 @@ static bool rgui_fonts_init(rgui_t *rgui) { case RETRO_LANGUAGE_ENGLISH: goto english; + case RETRO_LANGUAGE_FRENCH: case RETRO_LANGUAGE_SPANISH: case RETRO_LANGUAGE_GERMAN: @@ -1655,17 +1679,11 @@ static bool rgui_fonts_init(rgui_t *rgui) case RETRO_LANGUAGE_DUTCH: case RETRO_LANGUAGE_PORTUGUESE_BRAZIL: case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL: - case RETRO_LANGUAGE_ESPERANTO: - case RETRO_LANGUAGE_POLISH: case RETRO_LANGUAGE_VIETNAMESE: - case RETRO_LANGUAGE_TURKISH: - case RETRO_LANGUAGE_SLOVAK: case RETRO_LANGUAGE_ASTURIAN: case RETRO_LANGUAGE_FINNISH: case RETRO_LANGUAGE_INDONESIAN: case RETRO_LANGUAGE_SWEDISH: - /* extended ASCII (ASCII + Latin-1 Suplement) doesn't cover Czech completely*/ - /* case RETRO_LANGUAGE_CZECH: */ /* We have at least partial support for * these languages, but extended ASCII * is required */ @@ -1677,10 +1695,12 @@ static bool rgui_fonts_init(rgui_t *rgui) rgui->language = language; goto english; } + case RETRO_LANGUAGE_JAPANESE: case RETRO_LANGUAGE_KOREAN: case RETRO_LANGUAGE_CHINESE_SIMPLIFIED: case RETRO_LANGUAGE_CHINESE_TRADITIONAL: + { rgui->fonts.eng_10x10 = bitmapfont_10x10_load(RETRO_LANGUAGE_ENGLISH); rgui->fonts.chn_10x10 = bitmapfont_10x10_load(RETRO_LANGUAGE_CHINESE_SIMPLIFIED); rgui->fonts.jpn_10x10 = bitmapfont_10x10_load(RETRO_LANGUAGE_JAPANESE); @@ -1705,7 +1725,10 @@ static bool rgui_fonts_init(rgui_t *rgui) rgui->font_height_stride = FONT_10X10_HEIGHT_STRIDE; rgui->language = language; break; + } + case RETRO_LANGUAGE_RUSSIAN: + { rgui->fonts.eng_10x10 = bitmapfont_10x10_load(RETRO_LANGUAGE_ENGLISH); rgui->fonts.rus_10x10 = bitmapfont_10x10_load(RETRO_LANGUAGE_RUSSIAN); @@ -1726,6 +1749,47 @@ static bool rgui_fonts_init(rgui_t *rgui) rgui->font_height_stride = FONT_10X10_HEIGHT_STRIDE; rgui->language = language; break; + } + + case RETRO_LANGUAGE_ESPERANTO: + case RETRO_LANGUAGE_POLISH: + case RETRO_LANGUAGE_TURKISH: + case RETRO_LANGUAGE_SLOVAK: + case RETRO_LANGUAGE_CZECH: + /* These languages are not yet implemented + case RETRO_LANGUAGE_ROMANIAN: + case RETRO_LANGUAGE_CROATIAN: + case RETRO_LANGUAGE_HUNGARIAN: + case RETRO_LANGUAGE_SERBIAN: + case RETRO_LANGUAGE_WELSH: + */ + /* 6x10 fonts, including: + * Latin Extended A + B + * + */ + { + rgui->fonts.eng_6x10 = bitmapfont_6x10_load(RETRO_LANGUAGE_ENGLISH); + rgui->fonts.lse_6x10 = bitmapfont_6x10_load(language); + + if (!rgui->fonts.eng_6x10 || + !rgui->fonts.lse_6x10) + { + rgui_fonts_free(rgui); + *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE) = RETRO_LANGUAGE_ENGLISH; + runloop_msg_queue_push( + msg_hash_to_str(MSG_RGUI_MISSING_FONTS), 1, 256, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + goto english; + } + + rgui->font_width = FONT_6X10_WIDTH; + rgui->font_height = FONT_6X10_HEIGHT; + rgui->font_width_stride = FONT_6X10_WIDTH_STRIDE; + rgui->font_height_stride = FONT_6X10_HEIGHT_STRIDE; + rgui->language = language; + break; + } + case RETRO_LANGUAGE_ARABIC: case RETRO_LANGUAGE_GREEK: case RETRO_LANGUAGE_PERSIAN: @@ -3601,6 +3665,7 @@ static void blit_line_cjk( bool *symbol_lut; uint32_t symbol = utf8_walk(&message); + /* TODO/FIXME: check if really needed */ if (symbol == 339) /* Latin small ligature oe */ symbol = 156; if (symbol == 338) /* Latin capital ligature oe */ @@ -3664,6 +3729,7 @@ static void blit_line_cjk_shadow( bool *symbol_lut; uint32_t symbol = utf8_walk(&message); + /* TODO/FIXME: check if really needed */ if (symbol == 339) /* Latin small ligature oe */ symbol = 156; if (symbol == 338) /* Latin capital ligature oe */ @@ -3726,6 +3792,7 @@ static void blit_line_rus( bool *symbol_lut; uint32_t symbol = utf8_walk(&message); + /* TODO/FIXME: check if really needed */ if (symbol == 339) /* Latin small ligature oe */ symbol = 156; if (symbol == 338) /* Latin capital ligature oe */ @@ -3783,6 +3850,7 @@ static void blit_line_rus_shadow( bool *symbol_lut; uint32_t symbol = utf8_walk(&message); + /* TODO/FIXME: check if really needed */ if (symbol == 339) /* Latin small ligature oe */ symbol = 156; if (symbol == 338) /* Latin capital ligature oe */ @@ -3820,6 +3888,111 @@ static void blit_line_rus_shadow( x += FONT_10X10_WIDTH_STRIDE; } } + +static void blit_line_6x10( + rgui_t *rgui, + unsigned fb_width, int x, int y, + const char *message, uint16_t color, uint16_t shadow_color) +{ + uint16_t *frame_buf_data = rgui->frame_buf.data; + bitmapfont_lut_t *font_eng = rgui->fonts.eng_6x10; + bitmapfont_lut_t *font_lse = rgui->fonts.lse_6x10; + + while (!string_is_empty(message)) + { + /* Deal with spaces first, for efficiency */ + if (*message == ' ') + message++; + else + { + unsigned i, j; + bool *symbol_lut; + uint32_t symbol = utf8_walk(&message); + + /* Find glyph LUT data */ + if (symbol <= font_eng->glyph_max) + symbol_lut = font_eng->lut[symbol]; + else if ((symbol >= font_lse->glyph_min) && (symbol <= font_lse->glyph_max)) + symbol_lut = font_lse->lut[symbol - font_lse->glyph_min]; + else + continue; + + for (j = 0; j < FONT_6X10_HEIGHT; j++) + { + unsigned buff_offset = ((y + j) * fb_width) + x; + + for (i = 0; i < FONT_6X10_WIDTH; i++) + { + if (*(symbol_lut + i + (j * FONT_6X10_WIDTH))) + *(frame_buf_data + buff_offset + i) = color; + } + } + } + + x += FONT_6X10_WIDTH_STRIDE; + } +} + +static void blit_line_6x10_shadow( + rgui_t *rgui, + unsigned fb_width, int x, int y, + const char *message, uint16_t color, uint16_t shadow_color) +{ + uint16_t *frame_buf_data = rgui->frame_buf.data; + bitmapfont_lut_t *font_eng = rgui->fonts.eng_6x10; + bitmapfont_lut_t *font_lse = rgui->fonts.lse_6x10; + uint16_t color_buf[2]; + uint16_t shadow_color_buf[2]; + + color_buf[0] = color; + color_buf[1] = shadow_color; + + shadow_color_buf[0] = shadow_color; + shadow_color_buf[1] = shadow_color; + + while (!string_is_empty(message)) + { + /* Deal with spaces first, for efficiency */ + if (*message == ' ') + message++; + else + { + unsigned i, j; + bool *symbol_lut; + uint32_t symbol = utf8_walk(&message); + + /* Find glyph LUT data */ + if (symbol <= font_eng->glyph_max) + symbol_lut = font_eng->lut[symbol]; + else if ((symbol >= font_lse->glyph_min) && (symbol <= font_lse->glyph_max)) + symbol_lut = font_lse->lut[symbol - font_lse->glyph_min]; + else + continue; + + for (j = 0; j < FONT_6X10_HEIGHT; j++) + { + unsigned buff_offset = ((y + j) * fb_width) + x; + + for (i = 0; i < FONT_6X10_WIDTH; i++) + { + if (*(symbol_lut + i + (j * FONT_6X10_WIDTH))) + { + uint16_t *frame_buf_ptr = frame_buf_data + buff_offset + i; + + /* Text pixel + right shadow */ + memcpy(frame_buf_ptr, color_buf, sizeof(color_buf)); + + /* Bottom shadow */ + frame_buf_ptr += fb_width; + memcpy(frame_buf_ptr, shadow_color_buf, sizeof(shadow_color_buf)); + } + } + } + } + + x += FONT_6X10_WIDTH_STRIDE; + } +} #endif static void (*blit_line)(rgui_t *rgui, unsigned fb_width, int x, int y, @@ -3957,6 +4130,13 @@ static void rgui_set_blit_functions(unsigned language, case RETRO_LANGUAGE_RUSSIAN: blit_line = blit_line_rus_shadow; break; + case RETRO_LANGUAGE_ESPERANTO: + case RETRO_LANGUAGE_POLISH: + case RETRO_LANGUAGE_TURKISH: + case RETRO_LANGUAGE_SLOVAK: + case RETRO_LANGUAGE_CZECH: + blit_line = blit_line_6x10_shadow; + break; default: if (extended_ascii) blit_line = blit_line_extended_shadow; @@ -3987,6 +4167,13 @@ static void rgui_set_blit_functions(unsigned language, case RETRO_LANGUAGE_RUSSIAN: blit_line = blit_line_rus; break; + case RETRO_LANGUAGE_ESPERANTO: + case RETRO_LANGUAGE_POLISH: + case RETRO_LANGUAGE_TURKISH: + case RETRO_LANGUAGE_SLOVAK: + case RETRO_LANGUAGE_CZECH: + blit_line = blit_line_6x10; + break; default: if (extended_ascii) blit_line = blit_line_extended;