feat: update pyglet
This commit is contained in:
parent
c4313c0671
commit
d01503cca5
@ -163,6 +163,7 @@ options = {
|
|||||||
'headless': False,
|
'headless': False,
|
||||||
'headless_device': 0,
|
'headless_device': 0,
|
||||||
'win32_disable_shaping': False,
|
'win32_disable_shaping': False,
|
||||||
|
'dw_legacy_naming': False
|
||||||
}
|
}
|
||||||
|
|
||||||
_option_types = {
|
_option_types = {
|
||||||
@ -192,6 +193,7 @@ _option_types = {
|
|||||||
'headless': bool,
|
'headless': bool,
|
||||||
'headless_device': int,
|
'headless_device': int,
|
||||||
'win32_disable_shaping': bool,
|
'win32_disable_shaping': bool,
|
||||||
|
'dw_legacy_naming': bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
import copy
|
import copy
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import platform
|
||||||
|
from ctypes import *
|
||||||
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
|
import math
|
||||||
import pyglet
|
import pyglet
|
||||||
from pyglet.font import base
|
from pyglet.font import base
|
||||||
from pyglet.util import debug_print
|
from pyglet.image.codecs.wic import IWICBitmap, WICDecoder, GUID_WICPixelFormat32bppPBGRA
|
||||||
from pyglet.image.codecs.wic import IWICBitmap, GUID_WICPixelFormat32bppBGR, WICDecoder, GUID_WICPixelFormat32bppBGRA, \
|
|
||||||
GUID_WICPixelFormat32bppPBGRA
|
|
||||||
|
|
||||||
from pyglet import image
|
|
||||||
import ctypes
|
|
||||||
import math
|
|
||||||
from pyglet.libs.win32 import com
|
|
||||||
from pyglet.libs.win32 import _kernel32 as kernel32
|
from pyglet.libs.win32 import _kernel32 as kernel32
|
||||||
from pyglet.libs.win32 import _ole32 as ole32
|
|
||||||
from pyglet.libs.win32.constants import *
|
from pyglet.libs.win32.constants import *
|
||||||
from pyglet.libs.win32.types import *
|
from pyglet.libs.win32.types import *
|
||||||
from ctypes import *
|
from pyglet.util import debug_print
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dwrite = 'dwrite'
|
dwrite = 'dwrite'
|
||||||
@ -33,7 +29,9 @@ except OSError as err:
|
|||||||
# Doesn't exist? Should stop import of library.
|
# Doesn't exist? Should stop import of library.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
_debug_font = debug_print('debug_font')
|
_debug_font = pyglet.options['debug_font']
|
||||||
|
|
||||||
|
_debug_print = debug_print('debug_font')
|
||||||
|
|
||||||
|
|
||||||
def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d):
|
def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d):
|
||||||
@ -102,6 +100,7 @@ name_to_stretch = {"undefined": DWRITE_FONT_STRETCH_UNDEFINED,
|
|||||||
"semiexpanded": DWRITE_FONT_STRETCH_SEMI_EXPANDED,
|
"semiexpanded": DWRITE_FONT_STRETCH_SEMI_EXPANDED,
|
||||||
"expanded": DWRITE_FONT_STRETCH_EXPANDED,
|
"expanded": DWRITE_FONT_STRETCH_EXPANDED,
|
||||||
"extraexpanded": DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
|
"extraexpanded": DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
|
||||||
|
"narrow": DWRITE_FONT_STRETCH_CONDENSED,
|
||||||
}
|
}
|
||||||
|
|
||||||
DWRITE_GLYPH_IMAGE_FORMATS = c_int
|
DWRITE_GLYPH_IMAGE_FORMATS = c_int
|
||||||
@ -146,6 +145,34 @@ INT32 = c_int32
|
|||||||
UINT32 = c_uint32
|
UINT32 = c_uint32
|
||||||
UINT64 = c_uint64
|
UINT64 = c_uint64
|
||||||
|
|
||||||
|
DWRITE_INFORMATIONAL_STRING_ID = UINT32
|
||||||
|
|
||||||
|
DWRITE_INFORMATIONAL_STRING_NONE = 0
|
||||||
|
DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE = 1
|
||||||
|
DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS = 2
|
||||||
|
DWRITE_INFORMATIONAL_STRING_TRADEMARK = 3
|
||||||
|
DWRITE_INFORMATIONAL_STRING_MANUFACTURER = 4
|
||||||
|
DWRITE_INFORMATIONAL_STRING_DESIGNER = 5
|
||||||
|
DWRITE_INFORMATIONAL_STRING_DESIGNER_URL = 6
|
||||||
|
DWRITE_INFORMATIONAL_STRING_DESCRIPTION = 7
|
||||||
|
DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL = 8
|
||||||
|
DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION = 9
|
||||||
|
DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL = 10
|
||||||
|
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES = 11
|
||||||
|
DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES = 12
|
||||||
|
DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES = 13
|
||||||
|
DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES = 14
|
||||||
|
DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT = 15
|
||||||
|
DWRITE_INFORMATIONAL_STRING_FULL_NAME = 16
|
||||||
|
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME = 17
|
||||||
|
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME = 18
|
||||||
|
DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME = 19
|
||||||
|
DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG = 20
|
||||||
|
DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG = 21
|
||||||
|
DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES = 22
|
||||||
|
DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES = 23
|
||||||
|
DWRITE_INFORMATIONAL_STRING_WWS_FAMILY_NAME = 24
|
||||||
|
|
||||||
|
|
||||||
class D2D_POINT_2F(Structure):
|
class D2D_POINT_2F(Structure):
|
||||||
_fields_ = (
|
_fields_ = (
|
||||||
@ -236,12 +263,64 @@ class DWRITE_CLUSTER_METRICS(ctypes.Structure):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteFontFileStream(com.IUnknown):
|
||||||
|
_methods_ = [
|
||||||
|
('ReadFileFragment',
|
||||||
|
com.STDMETHOD(c_void_p, POINTER(c_void_p), UINT64, UINT64, POINTER(c_void_p))),
|
||||||
|
('ReleaseFileFragment',
|
||||||
|
com.STDMETHOD(c_void_p, c_void_p)),
|
||||||
|
('GetFileSize',
|
||||||
|
com.STDMETHOD(c_void_p, POINTER(UINT64))),
|
||||||
|
('GetLastWriteTime',
|
||||||
|
com.STDMETHOD(c_void_p, POINTER(UINT64))),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteFontFileLoader_LI(com.IUnknown): # Local implementation use only.
|
||||||
|
_methods_ = [
|
||||||
|
('CreateStreamFromKey',
|
||||||
|
com.STDMETHOD(c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream))))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteFontFileLoader(com.pIUnknown):
|
||||||
|
_methods_ = [
|
||||||
|
('CreateStreamFromKey',
|
||||||
|
com.STDMETHOD(c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream))))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteLocalFontFileLoader(IDWriteFontFileLoader, com.pIUnknown):
|
||||||
|
_methods_ = [
|
||||||
|
('GetFilePathLengthFromKey',
|
||||||
|
com.STDMETHOD(c_void_p, UINT32, POINTER(UINT32))),
|
||||||
|
('GetFilePathFromKey',
|
||||||
|
com.STDMETHOD(c_void_p, UINT32, c_wchar_p, UINT32)),
|
||||||
|
('GetLastWriteTimeFromKey',
|
||||||
|
com.STDMETHOD())
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
IID_IDWriteLocalFontFileLoader = com.GUID(0xb2d9f3ec, 0xc9fe, 0x4a11, 0xa2, 0xec, 0xd8, 0x62, 0x08, 0xf7, 0xc0, 0xa2)
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteFontFile(com.pIUnknown):
|
||||||
|
_methods_ = [
|
||||||
|
('GetReferenceKey',
|
||||||
|
com.STDMETHOD(POINTER(c_void_p), POINTER(UINT32))),
|
||||||
|
('GetLoader',
|
||||||
|
com.STDMETHOD(POINTER(IDWriteFontFileLoader))),
|
||||||
|
('Analyze',
|
||||||
|
com.STDMETHOD()),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class IDWriteFontFace(com.pIUnknown):
|
class IDWriteFontFace(com.pIUnknown):
|
||||||
_methods_ = [
|
_methods_ = [
|
||||||
('GetType',
|
('GetType',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD()),
|
||||||
('GetFiles',
|
('GetFiles',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD(POINTER(UINT32), POINTER(IDWriteFontFile))),
|
||||||
('GetIndex',
|
('GetIndex',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD()),
|
||||||
('GetSimulations',
|
('GetSimulations',
|
||||||
@ -582,9 +661,9 @@ class IDWriteFontList(com.pIUnknown):
|
|||||||
('GetFontCollection',
|
('GetFontCollection',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD()),
|
||||||
('GetFontCount',
|
('GetFontCount',
|
||||||
com.STDMETHOD()),
|
com.METHOD(UINT32)),
|
||||||
('GetFont',
|
('GetFont',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD(UINT32, c_void_p)), # IDWriteFont, use void because of forward ref.
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -610,33 +689,22 @@ class IDWriteFontFamily1(IDWriteFontFamily, IDWriteFontList, com.pIUnknown):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class IDWriteFontFile(com.pIUnknown):
|
|
||||||
_methods_ = [
|
|
||||||
('GetReferenceKey',
|
|
||||||
com.STDMETHOD()),
|
|
||||||
('GetLoader',
|
|
||||||
com.STDMETHOD()),
|
|
||||||
('Analyze',
|
|
||||||
com.STDMETHOD()),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IDWriteFont(com.pIUnknown):
|
class IDWriteFont(com.pIUnknown):
|
||||||
_methods_ = [
|
_methods_ = [
|
||||||
('GetFontFamily',
|
('GetFontFamily',
|
||||||
com.STDMETHOD(POINTER(IDWriteFontFamily))),
|
com.STDMETHOD(POINTER(IDWriteFontFamily))),
|
||||||
('GetWeight',
|
('GetWeight',
|
||||||
com.STDMETHOD()),
|
com.METHOD(DWRITE_FONT_WEIGHT)),
|
||||||
('GetStretch',
|
('GetStretch',
|
||||||
com.STDMETHOD()),
|
com.METHOD(DWRITE_FONT_STRETCH)),
|
||||||
('GetStyle',
|
('GetStyle',
|
||||||
com.STDMETHOD()),
|
com.METHOD(DWRITE_FONT_STYLE)),
|
||||||
('IsSymbolFont',
|
('IsSymbolFont',
|
||||||
com.STDMETHOD()),
|
com.METHOD(BOOL)),
|
||||||
('GetFaceNames',
|
('GetFaceNames',
|
||||||
com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
|
com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
|
||||||
('GetInformationalStrings',
|
('GetInformationalStrings',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD(DWRITE_INFORMATIONAL_STRING_ID, POINTER(IDWriteLocalizedStrings), POINTER(BOOL))),
|
||||||
('GetSimulations',
|
('GetSimulations',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD()),
|
||||||
('GetMetrics',
|
('GetMetrics',
|
||||||
@ -664,7 +732,7 @@ class IDWriteFont1(IDWriteFont, com.pIUnknown):
|
|||||||
class IDWriteFontCollection(com.pIUnknown):
|
class IDWriteFontCollection(com.pIUnknown):
|
||||||
_methods_ = [
|
_methods_ = [
|
||||||
('GetFontFamilyCount',
|
('GetFontFamilyCount',
|
||||||
com.STDMETHOD()),
|
com.METHOD(UINT32)),
|
||||||
('GetFontFamily',
|
('GetFontFamily',
|
||||||
com.STDMETHOD(UINT32, POINTER(IDWriteFontFamily))),
|
com.STDMETHOD(UINT32, POINTER(IDWriteFontFamily))),
|
||||||
('FindFamilyName',
|
('FindFamilyName',
|
||||||
@ -690,6 +758,21 @@ DWRITE_TEXT_ALIGNMENT_CENTER = 3
|
|||||||
DWRITE_TEXT_ALIGNMENT_JUSTIFIED = 4
|
DWRITE_TEXT_ALIGNMENT_JUSTIFIED = 4
|
||||||
|
|
||||||
|
|
||||||
|
class IDWriteGdiInterop(com.pIUnknown):
|
||||||
|
_methods_ = [
|
||||||
|
('CreateFontFromLOGFONT',
|
||||||
|
com.STDMETHOD(POINTER(LOGFONTW), POINTER(IDWriteFont))),
|
||||||
|
('ConvertFontToLOGFONT',
|
||||||
|
com.STDMETHOD()),
|
||||||
|
('ConvertFontFaceToLOGFONT',
|
||||||
|
com.STDMETHOD()),
|
||||||
|
('CreateFontFaceFromHdc',
|
||||||
|
com.STDMETHOD(HDC, POINTER(IDWriteFontFace))),
|
||||||
|
('CreateBitmapRenderTarget',
|
||||||
|
com.STDMETHOD())
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class IDWriteTextFormat(com.pIUnknown):
|
class IDWriteTextFormat(com.pIUnknown):
|
||||||
_methods_ = [
|
_methods_ = [
|
||||||
('SetTextAlignment',
|
('SetTextAlignment',
|
||||||
@ -884,19 +967,6 @@ class IDWriteFontCollectionLoader(com.IUnknown):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class IDWriteFontFileStream(com.IUnknown):
|
|
||||||
_methods_ = [
|
|
||||||
('ReadFileFragment',
|
|
||||||
com.STDMETHOD(c_void_p, POINTER(c_void_p), UINT64, UINT64, POINTER(c_void_p))),
|
|
||||||
('ReleaseFileFragment',
|
|
||||||
com.STDMETHOD(c_void_p, c_void_p)),
|
|
||||||
('GetFileSize',
|
|
||||||
com.STDMETHOD(c_void_p, POINTER(UINT64))),
|
|
||||||
('GetLastWriteTime',
|
|
||||||
com.STDMETHOD(c_void_p, POINTER(UINT64))),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MyFontFileStream(com.COMObject):
|
class MyFontFileStream(com.COMObject):
|
||||||
_interfaces_ = [IDWriteFontFileStream]
|
_interfaces_ = [IDWriteFontFileStream]
|
||||||
|
|
||||||
@ -938,15 +1008,8 @@ class MyFontFileStream(com.COMObject):
|
|||||||
return 0x80004001 # E_NOTIMPL
|
return 0x80004001 # E_NOTIMPL
|
||||||
|
|
||||||
|
|
||||||
class IDWriteFontFileLoader(com.IUnknown):
|
|
||||||
_methods_ = [
|
|
||||||
('CreateStreamFromKey',
|
|
||||||
com.STDMETHOD(c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream))))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class LegacyFontFileLoader(com.COMObject):
|
class LegacyFontFileLoader(com.COMObject):
|
||||||
_interfaces_ = [IDWriteFontFileLoader]
|
_interfaces_ = [IDWriteFontFileLoader_LI]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._streams = {}
|
self._streams = {}
|
||||||
@ -1089,7 +1152,7 @@ class IDWriteFactory(com.pIUnknown):
|
|||||||
('CreateFontFileReference',
|
('CreateFontFileReference',
|
||||||
com.STDMETHOD(c_wchar_p, c_void_p, POINTER(IDWriteFontFile))),
|
com.STDMETHOD(c_wchar_p, c_void_p, POINTER(IDWriteFontFile))),
|
||||||
('CreateCustomFontFileReference',
|
('CreateCustomFontFileReference',
|
||||||
com.STDMETHOD(c_void_p, UINT32, POINTER(IDWriteFontFileLoader), POINTER(IDWriteFontFile))),
|
com.STDMETHOD(c_void_p, UINT32, POINTER(IDWriteFontFileLoader_LI), POINTER(IDWriteFontFile))),
|
||||||
('CreateFontFace',
|
('CreateFontFace',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD()),
|
||||||
('CreateRenderingParams',
|
('CreateRenderingParams',
|
||||||
@ -1101,14 +1164,14 @@ class IDWriteFactory(com.pIUnknown):
|
|||||||
('RegisterFontFileLoader',
|
('RegisterFontFileLoader',
|
||||||
com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown.
|
com.STDMETHOD(c_void_p)), # Ambigious as newer is a pIUnknown and legacy is IUnknown.
|
||||||
('UnregisterFontFileLoader',
|
('UnregisterFontFileLoader',
|
||||||
com.STDMETHOD(POINTER(IDWriteFontFileLoader))),
|
com.STDMETHOD(POINTER(IDWriteFontFileLoader_LI))),
|
||||||
('CreateTextFormat',
|
('CreateTextFormat',
|
||||||
com.STDMETHOD(c_wchar_p, IDWriteFontCollection, DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH,
|
com.STDMETHOD(c_wchar_p, IDWriteFontCollection, DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH,
|
||||||
FLOAT, c_wchar_p, POINTER(IDWriteTextFormat))),
|
FLOAT, c_wchar_p, POINTER(IDWriteTextFormat))),
|
||||||
('CreateTypography',
|
('CreateTypography',
|
||||||
com.STDMETHOD(POINTER(IDWriteTypography))),
|
com.STDMETHOD(POINTER(IDWriteTypography))),
|
||||||
('GetGdiInterop',
|
('GetGdiInterop',
|
||||||
com.STDMETHOD()),
|
com.STDMETHOD(POINTER(IDWriteGdiInterop))),
|
||||||
('CreateTextLayout',
|
('CreateTextLayout',
|
||||||
com.STDMETHOD(c_wchar_p, UINT32, IDWriteTextFormat, FLOAT, FLOAT, POINTER(IDWriteTextLayout))),
|
com.STDMETHOD(c_wchar_p, UINT32, IDWriteTextFormat, FLOAT, FLOAT, POINTER(IDWriteTextLayout))),
|
||||||
('CreateGdiCompatibleTextLayout',
|
('CreateGdiCompatibleTextLayout',
|
||||||
@ -1548,6 +1611,13 @@ if not wic_decoder:
|
|||||||
raise Exception("Cannot use DirectWrite without a WIC Decoder")
|
raise Exception("Cannot use DirectWrite without a WIC Decoder")
|
||||||
|
|
||||||
|
|
||||||
|
def get_system_locale() -> str:
|
||||||
|
"""Retrieve the string representing the system locale."""
|
||||||
|
local_name = create_unicode_buffer(LOCALE_NAME_MAX_LENGTH)
|
||||||
|
kernel32.GetUserDefaultLocaleName(local_name, LOCALE_NAME_MAX_LENGTH)
|
||||||
|
return local_name.value
|
||||||
|
|
||||||
|
|
||||||
class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
||||||
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
|
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
|
||||||
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE
|
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE
|
||||||
@ -1782,6 +1852,11 @@ class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
|||||||
# Negative LSB: we shift the offset, otherwise the glyph will be cut off.
|
# Negative LSB: we shift the offset, otherwise the glyph will be cut off.
|
||||||
render_offset_x = glyph_lsb * self.font.font_scale_ratio
|
render_offset_x = glyph_lsb * self.font.font_scale_ratio
|
||||||
|
|
||||||
|
# Increase width by arbitrary amount to accommodate size of italic.
|
||||||
|
# No way to get actual size of italics outside of rendering to larger texture and checking pixels.
|
||||||
|
if self.font.italic:
|
||||||
|
render_width += (render_width // 2)
|
||||||
|
|
||||||
# Create new bitmap.
|
# Create new bitmap.
|
||||||
# TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
|
# TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
|
||||||
# Note: Floating point precision makes this a giant headache, will need to be solved for this approach.
|
# Note: Floating point precision makes this a giant headache, will need to be solved for this approach.
|
||||||
@ -1912,6 +1987,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
texture_internalformat = pyglet.gl.GL_RGBA
|
texture_internalformat = pyglet.gl.GL_RGBA
|
||||||
|
|
||||||
def __init__(self, name, size, bold=False, italic=False, stretch=False, dpi=None, locale=None):
|
def __init__(self, name, size, bold=False, italic=False, stretch=False, dpi=None, locale=None):
|
||||||
|
self._filename: Optional[str] = None
|
||||||
self._advance_cache = {} # Stores glyph's by the indice and advance.
|
self._advance_cache = {} # Stores glyph's by the indice and advance.
|
||||||
|
|
||||||
super(Win32DirectWriteFont, self).__init__()
|
super(Win32DirectWriteFont, self).__init__()
|
||||||
@ -1919,9 +1995,6 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
if not name:
|
if not name:
|
||||||
name = self._default_name
|
name = self._default_name
|
||||||
|
|
||||||
self._font_index, self._collection = self.get_collection(name)
|
|
||||||
assert self._collection is not None, "Font: {} not found in loaded or system font collection.".format(name)
|
|
||||||
|
|
||||||
self._name = name
|
self._name = name
|
||||||
self.bold = bold
|
self.bold = bold
|
||||||
self.size = size
|
self.size = size
|
||||||
@ -1965,6 +2038,27 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
else:
|
else:
|
||||||
self._stretch = DWRITE_FONT_STRETCH_NORMAL
|
self._stretch = DWRITE_FONT_STRETCH_NORMAL
|
||||||
|
|
||||||
|
self._font_index, self._collection = self.get_collection(name)
|
||||||
|
write_font = None
|
||||||
|
# If not font found, search all collections for legacy GDI naming.
|
||||||
|
if pyglet.options["dw_legacy_naming"]:
|
||||||
|
if self._font_index is None and self._collection is None:
|
||||||
|
write_font, self._collection = self.find_font_face(name, self._weight, self._style, self._stretch)
|
||||||
|
|
||||||
|
assert self._collection is not None, f"Font: '{name}' not found in loaded or system font collection."
|
||||||
|
|
||||||
|
if self._font_index is not None:
|
||||||
|
font_family = IDWriteFontFamily1()
|
||||||
|
self._collection.GetFontFamily(self._font_index, byref(font_family))
|
||||||
|
|
||||||
|
write_font = IDWriteFont()
|
||||||
|
font_family.GetFirstMatchingFont(
|
||||||
|
self._weight,
|
||||||
|
self._stretch,
|
||||||
|
self._style,
|
||||||
|
byref(write_font)
|
||||||
|
)
|
||||||
|
|
||||||
# Create the text format this font will use permanently.
|
# Create the text format this font will use permanently.
|
||||||
# Could technically be recreated, but will keep to be inline with other font objects.
|
# Could technically be recreated, but will keep to be inline with other font objects.
|
||||||
self._text_format = IDWriteTextFormat()
|
self._text_format = IDWriteTextFormat()
|
||||||
@ -1979,18 +2073,6 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
byref(self._text_format)
|
byref(self._text_format)
|
||||||
)
|
)
|
||||||
|
|
||||||
# All this work just to get a font face and its metrics!
|
|
||||||
font_family = IDWriteFontFamily1()
|
|
||||||
self._collection.GetFontFamily(self._font_index, byref(font_family))
|
|
||||||
|
|
||||||
write_font = IDWriteFont()
|
|
||||||
font_family.GetFirstMatchingFont(
|
|
||||||
self._weight,
|
|
||||||
self._stretch,
|
|
||||||
self._style,
|
|
||||||
byref(write_font)
|
|
||||||
)
|
|
||||||
|
|
||||||
font_face = IDWriteFontFace()
|
font_face = IDWriteFontFace()
|
||||||
write_font.CreateFontFace(byref(font_face))
|
write_font.CreateFontFace(byref(font_face))
|
||||||
|
|
||||||
@ -2013,7 +2095,54 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
self._fallback = IDWriteFontFallback()
|
self._fallback = IDWriteFontFallback()
|
||||||
self._write_factory.GetSystemFontFallback(byref(self._fallback))
|
self._write_factory.GetSystemFontFallback(byref(self._fallback))
|
||||||
else:
|
else:
|
||||||
assert _debug_font("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.")
|
assert _debug_print("Windows 8.1+ is required for font fallback. Colored glyphs cannot be omitted.")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filename(self):
|
||||||
|
"""Returns a filename associated with the font face.
|
||||||
|
Note: Capable of returning more than 1 file in the future, but will do just one for now."""
|
||||||
|
if self._filename is not None:
|
||||||
|
return self._filename
|
||||||
|
|
||||||
|
file_ct = UINT32()
|
||||||
|
self.font_face.GetFiles(byref(file_ct), None)
|
||||||
|
|
||||||
|
font_files = (IDWriteFontFile * file_ct.value)()
|
||||||
|
|
||||||
|
self.font_face.GetFiles(byref(file_ct), font_files)
|
||||||
|
|
||||||
|
self._filename = "Not Available"
|
||||||
|
|
||||||
|
pff = font_files[0]
|
||||||
|
|
||||||
|
key_data = c_void_p()
|
||||||
|
ff_key_size = UINT32()
|
||||||
|
|
||||||
|
pff.GetReferenceKey(byref(key_data), byref(ff_key_size))
|
||||||
|
|
||||||
|
loader = IDWriteFontFileLoader()
|
||||||
|
pff.GetLoader(byref(loader))
|
||||||
|
|
||||||
|
try:
|
||||||
|
local_loader = IDWriteLocalFontFileLoader()
|
||||||
|
loader.QueryInterface(IID_IDWriteLocalFontFileLoader, byref(local_loader))
|
||||||
|
except OSError: # E_NOTIMPL
|
||||||
|
loader.Release()
|
||||||
|
pff.Release()
|
||||||
|
return self._filename
|
||||||
|
|
||||||
|
path_len = UINT32()
|
||||||
|
local_loader.GetFilePathLengthFromKey(key_data, ff_key_size, byref(path_len))
|
||||||
|
|
||||||
|
buffer = create_unicode_buffer(path_len.value + 1)
|
||||||
|
local_loader.GetFilePathFromKey(key_data, ff_key_size, buffer, len(buffer))
|
||||||
|
|
||||||
|
loader.Release()
|
||||||
|
local_loader.Release()
|
||||||
|
pff.Release()
|
||||||
|
|
||||||
|
self._filename = pathlib.PureWindowsPath(buffer.value).as_posix() # Convert to forward slashes.
|
||||||
|
return self._filename
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -2176,7 +2305,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
for idx in clusters:
|
for idx in clusters:
|
||||||
ct = formatted_clusters.count(idx)
|
ct = formatted_clusters.count(idx)
|
||||||
if ct > 1:
|
if ct > 1:
|
||||||
substitutions[idx] = ct-1
|
substitutions[idx] = ct - 1
|
||||||
|
|
||||||
for i in range(actual_count):
|
for i in range(actual_count):
|
||||||
indice = indices[i]
|
indice = indices[i]
|
||||||
@ -2258,7 +2387,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
|
|
||||||
# Note: RegisterFontLoader takes a pointer. However, for legacy we implement our own callback interface.
|
# Note: RegisterFontLoader takes a pointer. However, for legacy we implement our own callback interface.
|
||||||
# Therefore we need to pass to the actual pointer directly.
|
# Therefore we need to pass to the actual pointer directly.
|
||||||
cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader])
|
cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader_LI])
|
||||||
|
|
||||||
cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
|
cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
|
||||||
cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
|
cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
|
||||||
@ -2297,6 +2426,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
|
|
||||||
cls._custom_collection = IDWriteFontCollection1()
|
cls._custom_collection = IDWriteFontCollection1()
|
||||||
cls._write_factory.CreateFontCollectionFromFontSet(cls._font_set, byref(cls._custom_collection))
|
cls._write_factory.CreateFontCollectionFromFontSet(cls._font_set, byref(cls._custom_collection))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cls._font_cache.append(data)
|
cls._font_cache.append(data)
|
||||||
|
|
||||||
@ -2311,7 +2441,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
|
cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
|
||||||
|
|
||||||
cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
|
cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
|
||||||
cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader])
|
cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader_LI])
|
||||||
|
|
||||||
cls._font_collection_loader.AddFontData(cls._font_cache)
|
cls._font_collection_loader.AddFontData(cls._font_cache)
|
||||||
|
|
||||||
@ -2323,7 +2453,7 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
byref(cls._custom_collection))
|
byref(cls._custom_collection))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_collection(cls, font_name):
|
def get_collection(cls, font_name) -> Tuple[Optional[int], Optional[IDWriteFontCollection1]]:
|
||||||
"""Returns which collection this font belongs to (system or custom collection), as well as its index in the
|
"""Returns which collection this font belongs to (system or custom collection), as well as its index in the
|
||||||
collection."""
|
collection."""
|
||||||
if not cls._write_factory:
|
if not cls._write_factory:
|
||||||
@ -2344,8 +2474,8 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
|
|
||||||
# Check if font is in the system collection.
|
# Check if font is in the system collection.
|
||||||
# Do not cache these values permanently as system font collection can be updated during runtime.
|
# Do not cache these values permanently as system font collection can be updated during runtime.
|
||||||
|
sys_collection = IDWriteFontCollection()
|
||||||
if not font_exists.value:
|
if not font_exists.value:
|
||||||
sys_collection = IDWriteFontCollection()
|
|
||||||
cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
|
cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
|
||||||
sys_collection.FindFamilyName(create_unicode_buffer(font_name),
|
sys_collection.FindFamilyName(create_unicode_buffer(font_name),
|
||||||
byref(font_index),
|
byref(font_index),
|
||||||
@ -2354,57 +2484,245 @@ class Win32DirectWriteFont(base.Font):
|
|||||||
if font_exists.value:
|
if font_exists.value:
|
||||||
return font_index.value, sys_collection
|
return font_index.value, sys_collection
|
||||||
|
|
||||||
# Font does not exist in either custom or system.
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def have_font(cls, name):
|
def find_font_face(cls, font_name, bold, italic, stretch) -> Tuple[
|
||||||
|
Optional[IDWriteFont], Optional[IDWriteFontCollection]]:
|
||||||
|
"""This will search font collections for legacy RBIZ names. However, matching to bold, italic, stretch is
|
||||||
|
problematic in that there are many values. We parse the font name looking for matches to the name database,
|
||||||
|
and attempt to pick the closest match.
|
||||||
|
This will search all fonts on the system and custom loaded, and all of their font faces. Returns a collection
|
||||||
|
and IDWriteFont if successful.
|
||||||
|
"""
|
||||||
|
p_bold, p_italic, p_stretch = cls.parse_name(font_name, bold, italic, stretch)
|
||||||
|
|
||||||
|
_debug_print(f"directwrite: '{font_name}' not found. Attempting legacy name lookup in all collections.")
|
||||||
|
collection_idx = cls.find_legacy_font(cls._custom_collection, font_name, p_bold, p_italic, p_stretch)
|
||||||
|
if collection_idx is not None:
|
||||||
|
return collection_idx, cls._custom_collection
|
||||||
|
|
||||||
|
sys_collection = IDWriteFontCollection()
|
||||||
|
cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
|
||||||
|
|
||||||
|
collection_idx = cls.find_legacy_font(sys_collection, font_name, p_bold, p_italic, p_stretch)
|
||||||
|
if collection_idx is not None:
|
||||||
|
return collection_idx, sys_collection
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def have_font(cls, name: str):
|
||||||
if cls.get_collection(name)[0] is not None:
|
if cls.get_collection(name)[0] is not None:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def get_font_face(cls, name):
|
def parse_name(font_name: str, weight: int, style: int, stretch: int):
|
||||||
# Check custom collection.
|
"""Attempt at parsing any special names in a font for legacy checks. Takes the first found."""
|
||||||
collection = None
|
|
||||||
font_index = UINT()
|
|
||||||
font_exists = BOOL()
|
|
||||||
|
|
||||||
# Check custom collection.
|
font_name = font_name.lower()
|
||||||
if cls._custom_collection:
|
split_name = font_name.split(' ')
|
||||||
cls._custom_collection.FindFamilyName(create_unicode_buffer(name),
|
|
||||||
byref(font_index),
|
|
||||||
byref(font_exists))
|
|
||||||
|
|
||||||
collection = cls._custom_collection
|
found_weight = weight
|
||||||
|
found_style = style
|
||||||
|
found_stretch = stretch
|
||||||
|
|
||||||
if font_exists.value == 0:
|
# Only search if name is split more than once.
|
||||||
sys_collection = IDWriteFontCollection()
|
if len(split_name) > 1:
|
||||||
cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
|
for name, value in name_to_weight.items():
|
||||||
sys_collection.FindFamilyName(create_unicode_buffer(name),
|
if name in split_name:
|
||||||
byref(font_index),
|
found_weight = value
|
||||||
byref(font_exists))
|
break
|
||||||
|
|
||||||
collection = sys_collection
|
for name, value in name_to_style.items():
|
||||||
|
if name in split_name:
|
||||||
|
found_style = value
|
||||||
|
break
|
||||||
|
|
||||||
if font_exists:
|
for name, value in name_to_stretch.items():
|
||||||
font_family = IDWriteFontFamily()
|
if name in split_name:
|
||||||
collection.GetFontFamily(font_index, byref(font_family))
|
found_stretch = value
|
||||||
|
break
|
||||||
|
|
||||||
write_font = IDWriteFont()
|
return found_weight, found_style, found_stretch
|
||||||
font_family.GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL,
|
|
||||||
DWRITE_FONT_STRETCH_NORMAL,
|
|
||||||
DWRITE_FONT_STYLE_NORMAL,
|
|
||||||
byref(write_font))
|
|
||||||
|
|
||||||
font_face = IDWriteFontFace1()
|
@staticmethod
|
||||||
write_font.CreateFontFace(byref(font_face))
|
def find_legacy_font(collection: IDWriteFontCollection, font_name: str, bold, italic, stretch, full_debug=False) -> \
|
||||||
|
Optional[IDWriteFont]:
|
||||||
|
coll_count = collection.GetFontFamilyCount()
|
||||||
|
|
||||||
return font_face
|
assert _debug_print(f"directwrite: Found {coll_count} fonts in collection.")
|
||||||
|
|
||||||
|
locale = get_system_locale()
|
||||||
|
|
||||||
|
for i in range(coll_count):
|
||||||
|
family = IDWriteFontFamily()
|
||||||
|
collection.GetFontFamily(i, byref(family))
|
||||||
|
|
||||||
|
# Just check the first character in Family Names to reduce search time. Arial -> A's only.
|
||||||
|
family_name_str = IDWriteLocalizedStrings()
|
||||||
|
family.GetFamilyNames(byref(family_name_str))
|
||||||
|
|
||||||
|
family_names = Win32DirectWriteFont.unpack_localized_string(family_name_str, locale)
|
||||||
|
family_name = family_names[0]
|
||||||
|
|
||||||
|
if family_name[0] != font_name[0]:
|
||||||
|
family.Release()
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert _debug_print(f"directwrite: Inspecting family name: {family_name}")
|
||||||
|
|
||||||
|
# Fonts in the family. Full search to search all font faces, typically the first will be good enough to tell
|
||||||
|
ft_ct = family.GetFontCount()
|
||||||
|
|
||||||
|
face_names = []
|
||||||
|
matches = []
|
||||||
|
for j in range(ft_ct):
|
||||||
|
temp_ft = IDWriteFont()
|
||||||
|
family.GetFont(j, byref(temp_ft))
|
||||||
|
|
||||||
|
if _debug_font and full_debug:
|
||||||
|
fc_str = IDWriteLocalizedStrings()
|
||||||
|
temp_ft.GetFaceNames(byref(fc_str))
|
||||||
|
|
||||||
|
strings = Win32DirectWriteFont.unpack_localized_string(fc_str, locale)
|
||||||
|
face_names.extend(strings)
|
||||||
|
|
||||||
|
print(f"directwrite: Face names found: {strings}")
|
||||||
|
|
||||||
|
# Check for GDI compatibility name
|
||||||
|
compat_names = IDWriteLocalizedStrings()
|
||||||
|
exists = BOOL()
|
||||||
|
temp_ft.GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
|
||||||
|
byref(compat_names),
|
||||||
|
byref(exists))
|
||||||
|
|
||||||
|
# Successful in finding GDI name.
|
||||||
|
match_found = False
|
||||||
|
if exists.value != 0:
|
||||||
|
for compat_name in Win32DirectWriteFont.unpack_localized_string(compat_names, locale):
|
||||||
|
if compat_name == font_name:
|
||||||
|
assert _debug_print(
|
||||||
|
f"Found legacy name '{font_name}' as '{family_name}' in font face '{j}' (collection id #{i}).")
|
||||||
|
|
||||||
|
match_found = True
|
||||||
|
matches.append((temp_ft.GetWeight(), temp_ft.GetStyle(), temp_ft.GetStretch(), temp_ft))
|
||||||
|
break
|
||||||
|
|
||||||
|
# Release resource if not a match.
|
||||||
|
if not match_found:
|
||||||
|
temp_ft.Release()
|
||||||
|
|
||||||
|
family.Release()
|
||||||
|
|
||||||
|
# If we have matches, we've already parsed through the proper family. Now try to match.
|
||||||
|
if matches:
|
||||||
|
write_font = Win32DirectWriteFont.match_closest_font(matches, bold, italic, stretch)
|
||||||
|
|
||||||
|
# Cleanup other matches not used.
|
||||||
|
for match in matches:
|
||||||
|
if match[3] != write_font:
|
||||||
|
match[3].Release() # Release all other matches.
|
||||||
|
|
||||||
|
return write_font
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def match_closest_font(font_list: List[Tuple[int, int, int, IDWriteFont]], bold: int, italic: int, stretch: int) -> \
|
||||||
|
Optional[IDWriteFont]:
|
||||||
|
"""Match the closest font to the parameters specified. If a full match is not found, a secondary match will be
|
||||||
|
found based on similar features. This can probably be improved, but it is possible you could get a different
|
||||||
|
font style than expected."""
|
||||||
|
closest = []
|
||||||
|
for match in font_list:
|
||||||
|
(f_weight, f_style, f_stretch, writefont) = match
|
||||||
|
|
||||||
|
# Found perfect match, no need for the rest.
|
||||||
|
if f_weight == bold and f_style == italic and f_stretch == stretch:
|
||||||
|
_debug_print(
|
||||||
|
f"directwrite: full match found. (bold: {f_weight}, italic: {f_style}, stretch: {f_stretch})")
|
||||||
|
return writefont
|
||||||
|
|
||||||
|
prop_match = 0
|
||||||
|
similar_match = 0
|
||||||
|
# Look for a full match, otherwise look for close enough.
|
||||||
|
# For example, Arial Black only has Oblique, not Italic, but good enough if you want slanted text.
|
||||||
|
if f_weight == bold:
|
||||||
|
prop_match += 1
|
||||||
|
elif bold != DWRITE_FONT_WEIGHT_NORMAL and f_weight != DWRITE_FONT_WEIGHT_NORMAL:
|
||||||
|
similar_match += 1
|
||||||
|
|
||||||
|
if f_style == italic:
|
||||||
|
prop_match += 1
|
||||||
|
elif italic != DWRITE_FONT_STYLE_NORMAL and f_style != DWRITE_FONT_STYLE_NORMAL:
|
||||||
|
similar_match += 1
|
||||||
|
|
||||||
|
if stretch == f_stretch:
|
||||||
|
prop_match += 1
|
||||||
|
elif stretch != DWRITE_FONT_STRETCH_NORMAL and f_stretch != DWRITE_FONT_STRETCH_NORMAL:
|
||||||
|
similar_match += 1
|
||||||
|
|
||||||
|
closest.append((prop_match, similar_match, *match))
|
||||||
|
|
||||||
|
# If we get here, no perfect match, sort by highest perfect match, to secondary matches.
|
||||||
|
closest.sort(key=lambda fts: (fts[0], fts[1]), reverse=True)
|
||||||
|
|
||||||
|
if closest:
|
||||||
|
# Take the first match after sorting.
|
||||||
|
closest_match = closest[0]
|
||||||
|
_debug_print(f"directwrite: falling back to partial match. "
|
||||||
|
f"(bold: {closest_match[2]}, italic: {closest_match[3]}, stretch: {closest_match[4]})")
|
||||||
|
return closest_match[5]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unpack_localized_string(local_string: IDWriteLocalizedStrings, locale: str) -> List[str]:
|
||||||
|
"""Takes IDWriteLocalizedStrings and unpacks the strings inside of it into a list."""
|
||||||
|
str_array_len = local_string.GetCount()
|
||||||
|
|
||||||
|
strings = []
|
||||||
|
for _ in range(str_array_len):
|
||||||
|
string_size = UINT32()
|
||||||
|
|
||||||
|
idx = Win32DirectWriteFont.get_localized_index(local_string, locale)
|
||||||
|
|
||||||
|
local_string.GetStringLength(idx, byref(string_size))
|
||||||
|
|
||||||
|
buffer_size = string_size.value
|
||||||
|
|
||||||
|
buffer = create_unicode_buffer(buffer_size + 1)
|
||||||
|
|
||||||
|
local_string.GetString(idx, buffer, len(buffer))
|
||||||
|
|
||||||
|
strings.append(buffer.value)
|
||||||
|
|
||||||
|
local_string.Release()
|
||||||
|
|
||||||
|
return strings
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_localized_index(strings: IDWriteLocalizedStrings, locale: str):
|
||||||
|
idx = UINT32()
|
||||||
|
exists = BOOL()
|
||||||
|
|
||||||
|
if locale:
|
||||||
|
strings.FindLocaleName(locale, byref(idx), byref(exists))
|
||||||
|
|
||||||
|
if not exists.value:
|
||||||
|
# fallback to english.
|
||||||
|
strings.FindLocaleName('en-us', byref(idx), byref(exists))
|
||||||
|
|
||||||
|
if not exists:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return idx.value
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
d2d_factory = ID2D1Factory()
|
d2d_factory = ID2D1Factory()
|
||||||
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, None, byref(d2d_factory))
|
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, None, byref(d2d_factory))
|
||||||
|
@ -194,10 +194,9 @@ class CocoaConfig(Config):
|
|||||||
elif _os_x_version >= os_x_release['lion']:
|
elif _os_x_version >= os_x_release['lion']:
|
||||||
# check for opengl profile
|
# check for opengl profile
|
||||||
# This requires OS-X Lion (Darwin 11) or higher
|
# This requires OS-X Lion (Darwin 11) or higher
|
||||||
version = (
|
version = (getattr(self, 'major_version', None) or 3,
|
||||||
getattr(self, 'major_version', None) or 2,
|
getattr(self, 'minor_version', None) or 3)
|
||||||
getattr(self, 'minor_version', None)
|
|
||||||
)
|
|
||||||
# tell os-x we want to request a profile
|
# tell os-x we want to request a profile
|
||||||
attrs.append(cocoapy.NSOpenGLPFAOpenGLProfile)
|
attrs.append(cocoapy.NSOpenGLPFAOpenGLProfile)
|
||||||
|
|
||||||
|
@ -5101,3 +5101,5 @@ DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004
|
|||||||
STREAM_SEEK_SET = 0
|
STREAM_SEEK_SET = 0
|
||||||
STREAM_SEEK_CUR = 1
|
STREAM_SEEK_CUR = 1
|
||||||
STREAM_SEEK_END = 2
|
STREAM_SEEK_END = 2
|
||||||
|
|
||||||
|
LOCALE_NAME_MAX_LENGTH = 85
|
||||||
|
@ -41,6 +41,9 @@ supported. Helper methods are included for rotating, scaling, and
|
|||||||
transforming. The :py:class:`~pyglet.matrix.Mat4` includes class methods
|
transforming. The :py:class:`~pyglet.matrix.Mat4` includes class methods
|
||||||
for creating orthographic and perspective projection matrixes.
|
for creating orthographic and perspective projection matrixes.
|
||||||
|
|
||||||
|
Matrices behave just like they do in GLSL: they are specified in column-major
|
||||||
|
order and multiply on the left of vectors, which are treated as columns.
|
||||||
|
|
||||||
:note: For performance, Matrixes subclass the `tuple` type. They
|
:note: For performance, Matrixes subclass the `tuple` type. They
|
||||||
are therefore immutable - all operations return a new object;
|
are therefore immutable - all operations return a new object;
|
||||||
the object is not updated in-place.
|
the object is not updated in-place.
|
||||||
@ -78,9 +81,6 @@ class Vec2:
|
|||||||
yield self.x
|
yield self.x
|
||||||
yield self.y
|
yield self.y
|
||||||
|
|
||||||
def __len__(self) -> int:
|
|
||||||
return 2
|
|
||||||
|
|
||||||
@_typing.overload
|
@_typing.overload
|
||||||
def __getitem__(self, item: int) -> float:
|
def __getitem__(self, item: int) -> float:
|
||||||
...
|
...
|
||||||
@ -92,6 +92,16 @@ class Vec2:
|
|||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return (self.x, self.y)[item]
|
return (self.x, self.y)[item]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if type(key) is slice:
|
||||||
|
for i, attr in enumerate(['x', 'y'][key]):
|
||||||
|
setattr(self, attr, value[i])
|
||||||
|
else:
|
||||||
|
setattr(self, ['x', 'y'][key], value)
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return 2
|
||||||
|
|
||||||
def __add__(self, other: Vec2) -> Vec2:
|
def __add__(self, other: Vec2) -> Vec2:
|
||||||
return Vec2(self.x + other.x, self.y + other.y)
|
return Vec2(self.x + other.x, self.y + other.y)
|
||||||
|
|
||||||
@ -315,6 +325,13 @@ class Vec3:
|
|||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return (self.x, self.y, self.z)[item]
|
return (self.x, self.y, self.z)[item]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if type(key) is slice:
|
||||||
|
for i, attr in enumerate(['x', 'y', 'z'][key]):
|
||||||
|
setattr(self, attr, value[i])
|
||||||
|
else:
|
||||||
|
setattr(self, ['x', 'y', 'z'][key], value)
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return 3
|
return 3
|
||||||
|
|
||||||
@ -520,6 +537,13 @@ class Vec4:
|
|||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return (self.x, self.y, self.z, self.w)[item]
|
return (self.x, self.y, self.z, self.w)[item]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if type(key) is slice:
|
||||||
|
for i, attr in enumerate(['x', 'y', 'z', 'w'][key]):
|
||||||
|
setattr(self, attr, value[i])
|
||||||
|
else:
|
||||||
|
setattr(self, ['x', 'y', 'z', 'w'][key], value)
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
@ -696,30 +720,30 @@ class Mat3(tuple):
|
|||||||
|
|
||||||
def __matmul__(self, other):
|
def __matmul__(self, other):
|
||||||
if isinstance(other, Vec3):
|
if isinstance(other, Vec3):
|
||||||
# Columns:
|
# Rows:
|
||||||
c0 = self[0::3]
|
r0 = self[0::3]
|
||||||
c1 = self[1::3]
|
r1 = self[1::3]
|
||||||
c2 = self[2::3]
|
r2 = self[2::3]
|
||||||
return Vec3(sum(map(_mul, c0, other)),
|
return Vec3(sum(map(_mul, r0, other)),
|
||||||
sum(map(_mul, c1, other)),
|
sum(map(_mul, r1, other)),
|
||||||
sum(map(_mul, c2, other)))
|
sum(map(_mul, r2, other)))
|
||||||
|
|
||||||
if not isinstance(other, Mat3):
|
if not isinstance(other, Mat3):
|
||||||
raise TypeError("Can only multiply with Mat3 or Vec3 types")
|
raise TypeError("Can only multiply with Mat3 or Vec3 types")
|
||||||
|
|
||||||
# Rows:
|
# Rows:
|
||||||
r0 = self[0:3]
|
r0 = self[0::3]
|
||||||
r1 = self[3:6]
|
r1 = self[1::3]
|
||||||
r2 = self[6:9]
|
r2 = self[2::3]
|
||||||
# Columns:
|
# Columns:
|
||||||
c0 = other[0::3]
|
c0 = other[0:3]
|
||||||
c1 = other[1::3]
|
c1 = other[3:6]
|
||||||
c2 = other[2::3]
|
c2 = other[6:9]
|
||||||
|
|
||||||
# Multiply and sum rows * colums:
|
# Multiply and sum rows * columns:
|
||||||
return Mat3((sum(map(_mul, r0, c0)), sum(map(_mul, r0, c1)), sum(map(_mul, r0, c2)),
|
return Mat3((sum(map(_mul, c0, r0)), sum(map(_mul, c0, r1)), sum(map(_mul, c0, r2)),
|
||||||
sum(map(_mul, r1, c0)), sum(map(_mul, r1, c1)), sum(map(_mul, r1, c2)),
|
sum(map(_mul, c1, r0)), sum(map(_mul, c1, r1)), sum(map(_mul, c1, r2)),
|
||||||
sum(map(_mul, r2, c0)), sum(map(_mul, r2, c1)), sum(map(_mul, r2, c2))))
|
sum(map(_mul, c2, r0)), sum(map(_mul, c2, r1)), sum(map(_mul, c2, r2))))
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}"
|
return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}"
|
||||||
@ -765,7 +789,11 @@ class Mat4(tuple):
|
|||||||
z_near: float,
|
z_near: float,
|
||||||
z_far: float
|
z_far: float
|
||||||
) -> Mat4T:
|
) -> Mat4T:
|
||||||
"""Create a Mat4 orthographic projection matrix."""
|
"""Create a Mat4 orthographic projection matrix for use with OpenGL.
|
||||||
|
|
||||||
|
This matrix doesn't actually perform the projection; it transforms the
|
||||||
|
space so that OpenGL's vertex processing performs it.
|
||||||
|
"""
|
||||||
width = right - left
|
width = right - left
|
||||||
height = top - bottom
|
height = top - bottom
|
||||||
depth = z_far - z_near
|
depth = z_far - z_near
|
||||||
@ -792,7 +820,10 @@ class Mat4(tuple):
|
|||||||
fov: float = 60
|
fov: float = 60
|
||||||
) -> Mat4T:
|
) -> Mat4T:
|
||||||
"""
|
"""
|
||||||
Create a Mat4 perspective projection matrix.
|
Create a Mat4 perspective projection matrix for use with OpenGL.
|
||||||
|
|
||||||
|
This matrix doesn't actually perform the projection; it transforms the
|
||||||
|
space so that OpenGL's vertex processing performs it.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
`aspect` : The aspect ratio as a `float`
|
`aspect` : The aspect ratio as a `float`
|
||||||
@ -857,17 +888,6 @@ class Mat4(tuple):
|
|||||||
0.0, 0.0, 1.0, 0.0,
|
0.0, 0.0, 1.0, 0.0,
|
||||||
vector[0], vector[1], vector[2], 1.0))
|
vector[0], vector[1], vector[2], 1.0))
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
|
|
||||||
vec_z = direction.normalize()
|
|
||||||
vec_x = direction.cross(up).normalize()
|
|
||||||
vec_y = direction.cross(vec_z).normalize()
|
|
||||||
|
|
||||||
return cls((vec_x.x, vec_y.x, vec_z.x, 0.0,
|
|
||||||
vec_x.y, vec_y.y, vec_z.y, 0.0,
|
|
||||||
vec_x.z, vec_z.z, vec_z.z, 0.0,
|
|
||||||
0.0, 0.0, 0.0, 1.0))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def look_at(cls: type[Mat4T], position: Vec3, target: Vec3, up: Vec3):
|
def look_at(cls: type[Mat4T], position: Vec3, target: Vec3, up: Vec3):
|
||||||
f = (target - position).normalize()
|
f = (target - position).normalize()
|
||||||
@ -882,11 +902,11 @@ class Mat4(tuple):
|
|||||||
|
|
||||||
def row(self, index: int) -> tuple:
|
def row(self, index: int) -> tuple:
|
||||||
"""Get a specific row as a tuple."""
|
"""Get a specific row as a tuple."""
|
||||||
return self[index * 4: index * 4 + 4]
|
return self[index::4]
|
||||||
|
|
||||||
def column(self, index: int) -> tuple:
|
def column(self, index: int) -> tuple:
|
||||||
"""Get a specific column as a tuple."""
|
"""Get a specific column as a tuple."""
|
||||||
return self[index::4]
|
return self[index * 4: index * 4 + 4]
|
||||||
|
|
||||||
def rotate(self, angle: float, vector: Vec3) -> Mat4:
|
def rotate(self, angle: float, vector: Vec3) -> Mat4:
|
||||||
"""Get a rotation Matrix on x, y, or z axis."""
|
"""Get a rotation Matrix on x, y, or z axis."""
|
||||||
@ -1012,34 +1032,34 @@ class Mat4(tuple):
|
|||||||
|
|
||||||
def __matmul__(self, other):
|
def __matmul__(self, other):
|
||||||
if isinstance(other, Vec4):
|
if isinstance(other, Vec4):
|
||||||
# Columns:
|
# Rows:
|
||||||
c0 = self[0::4]
|
r0 = self[0::4]
|
||||||
c1 = self[1::4]
|
r1 = self[1::4]
|
||||||
c2 = self[2::4]
|
r2 = self[2::4]
|
||||||
c3 = self[3::4]
|
r3 = self[3::4]
|
||||||
return Vec4(sum(map(_mul, c0, other)),
|
return Vec4(sum(map(_mul, r0, other)),
|
||||||
sum(map(_mul, c1, other)),
|
sum(map(_mul, r1, other)),
|
||||||
sum(map(_mul, c2, other)),
|
sum(map(_mul, r2, other)),
|
||||||
sum(map(_mul, c3, other)))
|
sum(map(_mul, r3, other)))
|
||||||
|
|
||||||
if not isinstance(other, Mat4):
|
if not isinstance(other, Mat4):
|
||||||
raise TypeError("Can only multiply with Mat4 or Vec4 types")
|
raise TypeError("Can only multiply with Mat4 or Vec4 types")
|
||||||
# Rows:
|
# Rows:
|
||||||
r0 = self[0:4]
|
r0 = self[0::4]
|
||||||
r1 = self[4:8]
|
r1 = self[1::4]
|
||||||
r2 = self[8:12]
|
r2 = self[2::4]
|
||||||
r3 = self[12:16]
|
r3 = self[3::4]
|
||||||
# Columns:
|
# Columns:
|
||||||
c0 = other[0::4]
|
c0 = other[0:4]
|
||||||
c1 = other[1::4]
|
c1 = other[4:8]
|
||||||
c2 = other[2::4]
|
c2 = other[8:12]
|
||||||
c3 = other[3::4]
|
c3 = other[12:16]
|
||||||
|
|
||||||
# Multiply and sum rows * columns:
|
# Multiply and sum rows * columns:
|
||||||
return Mat4((sum(map(_mul, r0, c0)), sum(map(_mul, r0, c1)), sum(map(_mul, r0, c2)), sum(map(_mul, r0, c3)),
|
return Mat4((sum(map(_mul, c0, r0)), sum(map(_mul, c0, r1)), sum(map(_mul, c0, r2)), sum(map(_mul, c0, r3)),
|
||||||
sum(map(_mul, r1, c0)), sum(map(_mul, r1, c1)), sum(map(_mul, r1, c2)), sum(map(_mul, r1, c3)),
|
sum(map(_mul, c1, r0)), sum(map(_mul, c1, r1)), sum(map(_mul, c1, r2)), sum(map(_mul, c1, r3)),
|
||||||
sum(map(_mul, r2, c0)), sum(map(_mul, r2, c1)), sum(map(_mul, r2, c2)), sum(map(_mul, r2, c3)),
|
sum(map(_mul, c2, r0)), sum(map(_mul, c2, r1)), sum(map(_mul, c2, r2)), sum(map(_mul, c2, r3)),
|
||||||
sum(map(_mul, r3, c0)), sum(map(_mul, r3, c1)), sum(map(_mul, r3, c2)), sum(map(_mul, r3, c3))))
|
sum(map(_mul, c3, r0)), sum(map(_mul, c3, r1)), sum(map(_mul, c3, r2)), sum(map(_mul, c3, r3))))
|
||||||
|
|
||||||
# def __getitem__(self, item):
|
# def __getitem__(self, item):
|
||||||
# row = [slice(0, 4), slice(4, 8), slice(8, 12), slice(12, 16)][item]
|
# row = [slice(0, 4), slice(4, 8), slice(8, 12), slice(12, 16)][item]
|
||||||
|
@ -215,6 +215,7 @@ class ShapeBase(ABC):
|
|||||||
_group = None
|
_group = None
|
||||||
_num_verts = 0
|
_num_verts = 0
|
||||||
_vertex_list = None
|
_vertex_list = None
|
||||||
|
_draw_mode = GL_TRIANGLES
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self._vertex_list is not None:
|
if self._vertex_list is not None:
|
||||||
@ -233,6 +234,21 @@ class ShapeBase(ABC):
|
|||||||
def _update_translation(self):
|
def _update_translation(self):
|
||||||
self._vertex_list.translation[:] = (self._x, self._y) * self._num_verts
|
self._vertex_list.translation[:] = (self._x, self._y) * self._num_verts
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
"""Build internal vertex list.
|
||||||
|
|
||||||
|
This method must create a vertex list and assign it to
|
||||||
|
`self._vertex_list`. It is advisable to use it
|
||||||
|
during `__init__` and to then update the vertices accordingly
|
||||||
|
with `self._update_vertices`.
|
||||||
|
|
||||||
|
While it is not mandatory to implement it, some properties (
|
||||||
|
namely `batch` and `group`) rely on this method to properly
|
||||||
|
recreate the vertex list.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('_create_vertex_list must be defined in '
|
||||||
|
'order to use group or batch properties')
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
"""
|
"""
|
||||||
@ -253,7 +269,7 @@ class ShapeBase(ABC):
|
|||||||
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
||||||
"""
|
"""
|
||||||
self._group.set_state_recursive()
|
self._group.set_state_recursive()
|
||||||
self._vertex_list.draw(GL_TRIANGLES)
|
self._vertex_list.draw(self._draw_mode)
|
||||||
self._group.unset_state_recursive()
|
self._group.unset_state_recursive()
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
@ -404,8 +420,45 @@ class ShapeBase(ABC):
|
|||||||
self._visible = value
|
self._visible = value
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group(self):
|
||||||
|
"""User assigned :class:`Group` object."""
|
||||||
|
return self._group.parent
|
||||||
|
|
||||||
|
@group.setter
|
||||||
|
def group(self, group):
|
||||||
|
if self._group.parent == group:
|
||||||
|
return
|
||||||
|
self._group = _ShapeGroup(self._group.blend_src,
|
||||||
|
self._group.blend_dest,
|
||||||
|
self._group.program,
|
||||||
|
group)
|
||||||
|
self._batch.migrate(self._vertex_list, self._draw_mode, self._group,
|
||||||
|
self._batch)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def batch(self):
|
||||||
|
"""User assigned :class:`Batch` object."""
|
||||||
|
return self._batch
|
||||||
|
|
||||||
|
@batch.setter
|
||||||
|
def batch(self, batch):
|
||||||
|
if self._batch == batch:
|
||||||
|
return
|
||||||
|
|
||||||
|
if batch is not None and self._batch is not None:
|
||||||
|
self._batch.migrate(self._vertex_list, self._draw_mode,
|
||||||
|
self._group, batch)
|
||||||
|
self._batch = batch
|
||||||
|
else:
|
||||||
|
self._vertex_list.delete()
|
||||||
|
self._batch = batch
|
||||||
|
self._create_vertex_list()
|
||||||
|
|
||||||
|
|
||||||
class Arc(ShapeBase):
|
class Arc(ShapeBase):
|
||||||
|
_draw_mode = GL_LINES
|
||||||
|
|
||||||
def __init__(self, x, y, radius, segments=None, angle=math.tau, start_angle=0,
|
def __init__(self, x, y, radius, segments=None, angle=math.tau, start_angle=0,
|
||||||
closed=False, color=(255, 255, 255, 255), batch=None, group=None):
|
closed=False, color=(255, 255, 255, 255), batch=None, group=None):
|
||||||
"""Create an Arc.
|
"""Create an Arc.
|
||||||
@ -460,11 +513,15 @@ class Arc(ShapeBase):
|
|||||||
program = get_default_shader()
|
program = get_default_shader()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._num_verts, GL_LINES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._num_verts, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
vertices = (0,) * (self._segments + 1) * 4
|
vertices = (0,) * (self._segments + 1) * 4
|
||||||
@ -539,7 +596,7 @@ class Arc(ShapeBase):
|
|||||||
Using this method is not recommended. Instead, add the
|
Using this method is not recommended. Instead, add the
|
||||||
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
||||||
"""
|
"""
|
||||||
self._vertex_list.draw(GL_LINES)
|
self._vertex_list.draw(self._draw_mode)
|
||||||
|
|
||||||
|
|
||||||
class Circle(ShapeBase):
|
class Circle(ShapeBase):
|
||||||
@ -582,11 +639,15 @@ class Circle(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._segments*3, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._segments*3, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
vertices = (0,) * self._segments * 6
|
vertices = (0,) * self._segments * 6
|
||||||
@ -623,6 +684,8 @@ class Circle(ShapeBase):
|
|||||||
|
|
||||||
|
|
||||||
class Ellipse(ShapeBase):
|
class Ellipse(ShapeBase):
|
||||||
|
_draw_mode = GL_LINES
|
||||||
|
|
||||||
def __init__(self, x, y, a, b, color=(255, 255, 255, 255),
|
def __init__(self, x, y, a, b, color=(255, 255, 255, 255),
|
||||||
batch=None, group=None):
|
batch=None, group=None):
|
||||||
"""Create an ellipse.
|
"""Create an ellipse.
|
||||||
@ -665,11 +728,15 @@ class Ellipse(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._num_verts, GL_LINES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._num_verts, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
vertices = (0,) * self._num_verts * 4
|
vertices = (0,) * self._num_verts * 4
|
||||||
@ -738,7 +805,7 @@ class Ellipse(ShapeBase):
|
|||||||
Using this method is not recommended. Instead, add the
|
Using this method is not recommended. Instead, add the
|
||||||
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
shape to a `pyglet.graphics.Batch` for efficient rendering.
|
||||||
"""
|
"""
|
||||||
self._vertex_list.draw(GL_LINES)
|
self._vertex_list.draw(self._draw_mode)
|
||||||
|
|
||||||
|
|
||||||
class Sector(ShapeBase):
|
class Sector(ShapeBase):
|
||||||
@ -791,11 +858,15 @@ class Sector(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._num_verts, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._num_verts, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
vertices = (0,) * self._segments * 6
|
vertices = (0,) * self._segments * 6
|
||||||
@ -918,11 +989,15 @@ class Line(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(6, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
6, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
@ -1013,11 +1088,15 @@ class Rectangle(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(6, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
6, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
@ -1143,12 +1222,16 @@ class BorderedRectangle(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
indices = [0, 1, 2, 0, 2, 3, 0, 4, 3, 4, 7, 3, 0, 1, 5, 0, 5, 4, 1, 2, 5, 5, 2, 6, 6, 2, 3, 6, 3, 7]
|
self._create_vertex_list()
|
||||||
self._vertex_list = program.vertex_list_indexed(8, GL_TRIANGLES, indices, self._batch, self._group,
|
|
||||||
colors=('Bn', self._rgba * 4 + self._border_rgba * 4),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
indices = [0, 1, 2, 0, 2, 3, 0, 4, 3, 4, 7, 3, 0, 1, 5, 0, 5, 4, 1, 2, 5, 5, 2, 6, 6, 2, 3, 6, 3, 7]
|
||||||
|
self._vertex_list = self._group.program.vertex_list_indexed(
|
||||||
|
8, self._draw_mode, indices, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * 4 + self._border_rgba * 4),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_color(self):
|
def _update_color(self):
|
||||||
self._vertex_list.colors[:] = self._rgba * 4 + self._border_rgba * 4
|
self._vertex_list.colors[:] = self._rgba * 4 + self._border_rgba * 4
|
||||||
|
|
||||||
@ -1318,11 +1401,15 @@ class Triangle(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(3, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
3, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0)
|
self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0)
|
||||||
@ -1434,12 +1521,16 @@ class Star(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._num_verts, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
rotation=('f', (rotation,) * self._num_verts),
|
|
||||||
translation=('f', (x, y) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._num_verts, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
rotation=('f', (self._rotation,) * self._num_verts),
|
||||||
|
translation=('f', (self._x, self._y) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
vertices = (0, 0) * self._num_spikes * 6
|
vertices = (0, 0) * self._num_spikes * 6
|
||||||
@ -1541,12 +1632,16 @@ class Polygon(ShapeBase):
|
|||||||
self._batch = batch or Batch()
|
self._batch = batch or Batch()
|
||||||
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, program, group)
|
||||||
|
|
||||||
self._vertex_list = program.vertex_list(self._num_verts, GL_TRIANGLES, self._batch, self._group,
|
self._create_vertex_list()
|
||||||
colors=('Bn', self._rgba * self._num_verts),
|
|
||||||
translation=('f', (coordinates[0]) * self._num_verts))
|
|
||||||
self._update_vertices()
|
self._update_vertices()
|
||||||
self._update_color()
|
self._update_color()
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self._group.program.vertex_list(
|
||||||
|
self._num_verts, self._draw_mode, self._batch, self._group,
|
||||||
|
colors=('Bn', self._rgba * self._num_verts),
|
||||||
|
translation=('f', (self._coordinates[0]) * self._num_verts))
|
||||||
|
|
||||||
def _update_vertices(self):
|
def _update_vertices(self):
|
||||||
if not self._visible:
|
if not self._visible:
|
||||||
self._vertex_list.vertices[:] = tuple([0] * ((len(self._coordinates) - 2) * 6))
|
self._vertex_list.vertices[:] = tuple([0] * ((len(self._coordinates) - 2) * 6))
|
||||||
|
@ -217,7 +217,7 @@ class InlineElement:
|
|||||||
"""
|
"""
|
||||||
return self._position
|
return self._position
|
||||||
|
|
||||||
def place(self, layout, x, y):
|
def place(self, layout, x, y, z):
|
||||||
"""Construct an instance of the element at the given coordinates.
|
"""Construct an instance of the element at the given coordinates.
|
||||||
|
|
||||||
Called when the element's position within a layout changes, either
|
Called when the element's position within a layout changes, either
|
||||||
|
@ -87,8 +87,8 @@ class ImageElement(pyglet.text.document.InlineElement):
|
|||||||
descent = min(0, -anchor_y)
|
descent = min(0, -anchor_y)
|
||||||
super().__init__(ascent, descent, self.width)
|
super().__init__(ascent, descent, self.width)
|
||||||
|
|
||||||
def place(self, layout, x, y):
|
def place(self, layout, x, y, z):
|
||||||
program = pyglet.text.layout.get_default_layout_shader()
|
program = pyglet.text.layout.get_default_image_layout_shader()
|
||||||
group = _InlineElementGroup(self.image.get_texture(), program, 0, layout.group)
|
group = _InlineElementGroup(self.image.get_texture(), program, 0, layout.group)
|
||||||
x1 = x
|
x1 = x
|
||||||
y1 = y + self.descent
|
y1 = y + self.descent
|
||||||
@ -96,7 +96,7 @@ class ImageElement(pyglet.text.document.InlineElement):
|
|||||||
y2 = y + self.height + self.descent
|
y2 = y + self.height + self.descent
|
||||||
vertex_list = program.vertex_list_indexed(4, pyglet.gl.GL_TRIANGLES, [0, 1, 2, 0, 2, 3],
|
vertex_list = program.vertex_list_indexed(4, pyglet.gl.GL_TRIANGLES, [0, 1, 2, 0, 2, 3],
|
||||||
layout.batch, group,
|
layout.batch, group,
|
||||||
position=('f', (x1, y1, x2, y1, x2, y2, x1, y2)),
|
position=('f', (x1, y1, z, x2, y1, z, x2, y2, z, x1, y2, z)),
|
||||||
tex_coords=('f', self.image.tex_coords))
|
tex_coords=('f', self.image.tex_coords))
|
||||||
self.vertex_lists[layout] = vertex_list
|
self.vertex_lists[layout] = vertex_list
|
||||||
|
|
||||||
|
@ -602,6 +602,31 @@ layout_fragment_source = """#version 330 core
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
layout_fragment_image_source = """#version 330 core
|
||||||
|
in vec4 text_colors;
|
||||||
|
in vec2 texture_coords;
|
||||||
|
in vec4 vert_position;
|
||||||
|
|
||||||
|
uniform sampler2D image_texture;
|
||||||
|
|
||||||
|
out vec4 final_colors;
|
||||||
|
|
||||||
|
uniform sampler2D text;
|
||||||
|
uniform bool scissor;
|
||||||
|
uniform vec4 scissor_area;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
final_colors = texture(image_texture, texture_coords.xy);
|
||||||
|
if (scissor == true) {
|
||||||
|
if (vert_position.x < scissor_area[0]) discard; // left
|
||||||
|
if (vert_position.y < scissor_area[1]) discard; // bottom
|
||||||
|
if (vert_position.x > scissor_area[0] + scissor_area[2]) discard; // right
|
||||||
|
if (vert_position.y > scissor_area[1] + scissor_area[3]) discard; // top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
decoration_vertex_source = """#version 330 core
|
decoration_vertex_source = """#version 330 core
|
||||||
in vec3 position;
|
in vec3 position;
|
||||||
in vec4 colors;
|
in vec4 colors;
|
||||||
@ -673,6 +698,17 @@ def get_default_layout_shader():
|
|||||||
return pyglet.gl.current_context.pyglet_text_layout_shader
|
return pyglet.gl.current_context.pyglet_text_layout_shader
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_image_layout_shader():
|
||||||
|
try:
|
||||||
|
return pyglet.gl.current_context.pyglet_text_layout_image_shader
|
||||||
|
except AttributeError:
|
||||||
|
pyglet.gl.current_context.pyglet_text_layout_image_shader = shader.ShaderProgram(
|
||||||
|
shader.Shader(layout_vertex_source, 'vertex'),
|
||||||
|
shader.Shader(layout_fragment_image_source, 'fragment'),
|
||||||
|
)
|
||||||
|
return pyglet.gl.current_context.pyglet_text_layout_image_shader
|
||||||
|
|
||||||
|
|
||||||
def get_default_decoration_shader():
|
def get_default_decoration_shader():
|
||||||
try:
|
try:
|
||||||
return pyglet.gl.current_context.pyglet_text_decoration_shader
|
return pyglet.gl.current_context.pyglet_text_decoration_shader
|
||||||
|
Loading…
Reference in New Issue
Block a user