diff --git a/pygame_gui/core/gui_font_freetype.py b/pygame_gui/core/gui_font_freetype.py index 120d4658..d9e38e0e 100644 --- a/pygame_gui/core/gui_font_freetype.py +++ b/pygame_gui/core/gui_font_freetype.py @@ -23,9 +23,13 @@ def __init__(self, file: Optional[FileArg], size: Union[int, float], self.point_size = size - if force_style and style is not None: - self.__internal_font.strong = style['bold'] - self.__internal_font.oblique = style['italic'] + if style is not None: + self.__internal_font.antialiased = style['antialiased'] + + if force_style: + self.__internal_font.strong = style['bold'] + self.__internal_font.oblique = style['italic'] + @property def underline(self) -> bool: diff --git a/pygame_gui/core/interfaces/font_dictionary_interface.py b/pygame_gui/core/interfaces/font_dictionary_interface.py index 1bf85de2..f9e93967 100644 --- a/pygame_gui/core/interfaces/font_dictionary_interface.py +++ b/pygame_gui/core/interfaces/font_dictionary_interface.py @@ -13,7 +13,8 @@ class IUIFontDictionaryInterface(metaclass=ABCMeta): @abstractmethod def find_font(self, font_size: int, font_name: str, - bold: bool = False, italic: bool = False) -> IGUIFontInterface: + bold: bool = False, italic: bool = False, + antialiased: bool = True) -> IGUIFontInterface: """ Find a loaded font from the font dictionary. Will load a font if it does not already exist and we have paths to the needed files, however it will issue a warning after doing so @@ -27,6 +28,7 @@ def find_font(self, font_size: int, font_name: str, :param font_name: The name of the font to find. :param bold: Whether the font is bold or not. :param italic: Whether the font is italic or not. + :param antialiased: Whether the font is antialiased or not. :return IGUIFontInterface: Returns either the font we asked for, or the default font. @@ -42,7 +44,7 @@ def get_default_font(self) -> IGUIFontInterface: """ @abstractmethod - def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: bool) -> str: + def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: bool, antialiased: bool = True) -> str: """ Create an id for a particularly styled and sized font from those characteristics. @@ -50,6 +52,7 @@ def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: boo :param font_name: The name of the font. :param bold: Whether the font is bold styled or not. :param italic: Whether the font is italic styled or not. + :param antialiased: Whether the font is antialiased or not. :return str: The finished font id. @@ -58,7 +61,8 @@ def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: boo @abstractmethod def preload_font(self, font_size: int, font_name: str, bold: bool = False, italic: bool = False, - force_immediate_load: bool = False): + force_immediate_load: bool = False, + antialiased: bool = True): """ Lets us load a font at a particular size and style before we use it. While you can get away with relying on dynamic font loading during development, it is better to eventually @@ -70,6 +74,7 @@ def preload_font(self, font_size: int, font_name: str, :param italic: Whether the font is italic styled or not. :param force_immediate_load: bypasses any asynchronous threaded loading setup to immediately load the font on the main thread. + :param antialiased: Whether the font is antialiased or not. """ diff --git a/pygame_gui/core/text/html_parser.py b/pygame_gui/core/text/html_parser.py index 04f1a8a9..e87e8862 100644 --- a/pygame_gui/core/text/html_parser.py +++ b/pygame_gui/core/text/html_parser.py @@ -85,6 +85,7 @@ def __init__(self, self.default_style['link_href'] = '' self.default_style['shadow_data'] = None self.default_style['effect_id'] = None + self.default_style['antialiased'] = True # this is the style used before any html is loaded self.push_style('default_style', self.default_style) @@ -247,7 +248,8 @@ def _handle_line_break(self): font_name=self.current_style['font_name'], font_size=self.current_style['font_size'], bold=self.current_style['bold'], - italic=self.current_style['italic']) + italic=self.current_style['italic'], + antialiased=self.current_style['antialiased']) dimensions = (current_font.get_rect(' ').width, int(round(self.current_style['font_size'] * self.line_spacing))) @@ -406,7 +408,8 @@ def create_styled_text_chunk(self, text: str): font_name=self.current_style['font_name'], font_size=self.current_style['font_size'], bold=self.current_style['bold'], - italic=self.current_style['italic']) + italic=self.current_style['italic'], + antialiased=self.current_style['antialiased']) if self.current_style['link']: should_underline = (self.current_style['underline'] or diff --git a/pygame_gui/core/ui_appearance_theme.py b/pygame_gui/core/ui_appearance_theme.py index 02dac31e..5f9e57e6 100644 --- a/pygame_gui/core/ui_appearance_theme.py +++ b/pygame_gui/core/ui_appearance_theme.py @@ -210,13 +210,16 @@ def _load_fonts(self): font_id = self.font_dict.create_font_id(font_info['size'], font_info['name'], font_info['bold'], - font_info['italic']) + font_info['italic'], + font_info['antialiased']) if font_id not in self.font_dict.loaded_fonts: self.font_dict.preload_font(font_info['size'], font_info['name'], font_info['bold'], - font_info['italic']) + font_info['italic'], + False, + font_info['antialiased']) if element_key not in self.ele_font_res: self.ele_font_res[element_key] = {} @@ -224,7 +227,8 @@ def _load_fonts(self): font_info['size'], font_info['name'], font_info['bold'], - font_info['italic']) + font_info['italic'], + font_info['antialiased']) def _load_images(self): """ @@ -939,6 +943,14 @@ def _load_element_font_data_from_theme(self, if 'italic' not in font_info_dict: font_info_dict['italic'] = False + if 'antialiased' in file_dict: + try: + font_info_dict['antialiased'] = bool(int(file_dict['antialiased'])) + except ValueError: + font_info_dict['antialiased'] = True + if 'antialiased' not in font_info_dict: + font_info_dict['antialiased'] = True + if 'regular_path' in file_dict: font_info_dict['regular_path'] = file_dict['regular_path'] if 'bold_path' in file_dict: diff --git a/pygame_gui/core/ui_font_dictionary.py b/pygame_gui/core/ui_font_dictionary.py index 686a96be..36f2d5ab 100644 --- a/pygame_gui/core/ui_font_dictionary.py +++ b/pygame_gui/core/ui_font_dictionary.py @@ -26,7 +26,7 @@ def __init__(self, size: int, name: str, style: str, self.size = size self.name = name self.style = style - self.idx = (self.name + '_' + self.style + '_' + str(self.size)) + self.idx = (self.name + '_' + self.style + '_' + 'aa_' + str(self.size)) self.regular_file_name = regular_file_name self.bold_file_name = bold_file_name @@ -155,7 +155,7 @@ def _load_default_font(self): default_font_res = FontResource( font_id=self.default_font.idx, size=self.default_font.size, - style={'bold': False, 'italic': False}, + style={'bold': False, 'italic': False, 'antialiased': True}, location=( PackageResource(package='pygame_gui.data', resource=self.default_font.regular_file_name), @@ -177,7 +177,7 @@ def _load_default_font(self): resource=self.default_font.bold_italic_file_name), False)] def find_font(self, font_size: int, font_name: str, - bold: bool = False, italic: bool = False) -> IGUIFontInterface: + bold: bool = False, italic: bool = False, antialiased: bool = True) -> IGUIFontInterface: """ Find a loaded font from the font dictionary. Will load a font if it does not already exist and we have paths to the needed files, however it will issue a warning after doing so @@ -191,14 +191,15 @@ def find_font(self, font_size: int, font_name: str, :param font_name: The name of the font to find. :param bold: Whether the font is bold or not. :param italic: Whether the font is italic or not. + :param antialiased: Whether the font is antialiased or not. :return IGUIFontInterface: Returns either the font we asked for, or the default font. """ - return self.find_font_resource(font_size, font_name, bold, italic).loaded_font + return self.find_font_resource(font_size, font_name, bold, italic, antialiased).loaded_font def find_font_resource(self, font_size: int, font_name: str, - bold: bool = False, italic: bool = False) -> FontResource: + bold: bool = False, italic: bool = False, antialiased: bool = True) -> FontResource: """ Find a loaded font resource from the font dictionary. Will load a font if it does not already exist and we have paths to the needed files, however it will issue a warning @@ -212,11 +213,12 @@ def find_font_resource(self, font_size: int, font_name: str, :param font_name: The name of the font to find. :param bold: Whether the font is bold or not. :param italic: Whether the font is italic or not. + :param antialiased: Whether the font is antialiased or not. :return FontResource: Returns either the font resource we asked for, or the default font. """ - font_id = self.create_font_id(font_size, font_name, bold, italic) + font_id = self.create_font_id(font_size, font_name, bold, italic, antialiased) if font_id not in self.used_font_ids: self.used_font_ids.append(font_id) # record font usage for optimisation purposes @@ -233,15 +235,21 @@ def find_font_resource(self, font_size: int, font_name: str, elif italic: style_string = "italic" + font_aliasing = "0" + if antialiased: + font_aliasing = "1" + warning_string = ('Finding font with id: ' + font_id + " that is not already loaded.\n" "Preload this font with {'name': " "'" + font_name + "'," " 'point_size': " + str(font_size) + "," - " 'style': '" + style_string + "'}") + " 'style': '" + style_string + "'," + + " 'antialiased': '" + font_aliasing + + "'}") warnings.warn(warning_string, UserWarning) - self.preload_font(font_size, font_name, bold, italic, force_immediate_load=True) + self.preload_font(font_size, font_name, bold, italic, force_immediate_load=True, antialiased=antialiased) return self.loaded_fonts[font_id] else: return self.loaded_fonts[self.default_font.idx] @@ -255,7 +263,7 @@ def get_default_font(self) -> IGUIFontInterface: """ return self.find_font(self.default_font.size, self.default_font.name) - def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: bool) -> str: + def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: bool, antialiased: bool = True) -> str: """ Create an id for a particularly styled and sized font from those characteristics. @@ -263,6 +271,7 @@ def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: boo :param font_name: The name of the font. :param bold: Whether the font is bold styled or not. :param italic: Whether the font is italic styled or not. + :param antialiased: Whether the font is antialiased or not. :return str: The finished font id. @@ -278,11 +287,16 @@ def create_font_id(self, font_size: int, font_name: str, bold: bool, italic: boo font_style_string = "italic" else: font_style_string = "regular" - return font_name + "_" + font_style_string + "_" + str(font_size) + + font_aliasing = "non_aa" + if antialiased: + font_aliasing = "aa" + + return font_name + "_" + font_style_string + "_" + font_aliasing + "_" + str(font_size) def preload_font(self, font_size: int, font_name: str, bold: bool = False, italic: bool = False, - force_immediate_load: bool = False): + force_immediate_load: bool = False, antialiased: bool = True): """ Lets us load a font at a particular size and style before we use it. While you can get away with relying on dynamic font loading during development, it is better to eventually @@ -294,9 +308,10 @@ def preload_font(self, font_size: int, font_name: str, :param italic: Whether the font is italic styled or not. :param force_immediate_load: resource loading setup to immediately load the font on the main thread. + :param antialiased: Whether the font is antialiased or not. """ - font_id = self.create_font_id(font_size, font_name, bold, italic) + font_id = self.create_font_id(font_size, font_name, bold, italic, antialiased) if font_id in self.loaded_fonts: # font already loaded warnings.warn('Trying to pre-load font id: ' + font_id + @@ -312,7 +327,8 @@ def preload_font(self, font_size: int, font_name: str, font_id, font_size, font_style={'bold': True, - 'italic': True}, + 'italic': True, + 'antialiased': antialiased}, force_immediate_load=force_immediate_load) elif bold: @@ -320,21 +336,24 @@ def preload_font(self, font_size: int, font_name: str, font_id, font_size, font_style={'bold': True, - 'italic': False}, + 'italic': False, + 'antialiased': antialiased}, force_immediate_load=force_immediate_load) elif italic: self._load_single_font_style(italic_path, font_id, font_size, font_style={'bold': False, - 'italic': True}, + 'italic': True, + 'antialiased': antialiased}, force_immediate_load=force_immediate_load) else: self._load_single_font_style(regular_path, font_id, font_size, font_style={'bold': False, - 'italic': False}, + 'italic': False, + 'antialiased': antialiased}, force_immediate_load=force_immediate_load) else: warnings.warn('Trying to pre-load font id:' + font_id + ' with no paths set') @@ -351,7 +370,7 @@ def _load_single_font_style(self, :param font_loc: Path to the font file. :param font_id: id for the font in the loaded fonts dictionary. :param font_size: pygame font size. - :param font_style: style dictionary (italic, bold, both or neither) + :param font_style: style dictionary (italic, bold, antialiased, all, some or none) """ resource = FontResource(font_id=font_id, diff --git a/pygame_gui/elements/ui_text_box.py b/pygame_gui/elements/ui_text_box.py index 413aadec..e4c1696e 100644 --- a/pygame_gui/elements/ui_text_box.py +++ b/pygame_gui/elements/ui_text_box.py @@ -564,7 +564,8 @@ def parse_html_into_style_data(self): font_name=self.parser.default_style['font_name'], font_size=self.parser.default_style['font_size'], bold=self.parser.default_style['bold'], - italic=self.parser.default_style['italic']) + italic=self.parser.default_style['italic'], + antialiased=self.parser.default_style['antialiased'],) default_font_data = {"font": default_font, "font_colour": self.parser.default_style['font_colour'], "bg_colour": self.parser.default_style['bg_colour'] diff --git a/pygame_gui/ui_manager.py b/pygame_gui/ui_manager.py index be0cf4db..eed7d8dd 100644 --- a/pygame_gui/ui_manager.py +++ b/pygame_gui/ui_manager.py @@ -381,7 +381,7 @@ def preload_fonts(self, font_list: List[Dict[str, Union[str, int, float]]]): 'fira_code' font) you must first add the paths to the files for those fonts, then load the specific fonts with a list of font descriptions in a dictionary form like so: - ``{'name': 'fira_code', 'point_size': 12, 'style': 'bold_italic'}`` + ``{'name': 'fira_code', 'point_size': 12, 'style': 'bold_italic', 'antialiased': 1}`` You can specify size either in pygame.Font point sizes with 'point_size', or in HTML style sizes with 'html_size'. Style options are: @@ -401,6 +401,7 @@ def preload_fonts(self, font_list: List[Dict[str, Union[str, int, float]]]): bold = False italic = False size = 14 + antialiased = True if 'name' in font: name = font['name'] if 'style' in font: @@ -408,13 +409,15 @@ def preload_fonts(self, font_list: List[Dict[str, Union[str, int, float]]]): bold = True if 'italic' in font['style']: italic = True + if 'antialiased' in font: + antialiased = bool(int(font['antialiased'])) if 'html_size' in font: font_dict = self.ui_theme.get_font_dictionary() size = font_dict.convert_html_to_point_size(font['html_size']) elif 'point_size' in font: size = font['point_size'] - self.ui_theme.get_font_dictionary().preload_font(size, name, bold, italic) + self.ui_theme.get_font_dictionary().preload_font(size, name, bold, italic, False, antialiased) def print_unused_fonts(self): """ diff --git a/tests/test_core/test_ui_font_dictionary.py b/tests/test_core/test_ui_font_dictionary.py index 77d38f52..ebc5f291 100644 --- a/tests/test_core/test_ui_font_dictionary.py +++ b/tests/test_core/test_ui_font_dictionary.py @@ -64,16 +64,16 @@ def test_find_font_unloaded(self, _init_pygame, _display_surface_return_none): def test_create_font_id(self, _init_pygame, _display_surface_return_none): font_dictionary = UIFontDictionary(BlockingThreadedResourceLoader(), locale='en') font_id_1 = font_dictionary.create_font_id(font_size=10, font_name='bob', bold=False, italic=False) - assert font_id_1 == 'bob_regular_10' + assert font_id_1 == 'bob_regular_aa_10' font_id_1 = font_dictionary.create_font_id(font_size=10, font_name='bob', bold=True, italic=False) - assert font_id_1 == 'bob_bold_10' + assert font_id_1 == 'bob_bold_aa_10' font_id_1 = font_dictionary.create_font_id(font_size=10, font_name='bob', bold=False, italic=True) - assert font_id_1 == 'bob_italic_10' + assert font_id_1 == 'bob_italic_aa_10' font_id_1 = font_dictionary.create_font_id(font_size=10, font_name='bob', bold=True, italic=True) - assert font_id_1 == 'bob_bold_italic_10' + assert font_id_1 == 'bob_bold_italic_aa_10' with pytest.warns(UserWarning, match="Font size less than or equal to 0"): font_dictionary.create_font_id(font_size=-50, font_name='bob', bold=True, italic=True) @@ -135,7 +135,7 @@ def test_print_unused_loaded_fonts(self, _init_pygame, _display_surface_return_n font_dictionary.print_unused_loaded_fonts() captured = capsys.readouterr() - assert captured.out == 'Unused font ids:\nroboto_regular_14(HTML size: 4)\n' + assert captured.out == 'Unused font ids:\nroboto_regular_aa_14(HTML size: 4)\n' def test_convert_html_size_to_point_size_invalid(self, _init_pygame, _display_surface_return_none): font_dictionary = UIFontDictionary(BlockingThreadedResourceLoader(), locale='en') @@ -156,7 +156,7 @@ def test_check_font_preloaded(self, _init_pygame, _display_surface_return_none): loader.start() loader.update() - assert font_dictionary.check_font_preloaded('roboto_regular_14') + assert font_dictionary.check_font_preloaded('roboto_regular_aa_14') if __name__ == '__main__': diff --git a/tests/test_ui_manager.py b/tests/test_ui_manager.py index 0600ce0a..1a40af9f 100644 --- a/tests/test_ui_manager.py +++ b/tests/test_ui_manager.py @@ -271,7 +271,7 @@ def test_print_unused_fonts(self, _init_pygame, default_ui_manager, _display_sur default_ui_manager.print_unused_fonts() captured = capsys.readouterr() - assert captured.out == 'Unused font ids:\nroboto_regular_14(HTML size: 4)\n' + assert captured.out == 'Unused font ids:\nroboto_regular_aa_14(HTML size: 4)\n' def test_focus_and_unfocus_focus_element(self, _init_pygame, default_ui_manager, _display_surface_return_none):