+ def __init__(self, file: Union[str, BinaryIO]) -> None:
+ # pylint: disable=too-many-locals, too-many-branches, too-many-statements
+ """
+ Create an OnDiskBitmap object with the given file.
+
+ :param file file: The name of the bitmap file. For backwards compatibility, a file opened
+ in binary mode may also be passed.
+
+ Older versions of CircuitPython required a file opened in binary mode. CircuitPython 7.0
+ modified OnDiskBitmap so that it takes a filename instead, and opens the file internally.
+ A future version of CircuitPython will remove the ability to pass in an opened file.
+ """
+
+ if isinstance(file, str):
+ file = open(file, "rb") # pylint: disable=consider-using-with
+
+ if not (file.readable() and file.seekable()):
+ raise TypeError("file must be a file opened in byte mode")
+
+ self._pixel_shader_base: Union[ColorConverter, Palette, None] = None
+
+ try:
+ self._file = file
+ file.seek(0)
+ bmp_header = file.read(138)
+
+ if len(bmp_header) != 138 or bmp_header[0:2] != b"BM":
+ raise ValueError("Invalid BMP file")
+
+ self._data_offset = _read_word(bmp_header, 5)
+
+ header_size = _read_word(bmp_header, 7)
+ bits_per_pixel = bmp_header[14 * 2] | bmp_header[14 * 2 + 1] << 8
+ compression = _read_word(bmp_header, 15)
+ number_of_colors = _read_word(bmp_header, 23)
+
+ indexed = bits_per_pixel <= 8
+ self._bitfield_compressed = compression == 3
+ self._bits_per_pixel = bits_per_pixel
+ self._width = _read_word(bmp_header, 9)
+ self._height = _read_word(bmp_header, 11)
+
+ self._colorconverter = ColorConverter()
+
+ if bits_per_pixel == 16:
+ if header_size >= 56 or self._bitfield_compressed:
+ self._r_bitmask = _read_word(bmp_header, 27)
+ self._g_bitmask = _read_word(bmp_header, 29)
+ self._b_bitmask = _read_word(bmp_header, 31)
+ else:
+ # No compression or short header mean 5:5:5
+ self._r_bitmask = 0x7C00
+ self._g_bitmask = 0x03E0
+ self._b_bitmask = 0x001F
+ elif indexed:
+ if number_of_colors == 0:
+ number_of_colors = 1 << bits_per_pixel
+
+ palette = Palette(number_of_colors)
+
+ if number_of_colors > 1:
+ palette_size = number_of_colors * 4
+ palette_offset = 0xE + header_size
+
+ file.seek(palette_offset)
+
+ palette_data = file.read(palette_size)
+ if len(palette_data) != palette_size:
+ raise ValueError("Unable to read color palette data")
+
+ for i in range(number_of_colors):
+ palette[i] = _read_uint32(palette_data, i * 4)
+ else:
+ palette[0] = 0x000000
+ palette[1] = 0xFFFFFF
+ self._palette = palette
+ elif header_size not in (12, 40, 108, 124):
+ raise ValueError(
+ "Only Windows format, uncompressed BMP supported: "
+ f"given header size is {header_size}"
+ )
+
+ if bits_per_pixel == 8 and number_of_colors == 0:
+ raise ValueError(
+ "Only monochrome, indexed 4bpp or 8bpp, and 16bpp or greater BMPs supported: "
+ f"{bits_per_pixel} bpp given"
+ )
+
+ bytes_per_pixel = (
+ self._bits_per_pixel // 8 if (self._bits_per_pixel // 8) else 1
+ )
+ pixels_per_byte = 8 // self._bits_per_pixel
+ if pixels_per_byte == 0:
+ self._stride = self._width * bytes_per_pixel
+ if self._stride % 4 != 0:
+ self._stride += 4 - self._stride % 4
+ else:
+ bit_stride = self._width * self._bits_per_pixel
+ if bit_stride % 32 != 0:
+ bit_stride += 32 - bit_stride % 32
+ self._stride = bit_stride // 8
+ except IOError as error:
+ raise OSError from error