Skip to content

Commit

Permalink
Update texture image fix (#1967)
Browse files Browse the repository at this point in the history
* Closes #1828

* More fixes

* pep8
  • Loading branch information
einarf authored Feb 10, 2024
1 parent 849ab2c commit 27ec873
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 54 deletions.
10 changes: 5 additions & 5 deletions arcade/draw_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,8 @@ def draw_lrtb_rectangle_filled(left: float, right: float, top: float,

center_x = (left + right) / 2
center_y = (top + bottom) / 2
width = right - left + 1
height = top - bottom + 1
width = right - left
height = top - bottom
draw_rectangle_filled(center_x, center_y, width, height, color)


Expand All @@ -838,8 +838,8 @@ def draw_lrbt_rectangle_filled(left: float, right: float, bottom: float, top: fl

center_x = (left + right) / 2
center_y = (top + bottom) / 2
width = right - left + 1
height = top - bottom + 1
width = right - left
height = top - bottom
draw_rectangle_filled(center_x, center_y, width, height, color)


Expand All @@ -853,7 +853,7 @@ def draw_xywh_rectangle_filled(bottom_left_x: float, bottom_left_y: float,
:param bottom_left_y: The y coordinate of the bottom of the rectangle.
:param width: The width of the rectangle.
:param height: The height of the rectangle.
:param color: The color of the rectangleas an RGBA
:param color: The color of the rectangles an RGBA
:py:class:`tuple` or :py:class`~arcade.types.Color` instance.
"""
center_x = bottom_left_x + (width / 2)
Expand Down
40 changes: 16 additions & 24 deletions arcade/texture_atlas/atlas_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,11 @@ def _allocate_image(self, image_data: "ImageData") -> Tuple[int, int, int, Atlas

# def write_texture(self, texture: "Texture", x: int, y: int):
# """
# Writes an arcade texture to a subsection of the texture atlas
# Writes an arcade texture to a subsection of the texture atlas.

# :param texture: The arcade texture
# :param x: The x position to write the texture
# :param y: The y position to write the texture
# """
# self.write_image(texture.image, x, y)

Expand Down Expand Up @@ -1012,46 +1016,34 @@ def calculate_minimum_size(cls, textures: Sequence["Texture"], border: int = 1):

return size, size

def get_texture_image(self, texture: "Texture") -> Image.Image:
def read_texture_image_from_atlas(self, texture: "Texture") -> Image.Image:
"""
Get a Pillow image of a texture's region in the atlas.
This can be used to inspect the contents of the atlas
or to save the texture to disk.
Read the pixel data for a texture directly from the atlas texture on the GPU.
The contents of this image can be altered by rendering into the atlas and
is useful in situations were you need the updated pixel data on the python side.
:param texture: The texture to get the image for
:return: A pillow image containing the pixel data in the atlas
"""
region = self.get_image_region_info(texture.image_data.hash)
viewport = (
region.x + self._border,
region.y + self._border,
region.x,
region.y,
region.width,
region.height,
)
data = self.fbo.read(viewport=viewport, components=4)
return Image.frombytes("RGBA", (region.width, region.height), data)

def sync_texture_image(self, texture: "Texture") -> None:
def update_texture_image_from_atlas(self, texture: "Texture") -> None:
"""
Updates a texture's image with the contents in the
texture atlas. This is usually not needed, but if
you have altered a texture in the atlas directly
this can be used to copy the image data back into
the texture.
Updating the image will not change the texture's
hash or the texture's hit box points.
.. warning::
This method is somewhat expensive and should be used sparingly.
Altering the internal image of a texture is not recommended
unless you know exactly what you're doing. Textures are
supposed to be immutable.
Update the arcade Texture's internal image with the pixel data content
from the atlas texture on the GPU. This can be useful if you render
into the atlas and need to update the texture with the new pixel data.
:param texture: The texture to update
"""
texture.image_data.image = self.get_texture_image(texture)
texture.image_data.image = self.read_texture_image_from_atlas(texture)

def to_image(
self,
Expand Down
25 changes: 0 additions & 25 deletions tests/unit/atlas/test_sync_texture_image.py

This file was deleted.

35 changes: 35 additions & 0 deletions tests/unit/atlas/test_update_texture_image_from_atlas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Test syncing atlas textures back into PIL images.
"""
from PIL import Image, ImageDraw
import arcade


def _create_image_with_rect(rect) -> Image.Image:
# Create an image with a centered square
im = Image.new("RGBA", (10, 10), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
draw.rectangle(rect, fill=(255, 0, 0, 255))
return im


def test_update_texture_image_from_atlas():
# Original image
im = _create_image_with_rect((1, 1, 4, 4))
im = im.transpose(Image.FLIP_TOP_BOTTOM)

atlas = arcade.TextureAtlas((256, 256), border=10)
tex = arcade.Texture(im)
atlas.add(tex)

# Check that the original image matches
atlas_im = atlas.read_texture_image_from_atlas(tex)
assert atlas_im.tobytes() == im.tobytes()

# Render new content and verify this content
with atlas.render_into(tex) as area:
area.clear()
arcade.draw_lrbt_rectangle_filled(1, 5, 1, 5, arcade.color.RED)

atlas_im = atlas.read_texture_image_from_atlas(tex)
assert atlas_im.tobytes() == im.tobytes()

0 comments on commit 27ec873

Please sign in to comment.