pyglet 2.1.12__py3-none-any.whl → 3.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +4 -17
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +27 -5
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +147 -177
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.12.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.12.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
@@ -141,6 +141,15 @@ def add_default_codecs():
141
141
  except ImportError:
142
142
  pass
143
143
 
144
+ # Compressed texture in KTX2 format
145
+ try:
146
+ from pyglet.image.codecs import ktx2
147
+ registry.add_encoders(ktx2)
148
+ registry.add_decoders(ktx2)
149
+ except ImportError:
150
+ pass
151
+
152
+
144
153
  # Mac OS X default: Quartz
145
154
  if compat_platform == 'darwin':
146
155
  try:
@@ -9,6 +9,7 @@ encoding. Alpha channel is supported for 32-bit BI_RGB only.
9
9
  #
10
10
  # But some details including alignment and bit/byte order are omitted; see
11
11
  # http://www.fileformat.info/format/bmp/egff.htm
12
+ from __future__ import annotations
12
13
 
13
14
  import ctypes
14
15
  from ctypes.wintypes import DWORD, LONG, WORD
@@ -31,7 +32,7 @@ class BITMAPFILEHEADER(ctypes.LittleEndianStructure):
31
32
  ('bfSize', DWORD),
32
33
  ('bfReserved1', WORD),
33
34
  ('bfReserved2', WORD),
34
- ('bfOffBits', DWORD)
35
+ ('bfOffBits', DWORD),
35
36
  ]
36
37
 
37
38
  class BITMAPINFOHEADER(ctypes.LittleEndianStructure):
@@ -47,7 +48,7 @@ class BITMAPINFOHEADER(ctypes.LittleEndianStructure):
47
48
  ('biXPelsPerMeter', LONG),
48
49
  ('biYPelsPerMeter', LONG),
49
50
  ('biClrUsed', DWORD),
50
- ('biClrImportant', DWORD)
51
+ ('biClrImportant', DWORD),
51
52
  ]
52
53
 
53
54
  CIEXYZTRIPLE = FXPT2DOT30 * 9
@@ -92,7 +93,7 @@ class RGBQUAD(ctypes.LittleEndianStructure):
92
93
  ('rgbBlue', BYTE),
93
94
  ('rgbGreen', BYTE),
94
95
  ('rgbRed', BYTE),
95
- ('rgbReserved', BYTE)
96
+ ('rgbReserved', BYTE),
96
97
  ]
97
98
 
98
99
  def __repr__(self):
@@ -137,7 +138,7 @@ class BMPImageDecoder(ImageDecoder):
137
138
  if width <= 0 or info_header.biPlanes != 1:
138
139
  raise ImageDecodeException(
139
140
  'BMP file has corrupt parameters: %r' % (filename or file))
140
- pitch_sign = height < 0 and -1 or 1
141
+ pitch_sign = (height < 0 and -1) or 1
141
142
  height = abs(height)
142
143
 
143
144
  compression = info_header.biCompression
@@ -146,7 +147,7 @@ class BMPImageDecoder(ImageDecoder):
146
147
  'Unsupported compression: %r' % (filename or file))
147
148
 
148
149
  clr_used = 0
149
- bitcount = info_header.biBitCount
150
+ bitcount = info_header.biBitCount
150
151
  if bitcount == 1:
151
152
  pitch = (width + 7) // 8
152
153
  bits_type = ctypes.c_ubyte
@@ -166,7 +167,7 @@ class BMPImageDecoder(ImageDecoder):
166
167
  elif bitcount == 24:
167
168
  pitch = width * 3
168
169
  bits_type = ctypes.c_ubyte
169
- decoder = decode_24bit
170
+ decoder = decode_24bit
170
171
  elif bitcount == 32:
171
172
  pitch = width * 4
172
173
  if compression == BI_RGB:
@@ -185,17 +186,17 @@ class BMPImageDecoder(ImageDecoder):
185
186
  pitch = (pitch + 3) & ~3
186
187
  packed_width = pitch // ctypes.sizeof(bits_type)
187
188
 
188
- if bitcount < 16 and compression == BI_RGB:
189
+ if bitcount < 16 and compression == BI_RGB:
189
190
  clr_used = info_header.biClrUsed or (1 << bitcount)
190
191
  palette = to_ctypes(buffer, palette_offset, RGBQUAD * clr_used)
191
- bits = to_ctypes(buffer, bits_offset,
192
+ bits = to_ctypes(buffer, bits_offset,
192
193
  bits_type * packed_width * height)
193
194
  return decoder(bits, palette, width, height, pitch, pitch_sign)
194
- elif bitcount >= 16 and compression == BI_RGB:
195
- bits = to_ctypes(buffer, bits_offset,
195
+ if bitcount >= 16 and compression == BI_RGB:
196
+ bits = to_ctypes(buffer, bits_offset,
196
197
  bits_type * (packed_width * height))
197
198
  return decoder(bits, None, width, height, pitch, pitch_sign)
198
- elif compression == BI_BITFIELDS:
199
+ if compression == BI_BITFIELDS:
199
200
  if info_header.biSize >= ctypes.sizeof(BITMAPV4HEADER):
200
201
  info_header = to_ctypes(buffer, info_header_offset,
201
202
  BITMAPV4HEADER)
@@ -204,7 +205,7 @@ class BMPImageDecoder(ImageDecoder):
204
205
  b_mask = info_header.bV4BlueMask
205
206
  else:
206
207
  fields_offset = info_header_offset + \
207
- ctypes.sizeof(BITMAPINFOHEADER)
208
+ ctypes.sizeof(BITMAPINFOHEADER)
208
209
  fields = to_ctypes(buffer, fields_offset, RGBFields)
209
210
  r_mask = fields.red
210
211
  g_mask = fields.green
@@ -215,46 +216,60 @@ class BMPImageDecoder(ImageDecoder):
215
216
  ('data', bits_type * packed_width * height),
216
217
  ]
217
218
  bits = to_ctypes(buffer, bits_offset, _BitsArray).data
218
- return decoder(bits, r_mask, g_mask, b_mask,
219
- width, height, pitch, pitch_sign)
219
+ return decoder(bits, r_mask, g_mask, b_mask, width, height, pitch, pitch_sign)
220
+ return None
221
+
220
222
 
221
223
  def decode_1bit(bits, palette, width, height, pitch, pitch_sign):
222
- rgb_pitch = (((pitch << 3) + 7) & ~0x7) * 3
224
+ rgb_pitch = width * 3
223
225
  buffer = (ctypes.c_ubyte * (height * rgb_pitch))()
224
226
  i = 0
225
227
  for row in bits:
228
+ pixel_count = 0
226
229
  for packed in row:
227
230
  for _ in range(8):
231
+ if pixel_count >= width:
232
+ break
228
233
  rgb = palette[(packed & 0x80) >> 7]
229
234
  buffer[i] = rgb.rgbRed
230
235
  buffer[i + 1] = rgb.rgbGreen
231
236
  buffer[i + 2] = rgb.rgbBlue
232
237
  i += 3
238
+ pixel_count += 1
233
239
  packed <<= 1
234
240
 
235
241
  return ImageData(width, height, 'RGB', buffer, pitch_sign * rgb_pitch)
236
242
 
237
243
  def decode_4bit(bits, palette, width, height, pitch, pitch_sign):
238
- rgb_pitch = (((pitch << 1) + 1) & ~0x1) * 3
244
+ packed_row_bytes = (width + 1) // 2
245
+ rgb_pitch = width * 3
239
246
  buffer = (ctypes.c_ubyte * (height * rgb_pitch))()
240
- i = 0
247
+ i = 0
241
248
  for row in bits:
242
- for packed in row:
243
- for index in ((packed & 0xf0) >> 4, packed & 0xf):
249
+ pixel_count = 0
250
+ for byte_i in range(packed_row_bytes):
251
+ packed = row[byte_i]
252
+ for index in ((packed & 0xF0) >> 4, packed & 0x0F):
253
+ if pixel_count >= width:
254
+ break
244
255
  rgb = palette[index]
245
256
  buffer[i] = rgb.rgbRed
246
257
  buffer[i + 1] = rgb.rgbGreen
247
258
  buffer[i + 2] = rgb.rgbBlue
248
259
  i += 3
260
+ pixel_count += 1
249
261
 
250
262
  return ImageData(width, height, 'RGB', buffer, pitch_sign * rgb_pitch)
251
263
 
264
+
252
265
  def decode_8bit(bits, palette, width, height, pitch, pitch_sign):
253
- rgb_pitch = pitch * 3
266
+ rgb_pitch = width * 3
254
267
  buffer = (ctypes.c_ubyte * (height * rgb_pitch))()
268
+
255
269
  i = 0
256
270
  for row in bits:
257
- for index in row:
271
+ for x in range(width):
272
+ index = row[x]
258
273
  rgb = palette[index]
259
274
  buffer[i] = rgb.rgbRed
260
275
  buffer[i + 1] = rgb.rgbGreen
@@ -265,9 +280,18 @@ def decode_8bit(bits, palette, width, height, pitch, pitch_sign):
265
280
 
266
281
 
267
282
  def decode_24bit(bits, palette, width, height, pitch, pitch_sign):
268
- buffer = (ctypes.c_ubyte * (height * pitch))()
269
- ctypes.memmove(buffer, bits, len(buffer))
270
- return ImageData(width, height, 'BGR', buffer, pitch_sign * pitch)
283
+ row_bytes = width * 3
284
+ buffer = (ctypes.c_ubyte * (height * row_bytes))()
285
+
286
+ src_base = ctypes.addressof(bits)
287
+ dst_base = ctypes.addressof(buffer)
288
+
289
+ for y in range(height):
290
+ src_row = src_base + y * pitch
291
+ dst_row = dst_base + y * row_bytes
292
+ ctypes.memmove(dst_row, src_row, row_bytes)
293
+
294
+ return ImageData(width, height, 'BGR', buffer, pitch_sign * row_bytes)
271
295
 
272
296
  def decode_32bit_rgb(bits, palette, width, height, pitch, pitch_sign):
273
297
  buffer = (ctypes.c_ubyte * (height * pitch))()
@@ -291,21 +315,20 @@ def get_shift(mask):
291
315
  s = shift - (8 - shift_up)
292
316
  if s < 0:
293
317
  return 0, -s
294
- else:
295
- return s, 0
318
+ return s, 0
296
319
 
297
- def decode_bitfields(bits, r_mask, g_mask, b_mask,
298
- width, height, pitch, pitch_sign):
320
+ def decode_bitfields(bits, r_mask, g_mask, b_mask, width, height, pitch, pitch_sign):
299
321
  r_shift1, r_shift2 = get_shift(r_mask)
300
322
  g_shift1, g_shift2 = get_shift(g_mask)
301
323
  b_shift1, b_shift2 = get_shift(b_mask)
302
324
 
303
- rgb_pitch = 3 * len(bits[0])
325
+ rgb_pitch = width * 3
304
326
  buffer = (ctypes.c_ubyte * (height * rgb_pitch))()
305
327
 
306
328
  i = 0
307
329
  for row in bits:
308
- for packed in row:
330
+ for x in range(width):
331
+ packed = row[x]
309
332
  buffer[i] = (packed & r_mask) >> r_shift1 << r_shift2
310
333
  buffer[i+1] = (packed & g_mask) >> g_shift1 << g_shift2
311
334
  buffer[i+2] = (packed & b_mask) >> b_shift1 << b_shift2
@@ -2,15 +2,15 @@
2
2
 
3
3
  Reference: http://msdn2.microsoft.com/en-us/library/bb172993.aspx
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
7
  import struct
7
8
  import itertools
9
+ from typing import BinaryIO
8
10
 
9
- from pyglet.gl import *
10
- from pyglet.image import CompressedImageData
11
+ from pyglet.image.base import CompressedImageData, CompressionFormat
11
12
  from pyglet.image import codecs
12
- from pyglet.image.codecs import s3tc, ImageDecodeException
13
-
13
+ from pyglet.image.codecs import ImageDecodeException
14
14
 
15
15
  # dwFlags of DDSURFACEDESC2
16
16
  DDSD_CAPS = 0x00000001
@@ -53,10 +53,10 @@ class _FileStruct:
53
53
  for field, value in itertools.zip_longest(self._fields, items, fillvalue=None):
54
54
  setattr(self, field[0], value)
55
55
 
56
- def __repr__(self):
56
+ def __repr__(self) -> str:
57
57
  name = self.__class__.__name__
58
- return '%s(%s)' % (name, (', \n%s' % (' ' * (len(name) + 1))).join(
59
- ['%s = %s' % (field[0], repr(getattr(self, field[0]))) for field in self._fields]))
58
+ return '{}({})'.format(name, (', \n%s' % (' ' * (len(name) + 1))).join(
59
+ [f'{field[0]} = {getattr(self, field[0])!r}' for field in self._fields]))
60
60
 
61
61
  @classmethod
62
62
  def get_format(cls):
@@ -82,13 +82,21 @@ class DDSURFACEDESC2(_FileStruct):
82
82
  ('dwCaps1', 'I'),
83
83
  ('dwCaps2', 'I'),
84
84
  ('dwCapsReserved', '8s'),
85
- ('dwReserved2', 'I')
85
+ ('dwReserved2', 'I'),
86
86
  ]
87
87
 
88
88
  def __init__(self, data):
89
89
  super().__init__(data)
90
90
  self.ddpfPixelFormat = DDPIXELFORMAT(self.ddpfPixelFormat)
91
91
 
92
+ class DDS_HEADER_DXT10(_FileStruct):
93
+ _fields = [
94
+ ('dxgiFormat', 'I'),
95
+ ('resourceDimension', 'I'),
96
+ ('miscFlag', 'I'),
97
+ ('arraySize', 'I'),
98
+ ('miscFlags2', 'I'),
99
+ ]
92
100
 
93
101
  class DDPIXELFORMAT(_FileStruct):
94
102
  _fields = [
@@ -99,25 +107,39 @@ class DDPIXELFORMAT(_FileStruct):
99
107
  ('dwRBitMask', 'I'),
100
108
  ('dwGBitMask', 'I'),
101
109
  ('dwBBitMask', 'I'),
102
- ('dwRGBAlphaBitMask', 'I')
110
+ ('dwRGBAlphaBitMask', 'I'),
103
111
  ]
104
112
 
113
+ def _get_dds_block_size_dxgi(dxgi_format: int) -> int:
114
+ if dxgi_format in (71, 72, 80, 81): # BC1 = 8 bytes per 4x4 block
115
+ return 8
116
+ if dxgi_format in (74, 75, # BC2 = 16 bytes per 4x4 block
117
+ 77, 78, # BC3
118
+ 83, 84, # BC5
119
+ 95, 96, # B6
120
+ 98, 99): # BC7
121
+ return 16
122
+ msg = f"Unsupported DXGI format {dxgi_format}"
123
+ raise ImageDecodeException(msg)
105
124
 
106
- _compression_formats = {
107
- (b'DXT1', False): (GL_COMPRESSED_RGB_S3TC_DXT1_EXT, s3tc.decode_dxt1_rgb),
108
- (b'DXT1', True): (GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, s3tc.decode_dxt1_rgba),
109
- (b'DXT3', False): (GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, s3tc.decode_dxt3),
110
- (b'DXT3', True): (GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, s3tc.decode_dxt3),
111
- (b'DXT5', False): (GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, s3tc.decode_dxt5),
112
- (b'DXT5', True): (GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, s3tc.decode_dxt5),
113
- }
125
+
126
+ def _get_dds_block_size(fourcc: bytes) -> int:
127
+ """Return block size in bytes based on the below formats."""
128
+ if fourcc in (b'DXT1', b'BC1 '):
129
+ return 8
130
+ if fourcc in (b'DXT3', b'DXT5', b'BC2 ', b'BC3 ', b'BC5 ', b'ATI2'):
131
+ return 16
132
+ if fourcc in (b'BC4 ', b'ATI1'):
133
+ return 8
134
+
135
+ return 0 # Not block compressed
114
136
 
115
137
 
116
138
  class DDSImageDecoder(codecs.ImageDecoder):
117
- def get_file_extensions(self):
139
+ def get_file_extensions(self) -> list[str]:
118
140
  return ['.dds']
119
141
 
120
- def decode(self, filename, file):
142
+ def decode(self, filename: str, file: BinaryIO | None = None):
121
143
  if not file:
122
144
  file = open(filename, 'rb')
123
145
 
@@ -147,19 +169,19 @@ class DDSImageDecoder(codecs.ImageDecoder):
147
169
 
148
170
  has_alpha = desc.ddpfPixelFormat.dwRGBAlphaBitMask != 0
149
171
 
150
- selector = (desc.ddpfPixelFormat.dwFourCC, has_alpha)
151
- if selector not in _compression_formats:
152
- raise ImageDecodeException('Unsupported texture compression %s' % desc.ddpfPixelFormat.dwFourCC)
153
-
154
- dformat, decoder = _compression_formats[selector]
155
- if dformat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
156
- block_size = 8
172
+ fourcc = desc.ddpfPixelFormat.dwFourCC
173
+ if fourcc == b'DX10':
174
+ dx10_header_data = file.read(DDS_HEADER_DXT10.get_size())
175
+ dx10_header = DDS_HEADER_DXT10(dx10_header_data)
176
+ fmt = CompressionFormat(fourcc, has_alpha, dx10_header.dxgiFormat)
177
+ block_size = _get_dds_block_size_dxgi(dx10_header.dxgiFormat)
157
178
  else:
158
- block_size = 16
179
+ block_size = _get_dds_block_size(fourcc)
180
+ fmt = CompressionFormat(fourcc, has_alpha)
159
181
 
160
182
  datas = []
161
183
  w, h = width, height
162
- for i in range(mipmaps):
184
+ for _ in range(mipmaps):
163
185
  if not w and not h:
164
186
  break
165
187
  if not w:
@@ -172,7 +194,7 @@ class DDSImageDecoder(codecs.ImageDecoder):
172
194
  w >>= 1
173
195
  h >>= 1
174
196
 
175
- image = CompressedImageData(width, height, dformat, datas[0], 'GL_EXT_texture_compression_s3tc', decoder)
197
+ image = CompressedImageData(width, height, fmt, datas[0])
176
198
  level = 0
177
199
  for data in datas[1:]:
178
200
  level += 1
@@ -181,9 +203,9 @@ class DDSImageDecoder(codecs.ImageDecoder):
181
203
  return image
182
204
 
183
205
 
184
- def get_decoders():
206
+ def get_decoders() -> list[DDSImageDecoder]:
185
207
  return [DDSImageDecoder()]
186
208
 
187
209
 
188
- def get_encoders():
210
+ def get_encoders() -> list:
189
211
  return []
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  from ctypes import (
4
+ addressof,
2
5
  POINTER,
3
6
  Structure,
4
7
  c_buffer,
@@ -15,10 +18,14 @@ from ctypes import (
15
18
  create_string_buffer,
16
19
  memmove,
17
20
  windll,
21
+ byref,
22
+ c_int,
23
+ c_uint,
24
+ sizeof,
18
25
  )
19
26
  from ctypes.wintypes import BOOL, BYTE, INT, UINT, ULONG
20
27
 
21
- from pyglet.image import Animation, AnimationFrame, ImageData, byref, c_int, c_uint, sizeof
28
+ from pyglet.image import Animation, AnimationFrame, ImageData
22
29
  from pyglet.image.codecs import ImageDecodeException, ImageDecoder
23
30
  from pyglet.libs.win32 import _kernel32 as kernel32
24
31
  from pyglet.libs.win32 import _ole32 as ole32
@@ -58,14 +65,14 @@ class GdiplusStartupInput(Structure):
58
65
  ('GdiplusVersion', c_uint32),
59
66
  ('DebugEventCallback', c_void_p),
60
67
  ('SuppressBackgroundThread', BOOL),
61
- ('SuppressExternalCodecs', BOOL)
68
+ ('SuppressExternalCodecs', BOOL),
62
69
  ]
63
70
 
64
71
 
65
72
  class GdiplusStartupOutput(Structure):
66
73
  _fields = [
67
74
  ('NotificationHookProc', c_void_p),
68
- ('NotificationUnhookProc', c_void_p)
75
+ ('NotificationUnhookProc', c_void_p),
69
76
  ]
70
77
 
71
78
 
@@ -76,7 +83,7 @@ class BitmapData(Structure):
76
83
  ('Stride', c_int),
77
84
  ('PixelFormat', c_int),
78
85
  ('Scan0', POINTER(c_byte)),
79
- ('Reserved', POINTER(c_uint))
86
+ ('Reserved', POINTER(c_uint)),
80
87
  ]
81
88
 
82
89
 
@@ -85,7 +92,7 @@ class Rect(Structure):
85
92
  ('X', c_int),
86
93
  ('Y', c_int),
87
94
  ('Width', c_int),
88
- ('Height', c_int)
95
+ ('Height', c_int),
89
96
  ]
90
97
 
91
98
 
@@ -94,7 +101,7 @@ class PropertyItem(Structure):
94
101
  ('id', c_uint),
95
102
  ('length', c_ulong),
96
103
  ('type', c_short),
97
- ('value', c_void_p)
104
+ ('value', c_void_p),
98
105
  ]
99
106
 
100
107
 
@@ -221,9 +228,7 @@ class GDIPlusDecoder(ImageDecoder):
221
228
  fmt = 'BGRA'
222
229
  if pf == PixelFormat24bppRGB:
223
230
  fmt = 'BGR'
224
- elif pf == PixelFormat32bppRGB:
225
- pass
226
- elif pf == PixelFormat32bppARGB:
231
+ elif pf == PixelFormat32bppRGB or pf == PixelFormat32bppARGB:
227
232
  pass
228
233
  elif pf in (PixelFormat16bppARGB1555, PixelFormat32bppPARGB,
229
234
  PixelFormat64bppARGB, PixelFormat64bppPARGB):
@@ -240,15 +245,34 @@ class GDIPlusDecoder(ImageDecoder):
240
245
  rect.Height = height
241
246
  bitmap_data = BitmapData()
242
247
  gdiplus.GdipBitmapLockBits(bitmap, byref(rect), ImageLockModeRead, pf, byref(bitmap_data))
243
-
248
+
244
249
  # Create buffer for RawImage
245
250
  buffer = create_string_buffer(bitmap_data.Stride * height)
246
- memmove(buffer, bitmap_data.Scan0, len(buffer))
247
-
251
+ if fmt == 'BGR':
252
+ stride = bitmap_data.Stride
253
+ components = len(fmt)
254
+ packed_row = width * components
255
+ src_stride = abs(stride)
256
+
257
+ src_addr = cast(bitmap_data.Scan0, c_void_p).value
258
+ src = (c_byte * (src_stride * height)).from_address(src_addr)
259
+
260
+ buf_address = addressof(buffer)
261
+ src_address = addressof(src)
262
+
263
+ for y in range(height):
264
+ src_y = y if stride < 0 else (height - 1 - y)
265
+ src_off = src_y * src_stride
266
+ dst_off = y * packed_row
267
+ memmove(buf_address + dst_off, src_address + src_off, packed_row)
268
+ else:
269
+ memmove(buffer, bitmap_data.Scan0, len(buffer))
270
+ packed_row = -bitmap_data.Stride
271
+
248
272
  # Unlock data
249
273
  gdiplus.GdipBitmapUnlockBits(bitmap, byref(bitmap_data))
250
274
 
251
- return ImageData(width, height, fmt, buffer, -bitmap_data.Stride)
275
+ return ImageData(width, height, fmt, buffer, packed_row)
252
276
 
253
277
  def _delete_bitmap(self, bitmap):
254
278
  # Release image and stream
@@ -286,7 +310,7 @@ class GDIPlusDecoder(ImageDecoder):
286
310
  gdiplus.GdipGetPropertyItemSize(bitmap, prop_id, byref(prop_size))
287
311
 
288
312
  prop_buffer = c_buffer(prop_size.value)
289
- prop_item = cast(prop_buffer, POINTER(PropertyItem)).contents
313
+ prop_item = cast(prop_buffer, POINTER(PropertyItem)).contents
290
314
  gdiplus.GdipGetPropertyItem(bitmap, prop_id, prop_size.value, prop_buffer)
291
315
 
292
316
  n_delays = prop_item.length // sizeof(c_long)
@@ -1,11 +1,9 @@
1
1
  from ctypes import *
2
2
 
3
- from pyglet.gl import *
4
3
  from pyglet.image import *
5
4
  from pyglet.image.codecs import *
6
5
  from pyglet.image.codecs import gif
7
6
 
8
- import pyglet.lib
9
7
  import pyglet.window
10
8
 
11
9
  gdk = pyglet.lib.load_library('gdk-x11-2.0')
@@ -0,0 +1,99 @@
1
+ import asyncio
2
+ import base64
3
+ import time
4
+
5
+ from pyglet.image import ImageDecoder
6
+ from pyglet.image import ImageData
7
+ from pyglet.image.codecs import ImageDecodeException
8
+
9
+ try:
10
+ import js
11
+ import pyodide.ffi
12
+ except ImportError:
13
+ raise ImportError
14
+
15
+
16
+ _image_canvas = js.document.createElement("canvas")
17
+ _image_context = _image_canvas.getContext("2d", willReadFrequently=True)
18
+
19
+ async def check_image_exists(url):
20
+ """Check if an image exists at the given URL."""
21
+ try:
22
+ response = await js.fetch(url, {"method": "HEAD"}) # HEAD request (faster than GET)
23
+ if response.ok:
24
+ print(f"Image exists: {url}")
25
+ return True
26
+ else:
27
+ print(f"Image does NOT exist: {url} (Status: {response.status})")
28
+ return False
29
+ except Exception as e:
30
+ print(f"Error checking image: {e}")
31
+ return False
32
+
33
+ async def load_image_from_vfs(vfs_path):
34
+ # Read image from Pyodide's VFS
35
+ with open(vfs_path, "rb") as f:
36
+ img_data = f.read()
37
+
38
+ # Convert image to Base64
39
+ base64_data = base64.b64encode(img_data).decode("utf-8")
40
+
41
+ # Create a Data URL
42
+ mime_type = "image/png" # Change this based on file type (e.g., "image/jpeg")
43
+ data_url = f"data:{mime_type};base64,{base64_data}"
44
+
45
+ # Use the Data URL as the image source
46
+ img = js.Image.new()
47
+ img.src = data_url
48
+
49
+ # Wait for image to load before returning
50
+ await pyodide.ffi.to_js(img.decode())
51
+
52
+ return img
53
+
54
+ class JSImageDecoder(ImageDecoder):
55
+ def get_file_extensions(self):
56
+ return ['.png']
57
+
58
+ async def decode(self, filename, file) -> ImageData:
59
+ t = time.perf_counter()
60
+ #img = js.Image.new()
61
+ #img.src = filename
62
+
63
+ #await pyodide.ffi.to_js(img.decode()) # Wait for image to load
64
+ img = await load_image_from_vfs(filename)
65
+
66
+ _image_canvas.width = img.width
67
+ _image_canvas.height = img.height
68
+
69
+ # Clear previous image
70
+ _image_context.clearRect(0, 0, img.width, img.height)
71
+
72
+ # Draw to context
73
+ _image_context.translate(0, img.height) # Move down
74
+ _image_context.scale(1, -1) # Flip vertically
75
+ _image_context.drawImage(img, 0, 0)
76
+
77
+ image_data = _image_context.getImageData(0, 0, img.width, img.height)
78
+ pixel_data = image_data.data # Uint8Array
79
+
80
+ print("Took", time.perf_counter() - t, "seconds")
81
+
82
+ return ImageData(img.width, img.height, 'RGBA', pixel_data)
83
+
84
+ def decode_animation(self, filename, file):
85
+ """Decode the given file object and return an instance of :py:class:`~pyglet.image.Animation`.
86
+ Throws ImageDecodeException if there is an error. filename
87
+ can be a file type hint.
88
+ """
89
+ raise ImageDecodeException('This decoder cannot decode animations.')
90
+
91
+ def __repr__(self) -> str:
92
+ return "{}{}".format(self.__class__.__name__,
93
+ self.get_animation_file_extensions() +
94
+ self.get_file_extensions())
95
+ def get_decoders():
96
+ return [JSImageDecoder()]
97
+
98
+ def get_encoders():
99
+ return []