+ if (self._absolute_transform.dx < 0) != flip_x:
+ x_shift = area.x2 - overlap.x2
+ else:
+ x_shift = overlap.x1 - area.x1
+ if (self._absolute_transform.dy < 0) != flip_y:
+ y_shift = area.y2 - overlap.y2
+ else:
+ y_shift = overlap.y1 - area.y1
+
+ # This untransposes x and y so it aligns with bitmap rows
+ if self._transpose_xy != self._absolute_transform.transpose_xy:
+ x_stride, y_stride = y_stride, x_stride
+ x_shift, y_shift = y_shift, x_shift
+
+ pixels_per_byte = 8 // colorspace.depth
+
+ input_pixel = InputPixelStruct()
+ output_pixel = OutputPixelStruct()
+ for input_pixel.y in range(start_y, end_y):
+ row_start = (
+ start + (input_pixel.y - start_y + y_shift) * y_stride
+ ) # In Pixels
+ local_y = input_pixel.y // self._absolute_transform.scale
+ for input_pixel.x in range(start_x, end_x):
+ # Compute the destination pixel in the buffer and mask based on the transformations
+ offset = (
+ row_start + (input_pixel.x - start_x + x_shift) * x_stride
+ ) # In Pixels
+
+ # Check the mask first to see if the pixel has already been set
+ if mask[offset // 32] & (1 << (offset % 32)):
+ continue
+ local_x = input_pixel.x // self._absolute_transform.scale
+ tile_location = (
+ (local_y // self._tile_height + self._top_left_y)
+ % self._height_in_tiles
+ ) * self._width_in_tiles + (
+ local_x // self._tile_width + self._top_left_x
+ ) % self._width_in_tiles
+ input_pixel.tile = tiles[tile_location]
+ input_pixel.tile_x = (
+ input_pixel.tile % self._bitmap_width_in_tiles
+ ) * self._tile_width + local_x % self._tile_width
+ input_pixel.tile_y = (
+ input_pixel.tile // self._bitmap_width_in_tiles
+ ) * self._tile_height + local_y % self._tile_height
+
+ output_pixel.pixel = 0
+ input_pixel.pixel = 0
+
+ # 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)):
+ input_pixel.pixel = (
+ self._bitmap._get_pixel( # pylint: disable=protected-access
+ input_pixel.tile_x, input_pixel.tile_y
+ )
+ )
+
+ output_pixel.opaque = True
+ if self._pixel_shader is None:
+ output_pixel.pixel = input_pixel.pixel
+ elif isinstance(self._pixel_shader, Palette):
+ self._pixel_shader._get_color( # pylint: disable=protected-access
+ colorspace, input_pixel, output_pixel
+ )
+ elif isinstance(self._pixel_shader, ColorConverter):
+ self._pixel_shader._convert( # pylint: disable=protected-access
+ colorspace, input_pixel, output_pixel
+ )
+
+ if not output_pixel.opaque:
+ full_coverage = False
+ else:
+ mask[offset // 32] |= 1 << (offset % 32)
+ # print("Mask", mask)
+ if colorspace.depth == 16:
+ struct.pack_into(
+ "H",
+ buffer.cast("H"),
+ offset,
+ output_pixel.pixel,
+ )
+ elif colorspace.depth == 32:
+ struct.pack_into(
+ "I",
+ buffer,
+ offset,
+ output_pixel.pixel,
+ )
+ elif colorspace.depth == 8:
+ 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).
+ if not colorspace.pixels_in_byte_share_row:
+ width = area.width()
+ row = offset // width
+ col = offset % width
+ # Dividing by pixels_per_byte does truncated division
+ # even if we multiply it back out
+ offset = (
+ col * pixels_per_byte
+ + (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.cast("B")[offset // pixels_per_byte] |= (
+ output_pixel.pixel << shift
+ )
+
+ return full_coverage