From f87ae43a10e2e1da02433bde259169c4970e7fcd Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Wed, 5 Jul 2023 19:44:30 +0800 Subject: [PATCH] sync pyglet update --- libs/pyglet/font/win32.py | 99 +++++++++++++++++++++++++-------- libs/pyglet/libs/win32/types.py | 1 + 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/libs/pyglet/font/win32.py b/libs/pyglet/font/win32.py index 063d415..67ea3c5 100644 --- a/libs/pyglet/font/win32.py +++ b/libs/pyglet/font/win32.py @@ -4,6 +4,8 @@ import math import warnings from sys import byteorder +from typing import Optional + import pyglet from pyglet.font import base from pyglet.font import win32query @@ -25,7 +27,10 @@ def str_ucs2(text): text = text.encode('utf_16_le') # explicit endian avoids BOM return create_string_buffer(text + '\0') + _debug_dir = 'debug_font' + + def _debug_filename(base, extension): import os if not os.path.exists(_debug_dir): @@ -36,20 +41,24 @@ def _debug_filename(base, extension): num += 1 return name.format(num, extension) + def _debug_image(image, name): filename = _debug_filename(name, 'png') image.save(filename) _debug(f'Saved image {image} to {filename}') + _debug_logfile = None + + def _debug(msg): global _debug_logfile if not _debug_logfile: _debug_logfile = open(_debug_filename('log', 'txt'), 'wt') _debug_logfile.write(msg + '\n') -class Win32GlyphRenderer(base.GlyphRenderer): +class Win32GlyphRenderer(base.GlyphRenderer): def __init__(self, font): self._bitmap = None @@ -67,13 +76,15 @@ class Win32GlyphRenderer(base.GlyphRenderer): gdi32.SelectObject(self._dc, self.font.hfont) - def _create_bitmap(self, width, height): + def _create_bitmap(self, width, height) -> None: pass - def render(self, text): + def render(self, text: str) -> pyglet.font.base.Glyph: raise NotImplementedError('abstract') + class GDIGlyphRenderer(Win32GlyphRenderer): + def __del__(self): try: if self._dc: @@ -83,18 +94,22 @@ class GDIGlyphRenderer(Win32GlyphRenderer): except: pass - def render(self, text): + def render(self, text: str) -> pyglet.font.base.Glyph: # Attempt to get ABC widths (only for TrueType) abc = ABC() - if gdi32.GetCharABCWidthsW(self._dc, - ord(text), ord(text), byref(abc)): + if gdi32.GetCharABCWidthsW( + self._dc, + ord(text), ord(text), byref(abc) + ): width = abc.abcB lsb = abc.abcA advance = abc.abcA + abc.abcB + abc.abcC + else: width_buf = c_int() - gdi32.GetCharWidth32W(self._dc, - ord(text), ord(text), byref(width_buf)) + gdi32.GetCharWidth32W( + self._dc, ord(text), ord(text), byref(width_buf)) + width = width_buf.value lsb = 0 advance = width @@ -120,7 +135,7 @@ class GDIGlyphRenderer(Win32GlyphRenderer): return glyph - def _get_image(self, text, width, height, lsb): + def _get_image(self, text: str, width: int, height: int, lsb: int) -> pyglet.image.ImageData: # There's no such thing as a greyscale bitmap format in GDI. We can # create an 8-bit palette bitmap with 256 shades of grey, but # unfortunately antialiasing will not work on such a bitmap. So, we @@ -134,16 +149,20 @@ class GDIGlyphRenderer(Win32GlyphRenderer): # Draw to DC user32.FillRect(self._dc, byref(self._bitmap_rect), self._black) - gdi32.ExtTextOutA(self._dc, -lsb, 0, 0, None, text, + gdi32.ExtTextOutA( + self._dc, -lsb, 0, 0, None, text, len(text), None) + gdi32.GdiFlush() # Create glyph object and copy bitmap data to texture - image = pyglet.image.ImageData(width, height, + image = pyglet.image.ImageData( + width, height, 'AXXX', self._bitmap_data, self._bitmap_rect.right * 4) + return image - def _create_bitmap(self, width, height): + def _create_bitmap(self, width: int, height: int) -> None: self._black = gdi32.GetStockObject(BLACK_BRUSH) self._white = gdi32.GetStockObject(WHITE_BRUSH) @@ -163,7 +182,8 @@ class GDIGlyphRenderer(Win32GlyphRenderer): info.bmiHeader.biCompression = BI_RGB self._dc = gdi32.CreateCompatibleDC(None) - self._bitmap = gdi32.CreateDIBSection(None, + self._bitmap = gdi32.CreateDIBSection( + None, byref(info), DIB_RGB_COLORS, byref(data), None, 0) # Spookiness: the above line causes a "not enough storage" error, @@ -186,10 +206,17 @@ class GDIGlyphRenderer(Win32GlyphRenderer): _debug(f'pitch = {pitch}') _debug(f'info.bmiHeader.biSize = {info.bmiHeader.biSize}') + class Win32Font(base.Font): + glyph_renderer_class = GDIGlyphRenderer - def __init__(self, name, size, bold=False, italic=False, stretch=False, dpi=None): + def __init__( + self, + name: str, size: int, + bold: bool = False, italic: bool = False, stretch: bool = False, + dpi: Optional[float] = None + ): super(Win32Font, self).__init__() self.logfont = self.get_logfont(name, size, bold, italic, dpi) @@ -208,16 +235,35 @@ class Win32Font(base.Font): gdi32.DeleteObject(self.hfont) @staticmethod - def get_logfont(name, size, bold, italic, dpi): + def get_logfont(name: str, size: float, bold: bool, italic: bool, dpi: Optional[float] = None) -> LOGFONTW: + """Get a raw Win32 :py:class:`.LOGFONTW` struct for the given arguments. + + Args: + name: The name of the font + size: The font size in points + bold: Whether to request the font as bold + italic: Whether to request the font as italic + dpi: The screen dpi + + Returns: + LOGFONTW: a ctypes binding of a Win32 LOGFONTW struct + """ + # Create a dummy DC for coordinate mapping with device_context(None) as dc: + + # Default to 96 DPI unless otherwise specified if dpi is None: dpi = 96 - logpixelsy = dpi + log_pixels_y = dpi + # Create LOGFONTW font description struct logfont = LOGFONTW() - # Conversion of point size to device pixels - logfont.lfHeight = int(-size * logpixelsy // 72) + + # Convert point size to actual device pixels + logfont.lfHeight = int(-size * log_pixels_y // 72) + + # Configure the LOGFONTW's font properties if bold: logfont.lfWeight = FW_BOLD else: @@ -229,7 +275,7 @@ class Win32Font(base.Font): return logfont @classmethod - def have_font(cls, name): + def have_font(cls, name) -> bool: # [ ] add support for loading raster fonts return win32query.have_font(name) @@ -243,8 +289,8 @@ class Win32Font(base.Font): raise ctypes.WinError() -# --- GDI+ font rendering --- +# --- GDI+ font rendering --- from pyglet.image.codecs.gdiplus import PixelFormat32bppARGB, gdiplus, Rect from pyglet.image.codecs.gdiplus import ImageLockModeRead, BitmapData @@ -263,6 +309,7 @@ StringFormatFlagsNoWrap = 0x00001000 StringFormatFlagsLineLimit = 0x00002000 StringFormatFlagsNoClip = 0x00004000 + class Rectf(ctypes.Structure): _fields_ = [ ('x', ctypes.c_float), @@ -271,6 +318,7 @@ class Rectf(ctypes.Structure): ('height', ctypes.c_float), ] + class GDIPlusGlyphRenderer(Win32GlyphRenderer): def __del__(self): try: @@ -305,11 +353,9 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer): gdiplus.GdipSetTextRenderingHint(self._graphics, TextRenderingHintAntiAliasGridFit) - self._brush = ctypes.c_void_p() gdiplus.GdipCreateSolidFill(0xffffffff, ctypes.byref(self._brush)) - self._matrix = ctypes.c_void_p() gdiplus.GdipCreateMatrix(ctypes.byref(self._matrix)) @@ -429,11 +475,13 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer): ctypes.byref(rect), fmt, self._brush) + gdiplus.GdipFlush(self._graphics, 1) gdiplus.GdipDeleteStringFormat(fmt) bitmap_data = BitmapData() - gdiplus.GdipBitmapLockBits(self._bitmap, + gdiplus.GdipBitmapLockBits( + self._bitmap, byref(self._rect), ImageLockModeRead, self._format, byref(bitmap_data)) @@ -445,7 +493,8 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer): # Unlock data gdiplus.GdipBitmapUnlockBits(self._bitmap, byref(bitmap_data)) - image = pyglet.image.ImageData(width, height, + image = pyglet.image.ImageData( + width, height, 'BGRA', buffer, -bitmap_data.Stride) glyph = self.font.create_glyph(image) @@ -454,11 +503,13 @@ class GDIPlusGlyphRenderer(Win32GlyphRenderer): glyph.set_bearings(-self.font.descent, lsb, advance) return glyph + FontStyleBold = 1 FontStyleItalic = 2 UnitPixel = 2 UnitPoint = 3 + class GDIPlusFont(Win32Font): glyph_renderer_class = GDIPlusGlyphRenderer diff --git a/libs/pyglet/libs/win32/types.py b/libs/pyglet/libs/win32/types.py index f0b6fcc..097c32b 100644 --- a/libs/pyglet/libs/win32/types.py +++ b/libs/pyglet/libs/win32/types.py @@ -232,6 +232,7 @@ class LOGFONT(Structure): class LOGFONTW(Structure): + # https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-logfontw _fields_ = [ ('lfHeight', LONG), ('lfWidth', LONG),