+ .. code-block:: Python
+
+ import board
+ import displayio
+ import time
+ import pulseio
+
+ board.DISPLAY.auto_brightness = False
+ board.DISPLAY.brightness = 0
+ splash = displayio.Group()
+ board.DISPLAY.root_group = splash
+
+ odb = displayio.OnDiskBitmap(\'/sample.bmp\')
+ face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
+ splash.append(face)
+ # Wait for the image to load.
+ board.DISPLAY.refresh(target_frames_per_second=60)
+
+ # Fade up the backlight
+ for i in range(100):
+ board.DISPLAY.brightness = 0.01 * i
+ time.sleep(0.05)
+
+ # Wait forever
+ while True:
+ pass
+
+ """
+
+ 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 = memoryview(file.read(138)).cast(
+ "H"
+ ) # cast as unsigned 16-bit int
+
+ if len(bmp_header.tobytes()) != 138 or bmp_header.tobytes()[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]
+ 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._pixel_shader_base = ColorConverter(
+ input_colorspace=Colorspace.RGB888, dither=False
+ )
+
+ 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, dither=False)
+
+ if number_of_colors > 1:
+ palette_size = number_of_colors * 4
+ palette_offset = 0xE + header_size
+
+ file.seek(palette_offset)
+
+ palette_data = memoryview(file.read(palette_size)).cast(
+ "I"
+ ) # cast as unsigned 32-bit int
+ if len(palette_data.tobytes()) != palette_size:
+ raise ValueError("Unable to read color palette data")
+
+ for i in range(number_of_colors):
+ palette._set_color(
+ i, palette_data[i]
+ ) # pylint: disable=protected-access
+ else:
+ palette._set_color(0x000000, 0) # pylint: disable=protected-access
+ palette._set_color(0xFFFFFF, 1) # pylint: disable=protected-access
+ self._pixel_shader_base = 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