From 9f2cec9a38c4e79dc8b8777801914255166805d7 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 3 Aug 2023 15:51:56 -0700 Subject: [PATCH 1/3] Add 16, 24, and 32-bit bmp support --- adafruit_imageload/bmp/__init__.py | 14 ++++- adafruit_imageload/bmp/truecolor.py | 90 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) mode change 100644 => 100755 adafruit_imageload/bmp/__init__.py create mode 100755 adafruit_imageload/bmp/truecolor.py diff --git a/adafruit_imageload/bmp/__init__.py b/adafruit_imageload/bmp/__init__.py old mode 100644 new mode 100755 index 03532d0..b4409fb --- a/adafruit_imageload/bmp/__init__.py +++ b/adafruit_imageload/bmp/__init__.py @@ -62,12 +62,20 @@ def load( file.seek(0x2E) # Number of colors in the color palette colors = int.from_bytes(file.read(4), "little") - if colors == 0 and color_depth >= 16: - raise NotImplementedError("True color BMP unsupported") - if compression > 2: raise NotImplementedError("bitmask compression unsupported") + if colors == 0 and color_depth >= 16: + from . import truecolor + + return truecolor.load( + file, + _width, + _height, + data_start, + color_depth, + bitmap=bitmap, + ) if colors == 0: colors = 2**color_depth from . import indexed diff --git a/adafruit_imageload/bmp/truecolor.py b/adafruit_imageload/bmp/truecolor.py new file mode 100755 index 0000000..5317ba0 --- /dev/null +++ b/adafruit_imageload/bmp/truecolor.py @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries +# SPDX-FileCopyrightText: 2022-2023 Matt Land +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_imageload.bmp.indexed` +==================================================== + +Load pixel values (indices or colors) into a bitmap and colors into a palette from an indexed BMP. + +* Author(s): Scott Shawcroft, Matt Land, Melissa LeBlanc-Williams + +""" + +import sys + +try: + from typing import Tuple, Optional + from io import BufferedReader + from displayio import Bitmap + from ..displayio_types import BitmapConstructor +except ImportError: + pass + +from displayio import ColorConverter, Colorspace + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" + + +def load( + file: BufferedReader, + width: int, + height: int, + data_start: int, + color_depth: int, + *, + bitmap: Optional[BitmapConstructor] = None, +) -> Tuple[Optional[Bitmap], Optional[ColorConverter]]: + """Loads truecolor bitmap data into bitmap and palette objects. Due to the 16-bit limit + that the bitmap object can hold, colors will be converted to 16-bit RGB565 values. + + :param file file: The open bmp file + :param int width: Image width in pixels + :param int height: Image height in pixels + :param int data_start: Byte location where the data starts (after headers) + :param int color_depth: Number of bits used to store a value + :param BitmapConstructor bitmap: a function that returns a displayio.Bitmap + """ + # pylint: disable=too-many-arguments,too-many-locals,too-many-branches + converter_obj = None + bitmap_obj = None + if bitmap: + input_colorspace = Colorspace.RGB888 + if color_depth == 16: + input_colorspace = Colorspace.RGB555 + converter_obj = ColorConverter(input_colorspace=input_colorspace) + if sys.maxsize > 1073741823: + # pylint: disable=import-outside-toplevel, relative-beyond-top-level + from .negative_height_check import negative_height_check + + # convert unsigned int to signed int when height is negative + height = negative_height_check(height) + bitmap_obj = bitmap(width, abs(height), 65535) + file.seek(data_start) + line_size = width * (color_depth // 8) + if height > 0: + range1 = height - 1 + range2 = -1 + range3 = -1 + else: + range1 = 0 + range2 = abs(height) + range3 = 1 + chunk = bytearray(line_size) + for y in range(range1, range2, range3): + file.readinto(chunk) + bytes_per_pixel = color_depth // 8 + offset = y * width + + for x in range(width): + i = x * bytes_per_pixel + if color_depth == 16: + pixel = chunk[i] | chunk[i + 1] << 8 + else: + pixel = chunk[i + 2] << 16 | chunk[i + 1] << 8 | chunk[i] + bitmap_obj[offset + x] = converter_obj.convert(pixel) + + return bitmap_obj, ColorConverter(input_colorspace=Colorspace.RGB565) From c4a4c5ae5be441864265df84440e3f61b251aecf Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 3 Aug 2023 15:54:24 -0700 Subject: [PATCH 2/3] Fix up headers --- adafruit_imageload/bmp/truecolor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adafruit_imageload/bmp/truecolor.py b/adafruit_imageload/bmp/truecolor.py index 5317ba0..c81ed26 100755 --- a/adafruit_imageload/bmp/truecolor.py +++ b/adafruit_imageload/bmp/truecolor.py @@ -1,15 +1,15 @@ # SPDX-FileCopyrightText: 2018 Scott Shawcroft for Adafruit Industries -# SPDX-FileCopyrightText: 2022-2023 Matt Land +# SPDX-FileCopyrightText: 2022-2023 Melissa LeBlanc-Williams # # SPDX-License-Identifier: MIT """ -`adafruit_imageload.bmp.indexed` +`adafruit_imageload.bmp.truecolor` ==================================================== -Load pixel values (indices or colors) into a bitmap and colors into a palette from an indexed BMP. +Load pixel colors into a bitmap from an truecolor BMP and return the correct colorconverter. -* Author(s): Scott Shawcroft, Matt Land, Melissa LeBlanc-Williams +* Author(s): Melissa LeBlanc-Williams """ From f6ddfcee2876cfb8bf9149f17585c14783a70daf Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 3 Aug 2023 16:19:25 -0700 Subject: [PATCH 3/3] Add some comments --- adafruit_imageload/bmp/truecolor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/adafruit_imageload/bmp/truecolor.py b/adafruit_imageload/bmp/truecolor.py index c81ed26..710d6bf 100755 --- a/adafruit_imageload/bmp/truecolor.py +++ b/adafruit_imageload/bmp/truecolor.py @@ -52,6 +52,8 @@ def load( converter_obj = None bitmap_obj = None if bitmap: + # Set up a ColorConverter object and set appropriate colorspace + # to convert from based on the color depth input_colorspace = Colorspace.RGB888 if color_depth == 16: input_colorspace = Colorspace.RGB555 @@ -65,6 +67,7 @@ def load( bitmap_obj = bitmap(width, abs(height), 65535) file.seek(data_start) line_size = width * (color_depth // 8) + # Set the seek direction based on whether the height value is negative or positive if height > 0: range1 = height - 1 range2 = -1