Skip to content

Commit

Permalink
Spritelist tweaks (#2160)
Browse files Browse the repository at this point in the history
* Default texture filter class variable + blend property

* Be more specific ..

* Remove blend parameter in initializer

* Tweaks

* tests

* Speed up unit tests by keeping textures

* Compare gc runtime without explicit gc.collect
  • Loading branch information
einarf authored Jun 30, 2024
1 parent 6ccbe55 commit 9ec913d
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
51 changes: 37 additions & 14 deletions arcade/sprite_list/sprite_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Callable,
cast,
Sized,
ClassVar,
)

from arcade import (
Expand Down Expand Up @@ -94,9 +95,20 @@ class SpriteList(Generic[SpriteType]):
:ref:`pg_spritelist_advanced_lazy_spritelists` to learn more.
:param visible: Setting this to False will cause the SpriteList to not
be drawn. When draw is called, the method will just return without drawing.
:param blend: Enable or disable blending for the sprite list
"""

#: The default texture filter used when no other filter is specified.
#: This can be used to change the global default for all spritelists
#:
#: Example::
#:
#: from arcade import gl
#: # Set global default to nearest filtering (pixelated)
#: arcade.SpriteList.DEFAULT_TEXTURE_FILTER = gl.NEAREST, gl.NEAREST
#: # Set global default to linear filtering (smooth). This is the default.
#: arcade.SpriteList.DEFAULT_TEXTURE_FILTER = gl.NEAREST, gl.NEAREST
DEFAULT_TEXTURE_FILTER: ClassVar[tuple[int, int]] = gl.LINEAR, gl.LINEAR

def __init__(
self,
use_spatial_hash: bool = False,
Expand All @@ -105,14 +117,13 @@ def __init__(
capacity: int = 100,
lazy: bool = False,
visible: bool = True,
blend: bool = True,
) -> None:
self.program: Optional[Program] = None
self._atlas: Optional[TextureAtlasBase] = atlas
self._initialized = False
self._lazy = lazy
self._visible = visible
self._blend = blend
self._blend = True
self._color: tuple[float, float, float, float] = (1.0, 1.0, 1.0, 1.0)

# The initial capacity of the spritelist buffers (internal)
Expand Down Expand Up @@ -305,6 +316,17 @@ def visible(self) -> bool:
def visible(self, value: bool) -> None:
self._visible = value

@property
def blend(self) -> bool:
"""
Flag for enabling or disabling alpha blending for the spritelist.
"""
return self._blend

@blend.setter
def blend(self, value: bool) -> None:
self._blend = value

@property
def color(self) -> Color:
"""
Expand Down Expand Up @@ -1006,13 +1028,17 @@ def draw(
:param filter: Optional parameter to set OpenGL filter, such as
`gl.GL_NEAREST` to avoid smoothing.
:param pixelated: ``True`` for pixelated and ``False`` for smooth interpolation.
Shortcut for setting filter=GL_NEAREST.
Shortcut for setting filter to GL_NEAREST for a pixelated look.
The filter parameter have precedence over this.
:param blend_function: Optional parameter to set the OpenGL blend function used for drawing the
sprite list, such as 'arcade.Window.ctx.BLEND_ADDITIVE' or 'arcade.Window.ctx.BLEND_DEFAULT'
"""
if len(self.sprite_list) == 0 or not self._visible or self.alpha_normalized == 0.0:
return

if not self.program:
raise ValueError("Attempting to render without shader program.")

self._init_deferred()
self._write_sprite_buffers_to_gpu()

Expand Down Expand Up @@ -1040,16 +1066,11 @@ def draw(
else: # assume it's an int
atlas_texture.filter = cast(OpenGlFilter, (filter, filter))
else:
atlas_texture.filter = self.ctx.LINEAR, self.ctx.LINEAR

# Handle the pixelated shortcut
if pixelated:
atlas_texture.filter = self.ctx.NEAREST, self.ctx.NEAREST
else:
atlas_texture.filter = self.ctx.LINEAR, self.ctx.LINEAR

if not self.program:
raise ValueError("Attempting to render without 'program' field being set.")
# Handle the pixelated shortcut if filter is not set
if pixelated:
atlas_texture.filter = self.ctx.NEAREST, self.ctx.NEAREST
else:
atlas_texture.filter = self.DEFAULT_TEXTURE_FILTER

self.program["spritelist_color"] = self._color

Expand All @@ -1063,8 +1084,10 @@ def draw(
vertices=self._sprite_index_slots,
)

# Leave global states to default
if self._blend:
self.ctx.disable(self.ctx.BLEND)
self.ctx.blend_func = self.ctx.BLEND_DEFAULT

def draw_hit_boxes(self, color: RGBA255 = (0, 0, 0, 255), line_thickness: float = 1.0) -> None:
"""Draw all the hit boxes in this list"""
Expand Down
11 changes: 6 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import gc
import os
import sys
from contextlib import contextmanager
from pathlib import Path

Expand All @@ -13,7 +12,8 @@
import pytest

import arcade
from arcade.texture import default_texture_cache
from arcade import gl
# from arcade.texture import default_texture_cache

PROJECT_ROOT = (Path(__file__).parent.parent).resolve()
FIXTURE_ROOT = PROJECT_ROOT / "tests" / "fixtures"
Expand Down Expand Up @@ -43,8 +43,9 @@ def prepare_window(window: arcade.Window):
window.set_size(800, 600)

ctx = window.ctx
ctx._atlas = None # Clear the global atlas
default_texture_cache.flush() # Clear the global/default texture cache
# ctx._atlas = None # Clear the global atlas
# default_texture_cache.flush() # Clear the global/default texture cache
arcade.SpriteList.DEFAULT_TEXTURE_FILTER = gl.LINEAR, gl.LINEAR
window.hide_view() # Disable views if any is active
window.dispatch_pending_events()
try:
Expand All @@ -60,7 +61,7 @@ def prepare_window(window: arcade.Window):
window.default_camera.use()
ctx.gc_mode = "context_gc"
ctx.gc()
gc.collect()
# gc.collect(1)

# Ensure no old functions are lingering
window.on_draw = lambda: None
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/spritelist/test_spritelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import struct
import pytest
import arcade
from arcade import gl


def make_named_sprites(amount):
Expand All @@ -18,6 +19,28 @@ def make_named_sprites(amount):
return spritelist


def test_filter(window):
atlas = arcade.DefaultTextureAtlas((256, 256))
spritelist = arcade.SpriteList(atlas=atlas)
spritelist.append(arcade.SpriteSolidColor(10, 10, color=arcade.color.WHITE))

assert atlas.texture.filter == (gl.LINEAR, gl.LINEAR)
spritelist.draw(filter=(gl.NEAREST, gl.NEAREST))
assert atlas.texture.filter == (gl.NEAREST, gl.NEAREST)
spritelist.draw()
assert atlas.texture.filter == (gl.LINEAR, gl.LINEAR)
spritelist.draw(pixelated=True)
assert atlas.texture.filter == (gl.NEAREST, gl.NEAREST)


def test_default_texture_filter(window):
arcade.SpriteList.DEFAULT_TEXTURE_FILTER = gl.NEAREST, gl.NEAREST
spritelist = arcade.SpriteList()
spritelist.append(arcade.SpriteSolidColor(10, 10, color=arcade.color.WHITE))
spritelist.draw()
assert spritelist.atlas.texture.filter == (gl.NEAREST, gl.NEAREST)


# Temp fix for https://github.com/pythonarcade/arcade/issues/2074
def test_copy_dunder_stubs_raise_notimplementederror():
spritelist = arcade.SpriteList()
Expand Down

0 comments on commit 9ec913d

Please sign in to comment.