diff --git a/arcade/application.py b/arcade/application.py index 7f7c46e63..e72554437 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -144,6 +144,11 @@ class Window(pyglet.window.Window): file_drops: Should the window listen for file drops? If True, the window will dispatch ``on_file_drop`` events when files are dropped onto the window. + pixel_perfect: + If True, ignore OS DPI scaling and use a 1:1 pixel ratio. + The window and framebuffer will be created at exactly the + requested size. The window may appear smaller on HiDPI + displays, but rendering will be pixel-perfect. **kwargs: Further keyword arguments are passed to the pyglet window constructor. This can be used to set advanced options that aren't explicitly handled by Arcade. @@ -175,6 +180,7 @@ def __init__( fixed_rate: float = 1.0 / 60.0, fixed_frame_cap: int | None = None, file_drops: bool = False, + pixel_perfect: bool = False, **kwargs, ) -> None: # In certain environments we can't have antialiasing/MSAA enabled. @@ -182,6 +188,9 @@ def __init__( if os.environ.get("REPL_ID"): antialiasing = False + if pixel_perfect: + pyglet.options.dpi_scaling = "platform" + desired_gl_provider = "opengl" if is_pyodide(): gl_api = "webgl" @@ -199,6 +208,8 @@ def __init__( """Indicates if the window was closed""" self.headless: bool = arcade.headless """If True, the window is running in headless mode.""" + self._pixel_perfect: bool = pixel_perfect + """If True, ignore OS DPI scaling and use a 1:1 pixel ratio.""" config = None # Attempt to make window with antialiasing @@ -891,6 +902,16 @@ def on_draw(self) -> EVENT_HANDLE_STATE: return EVENT_UNHANDLED + def get_pixel_ratio(self) -> float: + """Return the framebuffer/window size ratio. + + When ``pixel_perfect=True``, this always returns ``1.0`` so that + arcade treats the framebuffer as unscaled. + """ + if self._pixel_perfect: + return 1.0 + return super().get_pixel_ratio() + def _on_resize(self, width: int, height: int) -> EVENT_HANDLE_STATE: """ The internal method called when the window is resized. diff --git a/arcade/camera/default.py b/arcade/camera/default.py index b64399554..d4e30ee18 100644 --- a/arcade/camera/default.py +++ b/arcade/camera/default.py @@ -68,8 +68,6 @@ def viewport(self) -> tuple[int, int, int, int] | None: @viewport.setter def viewport(self, viewport: tuple[int, int, int, int] | None) -> None: - if viewport == self._viewport: - return self._viewport = viewport self._matrix = Mat4.orthogonal_projection( 0, self.width, 0, self.height, DEFAULT_NEAR_ORTHO, DEFAULT_FAR