from ._bitmap import Bitmap
from ._colorconverter import ColorConverter
from ._ondiskbitmap import OnDiskBitmap
-from ._shape import Shape
from ._palette import Palette
from ._structs import (
InputPixelStruct,
def __init__(
self,
- bitmap: Union[Bitmap, OnDiskBitmap, Shape],
+ bitmap: Union[Bitmap, OnDiskBitmap],
*,
pixel_shader: Union[ColorConverter, Palette],
width: int = 1,
tile_width and tile_height match the height of the bitmap by default.
"""
- if not isinstance(bitmap, (Bitmap, OnDiskBitmap, Shape)):
+ if not isinstance(bitmap, (Bitmap, OnDiskBitmap)):
raise ValueError("Unsupported Bitmap type")
self._bitmap = bitmap
bitmap_width = bitmap.width
self._tiles_in_bitmap = self._bitmap_width_in_tiles * (
bitmap_height // tile_height
)
+ self._needs_refresh = True
def _update_transform(self, absolute_transform):
"""Update the parent transform and child transforms"""
self._update_current_y()
def _update_current_x(self):
+ self._needs_refresh = True
if self._transpose_xy:
width = self._pixel_height
else:
)
def _update_current_y(self):
+ self._needs_refresh = True
if self._transpose_xy:
height = self._pixel_width
else:
if self._hidden_tilegrid or self._hidden_by_parent:
return False
-
overlap = Area() # area, current_area, overlap
if not area.compute_overlap(self._current_area, overlap):
return False
+ # else:
+ # print("Checking", area.x1, area.y1, area.x2, area.y2)
+ # print("Overlap", overlap.x1, overlap.y1, overlap.x2, overlap.y2)
if self._bitmap.width <= 0 or self._bitmap.height <= 0:
return False
# We always want to read bitmap pixels by row first and then transpose into
# the destination buffer because most bitmaps are row associated.
- if isinstance(self._bitmap, (Bitmap, Shape, OnDiskBitmap)):
+ if isinstance(self._bitmap, (Bitmap, OnDiskBitmap)):
input_pixel.pixel = (
self._bitmap._get_pixel( # pylint: disable=protected-access
input_pixel.tile_x, input_pixel.tile_y
if colorspace.depth == 16:
struct.pack_into(
"H",
- buffer,
+ buffer.cast("B"),
offset * 2,
output_pixel.pixel,
)
elif colorspace.depth == 32:
struct.pack_into(
"I",
- buffer,
+ buffer.cast("B"),
offset * 4,
output_pixel.pixel,
)
elif colorspace.depth == 8:
- buffer[offset] = output_pixel.pixel & 0xFF
+ buffer.cast("B")[offset] = output_pixel.pixel & 0xFF
elif colorspace.depth < 8:
# Reorder the offsets to pack multiple rows into
# a byte (meaning they share a column).
# even if we multiply it back out
offset = (
col * pixels_per_byte
- + (row // pixels_per_byte) * width
+ + (row // pixels_per_byte) * pixels_per_byte * width
+ (row % pixels_per_byte)
)
shift = (offset % pixels_per_byte) * colorspace.depth
if colorspace.reverse_pixels_in_byte:
# Reverse the shift by subtracting it from the leftmost shift
shift = (pixels_per_byte - 1) * colorspace.depth - shift
- buffer[offset // pixels_per_byte] |= output_pixel.pixel << shift
+ buffer.cast("B")[offset // pixels_per_byte] |= (
+ output_pixel.pixel << shift
+ )
+
return full_coverage
def _finish_refresh(self):
- first_draw = self._previous_area.x1 == self._previous_area.x2
- hidden = self._hidden_tilegrid or self._hidden_by_parent
- if not first_draw and hidden:
- self._previous_area.x2 = self._previous_area.x1
- elif self._moved or first_draw:
- self._current_area.copy_into(self._previous_area)
-
- self._moved = False
- self._full_change = False
- self._partial_change = False
- if isinstance(self._pixel_shader, (Palette, ColorConverter)):
- self._pixel_shader._finish_refresh() # pylint: disable=protected-access
- if isinstance(self._bitmap, (Bitmap, Shape)):
- self._bitmap._finish_refresh() # pylint: disable=protected-access
+ if not self._needs_refresh:
+ first_draw = self._previous_area.x1 == self._previous_area.x2
+ hidden = self._hidden_tilegrid or self._hidden_by_parent
+ if not first_draw and hidden:
+ self._previous_area.x2 = self._previous_area.x1
+ elif self._moved or first_draw:
+ self._current_area.copy_into(self._previous_area)
+
+ self._moved = False
+ self._full_change = False
+ self._partial_change = False
+ if isinstance(self._pixel_shader, (Palette, ColorConverter)):
+ self._pixel_shader._finish_refresh() # pylint: disable=protected-access
+ if isinstance(self._bitmap, Bitmap):
+ self._bitmap._finish_refresh() # pylint: disable=protected-access
def _get_refresh_areas(self, areas: list[Area]) -> None:
# pylint: disable=invalid-name, too-many-branches, too-many-statements
self._rendered_hidden = True
if not first_draw:
areas.append(self._previous_area)
+ self._needs_refresh = False
return
if self._moved and not first_draw:
self._previous_area.union(self._current_area, self._dirty_area)
if self._dirty_area.size() < 2 * self._pixel_width * self._pixel_height:
areas.append(self._dirty_area)
+ self._needs_refresh = False
return
areas.append(self._current_area)
areas.append(self._previous_area)
+ self._needs_refresh = False
return
tail = areas[-1] if areas else None
if isinstance(self._bitmap, Bitmap):
self._bitmap._get_refresh_areas(areas) # pylint: disable=protected-access
refresh_area = areas[-1] if areas else None
- if tail != refresh_area:
+ if refresh_area != tail:
# Special case a TileGrid that shows a full bitmap and use its
# dirty area. Copy it to ours so we can transform it.
if self._tiles_in_bitmap == 1:
self._partial_change = True
else:
self._full_change = True
- elif isinstance(self._bitmap, Shape):
- self._bitmap._get_refresh_areas(areas) # pylint: disable=protected-access
- refresh_area = areas[-1] if areas else None
- if refresh_area != tail:
- refresh_area.copy_into(self._dirty_area)
- self._partial_change = True
self._full_change = self._full_change or (
isinstance(self._pixel_shader, (Palette, ColorConverter))
)
if self._full_change or first_draw:
areas.append(self._current_area)
+ self._needs_refresh = False
return
if self._partial_change:
self._dirty_area.x1,
)
areas.append(self._dirty_area)
+ self._needs_refresh = False
def _set_hidden(self, hidden: bool) -> None:
+ self._needs_refresh = True
self._hidden_tilegrid = hidden
self._rendered_hidden = False
if not hidden:
self._full_change = True
def _set_hidden_by_parent(self, hidden: bool) -> None:
+ self._needs_refresh = True
self._hidden_by_parent = hidden
self._rendered_hidden = False
if not hidden:
)
self._full_change = True
+ def _set_tile(self, x: int, y: int, tile_index: int) -> None:
+ self._needs_refresh = True
+ self._tiles[y * self._width_in_tiles + x] = tile_index
+ temp_area = Area()
+ if not self._partial_change:
+ tile_area = self._dirty_area
+ else:
+ tile_area = temp_area
+ top_x = (x - self._top_left_x) % self._width_in_tiles
+ if top_x < 0:
+ top_x += self._width_in_tiles
+ tile_area.x1 = top_x * self._tile_width
+ tile_area.x2 = tile_area.x1 + self._tile_width
+ top_y = (y - self._top_left_y) % self._height_in_tiles
+ if top_y < 0:
+ top_y += self._height_in_tiles
+ tile_area.y1 = top_y * self._tile_height
+ tile_area.y2 = tile_area.y1 + self._tile_height
+
+ if self._partial_change:
+ self._dirty_area.union(temp_area, self._dirty_area)
+
+ self._partial_change = True
+
def _set_top_left(self, x: int, y: int) -> None:
self._top_left_x = x
self._top_left_y = y
if not isinstance(value, bool):
raise TypeError("Flip X should be a boolean type")
if self._flip_x != value:
+ self._needs_refresh = True
self._flip_x = value
self._full_change = True
if not isinstance(value, bool):
raise TypeError("Flip Y should be a boolean type")
if self._flip_y != value:
+ self._needs_refresh = True
self._flip_y = value
self._full_change = True
if not isinstance(value, bool):
raise TypeError("Transpose XY should be a boolean type")
if self._transpose_xy != value:
+ self._needs_refresh = True
self._transpose_xy = value
if self._pixel_width == self._pixel_height:
self._full_change = True
self._pixel_shader = new_pixel_shader
self._full_change = True
+ self._needs_refresh = True
@property
- def bitmap(self) -> Union[Bitmap, OnDiskBitmap, Shape]:
- """The Bitmap, OnDiskBitmap, or Shape that is assigned to this TileGrid"""
+ def bitmap(self) -> Union[Bitmap, OnDiskBitmap]:
+ """The Bitmap or OnDiskBitmap that is assigned to this TileGrid"""
return self._bitmap
@bitmap.setter
- def bitmap(self, new_bitmap: Union[Bitmap, OnDiskBitmap, Shape]) -> None:
- if (
- not isinstance(new_bitmap, Bitmap)
- and not isinstance(new_bitmap, OnDiskBitmap)
- and not isinstance(new_bitmap, Shape)
+ def bitmap(self, new_bitmap: Union[Bitmap, OnDiskBitmap]) -> None:
+ if not isinstance(new_bitmap, Bitmap) and not isinstance(
+ new_bitmap, OnDiskBitmap
):
raise TypeError(
- "Unsupported Type: new_bitmap must be Bitmap, OnDiskBitmap, or Shape"
+ "Unsupported Type: new_bitmap must be Bitmap or OnDiskBitmap"
)
if (
):
raise ValueError("New bitmap must be same size as old bitmap")
+ self._needs_refresh = True
self._bitmap = new_bitmap
self._full_change = True
or index >= len(self._tiles)
):
raise ValueError("Tile index out of bounds")
- return index
+ return x, y
def __getitem__(self, index: Union[Tuple[int, int], int]) -> int:
"""Returns the tile index at the given index. The index can either be
an x,y tuple or an int equal to ``y * width + x``'.
"""
- index = self._extract_and_check_index(index)
- return self._tiles[index]
+ x, y = self._extract_and_check_index(index)
+ return self._tiles[y * self._width_in_tiles + x]
def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None:
"""Sets the tile index at the given index. The index can either be
an x,y tuple or an int equal to ``y * width + x``.
"""
- index = self._extract_and_check_index(index)
+ x, y = self._extract_and_check_index(index)
if not 0 <= value <= 255:
raise ValueError("Tile value out of bounds")
- self._tiles[index] = value
+ self._set_tile(x, y, value)
@property
def width(self) -> int: