+ 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
+ first_draw = self._previous_area.x1 == self._previous_area.x2
+ hidden = self._hidden_tilegrid or self._hidden_by_parent
+
+ # Check hidden first because it trumps all other changes
+ if hidden:
+ 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 we have an in-memory bitmap, then check it for modifications
+ if isinstance(self._bitmap, Bitmap):
+ self._bitmap._get_refresh_areas(areas) # pylint: disable=protected-access
+ refresh_area = areas[-1] if areas else None
+ 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:
+ refresh_area.copy_into(self._dirty_area)
+ self._partial_change = True
+ else:
+ self._full_change = True
+
+ self._full_change = self._full_change or (
+ isinstance(self._pixel_shader, (Palette, ColorConverter))
+ and self._pixel_shader._needs_refresh # pylint: disable=protected-access
+ )
+ if self._full_change or first_draw:
+ areas.append(self._current_area)
+ self._needs_refresh = False
+ return
+
+ if self._partial_change:
+ x = self._x
+ y = self._y
+ if self._absolute_transform.transpose_xy:
+ x, y = y, x
+ x1 = self._dirty_area.x1
+ x2 = self._dirty_area.x2
+ if self._flip_x:
+ x1 = self._pixel_width - x1
+ x2 = self._pixel_width - x2
+ y1 = self._dirty_area.y1
+ y2 = self._dirty_area.y2
+ if self._flip_y:
+ y1 = self._pixel_height - y1
+ y2 = self._pixel_height - y2
+ if self._transpose_xy != self._absolute_transform.transpose_xy:
+ x1, y1 = y1, x1
+ x2, y2 = y2, x2
+ self._dirty_area.x1 = (
+ self._absolute_transform.x + self._absolute_transform.dx * (x + x1)
+ )
+ self._dirty_area.y1 = (
+ self._absolute_transform.y + self._absolute_transform.dy * (y + y1)
+ )
+ self._dirty_area.x2 = (
+ self._absolute_transform.x + self._absolute_transform.dx * (x + x2)
+ )
+ self._dirty_area.y2 = (
+ self._absolute_transform.y + self._absolute_transform.dy * (y + y2)
+ )
+ if self._dirty_area.y2 < self._dirty_area.y1:
+ self._dirty_area.y1, self._dirty_area.y2 = (
+ self._dirty_area.y2,
+ self._dirty_area.y1,
+ )
+ if self._dirty_area.x2 < self._dirty_area.x1:
+ self._dirty_area.x1, self._dirty_area.x2 = (
+ self._dirty_area.x2,
+ 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 _get_rendered_hidden(self) -> bool:
+ return self._rendered_hidden
+
+ def _set_all_tiles(self, tile_index: int) -> None:
+ """Set all tiles to the given tile index"""
+ if tile_index >= self._tiles_in_bitmap:
+ raise ValueError("Tile index out of bounds")
+ self._tiles = bytearray(
+ (self._width_in_tiles * self._height_in_tiles) * [tile_index]
+ )
+ 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
+ self._full_change = True