]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blobdiff - displayio/_ondiskbitmap.py
Merge pull request #156 from janvolck/main
[hackapet/Adafruit_Blinka_Displayio.git] / displayio / _ondiskbitmap.py
index f9e9e8b48ba2d994e2b7a6a6efc51a10fd507988..654a312a199b999b22231da11f5b1f262b0bbb04 100644 (file)
@@ -19,26 +19,15 @@ displayio for Blinka
 """
 
 from typing import Union, BinaryIO
+from ._helpers import read_word
 from ._colorconverter import ColorConverter
+from ._colorspace import Colorspace
 from ._palette import Palette
 
 __version__ = "0.0.0+auto.0"
 __repo__ = "https://github.com/adafruit/Adafruit_Blinka_displayio.git"
 
 
-def _read_uint32(buffer: bytes, idx: int) -> int:
-    return (
-        buffer[idx]
-        | buffer[idx + 1] << 8
-        | buffer[idx + 2] << 16
-        | buffer[idx + 3] << 24
-    )
-
-
-def _read_word(header: bytes, idx: int) -> int:
-    return _read_uint32(header, idx * 2)
-
-
 class OnDiskBitmap:
     # pylint: disable=too-many-instance-attributes
     """
@@ -46,9 +35,6 @@ class OnDiskBitmap:
     load times. These load times may result in frame tearing where only part of the image is
     visible.
 
-    It's easiest to use on a board with a built in display such as the `Hallowing M0 Express
-    <https://www.adafruit.com/product/3900>`_.
-
     .. code-block:: Python
 
         import board
@@ -59,7 +45,7 @@ class OnDiskBitmap:
         board.DISPLAY.auto_brightness = False
         board.DISPLAY.brightness = 0
         splash = displayio.Group()
-        board.DISPLAY.show(splash)
+        board.DISPLAY.root_group = splash
 
         odb = displayio.OnDiskBitmap(\'/sample.bmp\')
         face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader)
@@ -102,31 +88,35 @@ class OnDiskBitmap:
         try:
             self._file = file
             file.seek(0)
-            bmp_header = file.read(138)
+            bmp_header = memoryview(file.read(138)).cast(
+                "H"
+            )  # cast as unsigned 16-bit int
 
-            if len(bmp_header) != 138 or bmp_header[0:2] != b"BM":
+            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)
+            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)
+            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._width = read_word(bmp_header, 9)
+            self._height = read_word(bmp_header, 11)
 
-            self._colorconverter = ColorConverter()
+            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)
+                    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
@@ -136,7 +126,7 @@ class OnDiskBitmap:
                 if number_of_colors == 0:
                     number_of_colors = 1 << bits_per_pixel
 
-                palette = Palette(number_of_colors)
+                palette = Palette(number_of_colors, dither=False)
 
                 if number_of_colors > 1:
                     palette_size = number_of_colors * 4
@@ -144,16 +134,20 @@ class OnDiskBitmap:
 
                     file.seek(palette_offset)
 
-                    palette_data = file.read(palette_size)
-                    if len(palette_data) != palette_size:
+                    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[i] = _read_uint32(palette_data, i * 4)
+                        palette._set_color(
+                            palette_data[i], i
+                        )  # pylint: disable=protected-access
                 else:
-                    palette[0] = 0x000000
-                    palette[1] = 0xFFFFFF
-                self._palette = palette
+                    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: "
@@ -214,22 +208,6 @@ class OnDiskBitmap:
 
         return self._pixel_shader_base
 
-    @property
-    def _colorconverter(self) -> ColorConverter:
-        return self._pixel_shader_base
-
-    @_colorconverter.setter
-    def _colorconverter(self, colorconverter: ColorConverter) -> None:
-        self._pixel_shader_base = colorconverter
-
-    @property
-    def _palette(self) -> Palette:
-        return self._pixel_shader_base
-
-    @_palette.setter
-    def _palette(self, palette: Palette) -> None:
-        self._pixel_shader_base = palette
-
     def _get_pixel(self, x: int, y: int) -> int:
         if not (0 <= x < self.width and 0 <= y < self.height):
             return 0
@@ -272,7 +250,6 @@ class OnDiskBitmap:
                 return red << 19 | green << 10 | blue << 3
             if bytes_per_pixel == 4 and self._bitfield_compressed:
                 return pixel_data[0] | pixel_data[1] << 8 | pixel_data[2] << 16
-
             pixel = pixel_data[0] | pixel_data[1] << 8 | pixel_data[2] << 16
             if bytes_per_pixel == 4:
                 pixel |= pixel_data[3] << 24