Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions arcade/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,16 @@ def get_pixel_ratio(self) -> float:
"""
if self._pixel_perfect:
return 1.0
if is_pyodide:
# Pyglet's emscripten window caches devicePixelRatio at init, but the
# actual canvas drawing buffer is sized via getBoundingClientRect()
# which can be sub-pixel less than logical_size * devicePixelRatio.
# Returning fb_size / logical_size matches the canvas exactly, so
# full-canvas viewport round-trips through Camera2D don't leave a
# 1-2 pixel gap on the top/right edges.
log_w = self._width
if log_w:
return self.get_framebuffer_size()[0] / log_w
return super().get_pixel_ratio()

def _on_resize(self, width: int, height: int) -> EVENT_HANDLE_STATE:
Expand Down Expand Up @@ -1038,6 +1048,14 @@ def set_size(self, width: int, height: int) -> None:

def get_size(self) -> tuple[int, int]:
"""Get the size of the window."""
if is_pyodide:
# Pyglet's emscripten window returns the canvas drawing-buffer
# size (physical, DPI-scaled pixels) from get_size(); desktop
# pyglet returns logical pixels. Return logical pixels here so
# viewport math in show_view/_on_resize stays consistent across
# backends and Camera2D doesn't render into a sub-region of the
# canvas on HiDPI displays.
return self._width, self._height
return super().get_size()

def get_location(self) -> tuple[int, int]:
Expand Down
27 changes: 19 additions & 8 deletions arcade/gl/backends/webgl/framebuffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,20 @@ def __init__(self, ctx: WebGLContext):

@DefaultFrameBuffer.viewport.setter
def viewport(self, value: tuple[int, int, int, int]):
# This is very similar to the OpenGL backend setter
# WebGL backend doesn't need to handle pixel scaling for the
# default framebuffer like desktop does, the browser does that
# for us. However we need a separate implementation for the
# function because of ABC
# Pyglet sizes the canvas drawing buffer at physical pixels
# (canvas.width = logical_width * devicePixelRatio), so we apply
# the same pixel-ratio multiply as the OpenGL backend to keep the
# default framebuffer's get/set symmetric in logical pixels.
if not isinstance(value, tuple) or len(value) != 4:
raise ValueError("viewport shouldbe a 4-component tuple")
raise ValueError("viewport should be a 4-component tuple")

self._viewport = value
ratio = self.ctx.window.get_pixel_ratio()
self._viewport = (
int(value[0] * ratio),
int(value[1] * ratio),
int(value[2] * ratio),
int(value[3] * ratio),
)

if self._ctx.active_framebuffer == self:
self._ctx._gl.viewport(*self._viewport)
Expand All @@ -280,6 +285,12 @@ def scissor(self, value):
if self._ctx.active_framebuffer == self:
self._ctx._gl.scissor(*self._viewport)
else:
self._scissor = value
ratio = self.ctx.window.get_pixel_ratio()
self._scissor = (
int(value[0] * ratio),
int(value[1] * ratio),
int(value[2] * ratio),
int(value[3] * ratio),
)
if self._ctx.active_framebuffer == self:
self._ctx._gl.scissor(*self._scissor)
Loading