From 30f3e71827c6e14b4cf3fa2a59b3f1301e74a528 Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Fri, 19 Jul 2024 15:13:42 -0500 Subject: [PATCH 1/7] Initial Commit Add support for JPG files using a wrapper that implements the jpegio library. --- adafruit_imageload/jpg.py | 0 examples/imageload_jpg_simpletest.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 adafruit_imageload/jpg.py create mode 100644 examples/imageload_jpg_simpletest.py diff --git a/adafruit_imageload/jpg.py b/adafruit_imageload/jpg.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/imageload_jpg_simpletest.py b/examples/imageload_jpg_simpletest.py new file mode 100644 index 0000000..e69de29 From 0e2c51241ac55a8bcfe091b46ac2011c631f2d04 Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Mon, 22 Jul 2024 12:28:53 -0500 Subject: [PATCH 2/7] JPG Reading Implemented Added a JPG module that calls jpegio and updated the base __init__.py to recognize JPG files. --- adafruit_imageload/__init__.py | 5 +++++ adafruit_imageload/jpg.py | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/adafruit_imageload/__init__.py b/adafruit_imageload/__init__.py index bd8cd98..38524e3 100644 --- a/adafruit_imageload/__init__.py +++ b/adafruit_imageload/__init__.py @@ -68,6 +68,7 @@ def load( with open_file as file: header = file.read(3) file.seek(0) + print(header) if header.startswith(b"BM"): from . import bmp @@ -89,4 +90,8 @@ def load( from . import png return png.load(file, bitmap=bitmap, palette=palette) + if header.startswith(b"\xff\xd8"): + from . import jpg + + return jpg.load(file, bitmap=bitmap, palette=palette) raise RuntimeError("Unsupported image format") diff --git a/adafruit_imageload/jpg.py b/adafruit_imageload/jpg.py index e69de29..e2ae895 100644 --- a/adafruit_imageload/jpg.py +++ b/adafruit_imageload/jpg.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2024 Channing Ramos +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_imageload.jpg` +==================================================== + +Load a JPG into a bitmap by calling jpegio. + +* Author(s): Channing Ramos + +""" + +#A separate try for jpegio. While it has wide support it is not universal, and this import may fail. +#If that happens an ImportError with a proper message needs to be raised +try: + from jpegio import JpegDecoder +except ImportError: + print("jpegio not supported on this board.") + +try: + from io import BufferedReader + from typing import Tuple, Iterator, Optional, List + from .displayio_types import PaletteConstructor, BitmapConstructor +except ImportError: + pass + +from displayio import Bitmap, ColorConverter, Colorspace + +def load(file: BufferedReader, + *, + bitmap: BitmapConstructor, + palette: Optional[PaletteConstructor] = None) -> Tuple[Bitmap, Optional[ColorConverter]]: + + decoder = JpegDecoder() + width, height = decoder.open(file) + bitmap_obj = bitmap(width, height, 65535) + decoder.decode(bitmap_obj) + + return bitmap_obj, ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED) \ No newline at end of file From dbb20f58fddc696959497825a01ac27c48174477 Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Mon, 22 Jul 2024 16:04:40 -0500 Subject: [PATCH 3/7] Removed palette option from load function Removed the palette option from the load function since it isnt used, as we use colorconverter instead. --- adafruit_imageload/__init__.py | 3 +-- adafruit_imageload/jpg.py | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/adafruit_imageload/__init__.py b/adafruit_imageload/__init__.py index 38524e3..0a54f68 100644 --- a/adafruit_imageload/__init__.py +++ b/adafruit_imageload/__init__.py @@ -68,7 +68,6 @@ def load( with open_file as file: header = file.read(3) file.seek(0) - print(header) if header.startswith(b"BM"): from . import bmp @@ -93,5 +92,5 @@ def load( if header.startswith(b"\xff\xd8"): from . import jpg - return jpg.load(file, bitmap=bitmap, palette=palette) + return jpg.load(file, bitmap=bitmap) raise RuntimeError("Unsupported image format") diff --git a/adafruit_imageload/jpg.py b/adafruit_imageload/jpg.py index e2ae895..4570ee9 100644 --- a/adafruit_imageload/jpg.py +++ b/adafruit_imageload/jpg.py @@ -6,7 +6,7 @@ `adafruit_imageload.jpg` ==================================================== -Load a JPG into a bitmap by calling jpegio. +Load a JPG into a bitmap by calling the jpegio class. * Author(s): Channing Ramos @@ -21,21 +21,34 @@ try: from io import BufferedReader - from typing import Tuple, Iterator, Optional, List - from .displayio_types import PaletteConstructor, BitmapConstructor + from typing import Tuple, Optional + from .displayio_types import BitmapConstructor except ImportError: pass from displayio import Bitmap, ColorConverter, Colorspace +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" + def load(file: BufferedReader, *, bitmap: BitmapConstructor, - palette: Optional[PaletteConstructor] = None) -> Tuple[Bitmap, Optional[ColorConverter]]: - + ) -> Tuple[Bitmap, Optional[ColorConverter]]: + """ + Loads a JPG image from the open ''file''. + The JPG must be a Baseline JPG, Progressive and Lossless JPG formats are not supported. + + Returns tuple of bitmap object and ColorConverter object. + + :param io.BufferedReader file: Open file handle or compatible (like 'io.BytesIO') + :param object bitmap: Type to store bitmap data. + Must have API similar to 'displayio.Bitmap'. Will be skipped if None. + Will be skipped if None. + """ decoder = JpegDecoder() width, height = decoder.open(file) bitmap_obj = bitmap(width, height, 65535) decoder.decode(bitmap_obj) - return bitmap_obj, ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED) \ No newline at end of file + return bitmap_obj, ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED) From 41a28c13676595879326e3e6e185215ff76d8ecf Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Mon, 22 Jul 2024 16:32:07 -0500 Subject: [PATCH 4/7] Simpletest completed. Completed the simpletest example script, along with a provided jpg. --- examples/imageload_jpg_simpletest.py | 18 ++++++++++++++++++ examples/images/jpg_test.jpg | Bin 0 -> 5465 bytes examples/images/jpg_test.jpg.license | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 examples/images/jpg_test.jpg create mode 100644 examples/images/jpg_test.jpg.license diff --git a/examples/imageload_jpg_simpletest.py b/examples/imageload_jpg_simpletest.py index e69de29..22d5e05 100644 --- a/examples/imageload_jpg_simpletest.py +++ b/examples/imageload_jpg_simpletest.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2024 Channing Ramos +# +# SPDX-License-Identifier: MIT + +import board +import displayio +import adafruit_imageload + +group = displayio.Group() +board.DISPLAY.root_group = group + +image, color_converter = adafruit_imageload.load("images/jpg_test.jpg") + +tile_grid = displayio.TileGrid(image, pixel_shader=color_converter) +group.append(tile_grid) + +while True: + pass diff --git a/examples/images/jpg_test.jpg b/examples/images/jpg_test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7479826e16a4c5440dedc123f0fecf48f4d4e8a5 GIT binary patch literal 5465 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 literal 0 HcmV?d00001 diff --git a/examples/images/jpg_test.jpg.license b/examples/images/jpg_test.jpg.license new file mode 100644 index 0000000..ea30990 --- /dev/null +++ b/examples/images/jpg_test.jpg.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 Channing Ramos +# SPDX-License-Identifier: MIT \ No newline at end of file From 72e7ae25afa91af9c78f4c3b8f814b1bee9ea043 Mon Sep 17 00:00:00 2001 From: ch4nsuk3 <134003603+ch4nsuk3@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:19:14 -0500 Subject: [PATCH 5/7] Corrected Actions Errors Disabled the too-many-branches pylint check in __init__.py The new 'if' statement goes over the limit of 12 branches. Shortened comment in jpg.py --- adafruit_imageload/__init__.py | 1 + adafruit_imageload/jpg.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/adafruit_imageload/__init__.py b/adafruit_imageload/__init__.py index 0a54f68..3f632a6 100644 --- a/adafruit_imageload/__init__.py +++ b/adafruit_imageload/__init__.py @@ -47,6 +47,7 @@ def load( palette is the desired palette type. The constructor should take the number of colors and support assignment to indices via []. """ + # pylint: disable=too-many-branches if not bitmap or not palette: try: # use displayio if available diff --git a/adafruit_imageload/jpg.py b/adafruit_imageload/jpg.py index 4570ee9..3101b37 100644 --- a/adafruit_imageload/jpg.py +++ b/adafruit_imageload/jpg.py @@ -12,7 +12,7 @@ """ -#A separate try for jpegio. While it has wide support it is not universal, and this import may fail. +#A separate try for jpegio. Not every board supports it and this import may fail. #If that happens an ImportError with a proper message needs to be raised try: from jpegio import JpegDecoder From 63779103dcaa661c6be7748a98e795b0d7f245ac Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Thu, 25 Jul 2024 14:23:28 -0500 Subject: [PATCH 6/7] Re-ran Black locally Formatting errors in jpg.py should be corrected now. PyCharm was using CRLF instead of LF line separators. --- adafruit_imageload/jpg.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/adafruit_imageload/jpg.py b/adafruit_imageload/jpg.py index 3101b37..9af3d60 100644 --- a/adafruit_imageload/jpg.py +++ b/adafruit_imageload/jpg.py @@ -12,8 +12,8 @@ """ -#A separate try for jpegio. Not every board supports it and this import may fail. -#If that happens an ImportError with a proper message needs to be raised +# A separate try for jpegio. Not every board supports it and this import may fail. +# If that happens an ImportError with a proper message needs to be raised try: from jpegio import JpegDecoder except ImportError: @@ -31,10 +31,12 @@ __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" -def load(file: BufferedReader, - *, - bitmap: BitmapConstructor, - ) -> Tuple[Bitmap, Optional[ColorConverter]]: + +def load( + file: BufferedReader, + *, + bitmap: BitmapConstructor, +) -> Tuple[Bitmap, Optional[ColorConverter]]: """ Loads a JPG image from the open ''file''. The JPG must be a Baseline JPG, Progressive and Lossless JPG formats are not supported. From 9b9cbf41fda9e23caa5542b3f2c9ad8782ece871 Mon Sep 17 00:00:00 2001 From: ch4nsuk3 Date: Thu, 25 Jul 2024 14:34:26 -0500 Subject: [PATCH 7/7] Missed Files Helps if I include them all. --- examples/imageload_jpg_simpletest.py | 4 ++++ examples/images/jpg_test.jpg.license | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/imageload_jpg_simpletest.py b/examples/imageload_jpg_simpletest.py index 22d5e05..dc4b8ef 100644 --- a/examples/imageload_jpg_simpletest.py +++ b/examples/imageload_jpg_simpletest.py @@ -2,6 +2,10 @@ # # SPDX-License-Identifier: MIT +""" +Basic JPG imageload example +""" + import board import displayio import adafruit_imageload diff --git a/examples/images/jpg_test.jpg.license b/examples/images/jpg_test.jpg.license index ea30990..d2f8702 100644 --- a/examples/images/jpg_test.jpg.license +++ b/examples/images/jpg_test.jpg.license @@ -1,2 +1,2 @@ # SPDX-FileCopyrightText: 2024 Channing Ramos -# SPDX-License-Identifier: MIT \ No newline at end of file +# SPDX-License-Identifier: MIT