diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fff3aa9..1dad804 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,7 +40,7 @@ jobs: source actions-ci/install.sh - name: Pip install pylint, black, & Sphinx run: | - pip install --force-reinstall pylint==1.9.2 black==19.10b0 Sphinx sphinx-rtd-theme + pip install --force-reinstall pylint black==19.10b0 Sphinx sphinx-rtd-theme - name: Library version run: git describe --dirty --always --tags - name: PyLint diff --git a/adafruit_imageload/__init__.py b/adafruit_imageload/__init__.py index cc744bf..c6fe8b5 100644 --- a/adafruit_imageload/__init__.py +++ b/adafruit_imageload/__init__.py @@ -28,10 +28,12 @@ * Author(s): Scott Shawcroft """ +# pylint: disable=import-outside-toplevel __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" + def load(filename, *, bitmap=None, palette=None): """Load pixel values (indices or colors) into a bitmap and colors into a palette. @@ -46,11 +48,14 @@ def load(filename, *, bitmap=None, palette=None): file.seek(0) if header.startswith(b"BM"): from . import bmp + return bmp.load(file, bitmap=bitmap, palette=palette) if header.startswith(b"P"): from . import pnm + return pnm.load(file, header, bitmap=bitmap, palette=palette) if header.startswith(b"GIF"): from . import gif + return gif.load(file, bitmap=bitmap, palette=palette) raise RuntimeError("Unsupported image format") diff --git a/adafruit_imageload/bmp/__init__.py b/adafruit_imageload/bmp/__init__.py index 0c77f07..5a72935 100644 --- a/adafruit_imageload/bmp/__init__.py +++ b/adafruit_imageload/bmp/__init__.py @@ -28,10 +28,12 @@ * Author(s): Scott Shawcroft """ +# pylint: disable=import-outside-toplevel __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" + def load(file, *, bitmap=None, palette=None): """Loads a bmp image from the open ``file``. @@ -42,19 +44,19 @@ def load(file, *, bitmap=None, palette=None): :param object palette: Type to store the palette. Must have API similar to `displayio.Palette`. Will be skipped if None""" file.seek(10) - data_start = int.from_bytes(file.read(4), 'little') + data_start = int.from_bytes(file.read(4), "little") # f.seek(14) # bmp_header_length = int.from_bytes(file.read(4), 'little') # print(bmp_header_length) - file.seek(0x12) # Width of the bitmap in pixels - width = int.from_bytes(file.read(4), 'little') - height = int.from_bytes(file.read(4), 'little') - file.seek(0x1c) # Number of bits per pixel - color_depth = int.from_bytes(file.read(2), 'little') - file.seek(0x1e) # Compression type - compression = int.from_bytes(file.read(2), 'little') - file.seek(0x2e) # Number of colors in the color palette - colors = int.from_bytes(file.read(4), 'little') + file.seek(0x12) # Width of the bitmap in pixels + width = int.from_bytes(file.read(4), "little") + height = int.from_bytes(file.read(4), "little") + file.seek(0x1C) # Number of bits per pixel + color_depth = int.from_bytes(file.read(2), "little") + file.seek(0x1E) # Compression type + compression = int.from_bytes(file.read(2), "little") + 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") @@ -65,5 +67,15 @@ def load(file, *, bitmap=None, palette=None): if colors == 0: colors = 2 ** color_depth from . import indexed - return indexed.load(file, width, height, data_start, colors, color_depth, - compression, bitmap=bitmap, palette=palette) + + return indexed.load( + file, + width, + height, + data_start, + colors, + color_depth, + compression, + bitmap=bitmap, + palette=palette, + ) diff --git a/adafruit_imageload/bmp/indexed.py b/adafruit_imageload/bmp/indexed.py index e25c89b..4d1178a 100644 --- a/adafruit_imageload/bmp/indexed.py +++ b/adafruit_imageload/bmp/indexed.py @@ -32,8 +32,19 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" -def load(file, width, height, data_start, colors, color_depth, compression, *, - bitmap=None, palette=None): + +def load( + file, + width, + height, + data_start, + colors, + color_depth, + compression, + *, + bitmap=None, + palette=None +): """Loads indexed bitmap data into bitmap and palette objects. :param file file: The open bmp file @@ -51,18 +62,17 @@ def load(file, width, height, data_start, colors, color_depth, compression, *, for value in range(colors): c_bytes = file.read(4) # Need to swap red & blue bytes (bytes 0 and 2) - palette[value] = bytes(b''.join([c_bytes[2:3], - c_bytes[1:2], - c_bytes[0:1], - c_bytes[3:1]])) + palette[value] = bytes( + b"".join([c_bytes[2:3], c_bytes[1:2], c_bytes[0:1], c_bytes[3:1]]) + ) if bitmap: minimum_color_depth = 1 while colors > 2 ** minimum_color_depth: minimum_color_depth *= 2 - #convert unsigned int to signed int when height is negative - if height > 0x7fffffff: + # convert unsigned int to signed int when height is negative + if height > 0x7FFFFFFF: height = height - 4294967296 bitmap = bitmap(width, abs(height), colors) file.seek(data_start) @@ -70,7 +80,7 @@ def load(file, width, height, data_start, colors, color_depth, compression, *, if width % (8 // color_depth) != 0: line_size += 1 if line_size % 4 != 0: - line_size += (4 - line_size % 4) + line_size += 4 - line_size % 4 mask = (1 << minimum_color_depth) - 1 if height > 0: @@ -91,7 +101,9 @@ def load(file, width, height, data_start, colors, color_depth, compression, *, for x in range(width): i = x // pixels_per_byte - pixel = (chunk[i] >> (8 - color_depth*(x % pixels_per_byte + 1))) & mask + pixel = ( + chunk[i] >> (8 - color_depth * (x % pixels_per_byte + 1)) + ) & mask bitmap[offset + x] = pixel elif compression in (1, 2): decode_rle( @@ -99,10 +111,12 @@ def load(file, width, height, data_start, colors, color_depth, compression, *, file=file, compression=compression, y_range=(range1, range2, range3), - width=width) + width=width, + ) return bitmap, palette + def decode_rle(bitmap, file, compression, y_range, width): """Helper to decode RLE images""" # pylint: disable=too-many-locals,too-many-nested-blocks,too-many-branches @@ -218,10 +232,7 @@ def decode_rle(bitmap, file, compression, y_range, width): # 0xab 3 times, the output pixel values would be: 0x0a # 0x0b 0x0a (notice how it ends at 0x0a) rather than # 0x0a 0x0b 0x0a 0x0b 0x0a 0x0b - run_values = [ - run_buf[1] >> 4, - run_buf[1] & 0x0F - ] + run_values = [run_buf[1] >> 4, run_buf[1] & 0x0F] for i in range(0, min(run_length_px, width_remaining)): bitmap[offset + i] = run_values[i % 2] else: @@ -229,5 +240,4 @@ def decode_rle(bitmap, file, compression, y_range, width): for i in range(0, min(run_length_px, width_remaining)): bitmap[offset + i] = run_value - x = x + run_length_px diff --git a/adafruit_imageload/gif.py b/adafruit_imageload/gif.py index 4657ee1..c5ead05 100644 --- a/adafruit_imageload/gif.py +++ b/adafruit_imageload/gif.py @@ -47,9 +47,9 @@ def load(file, *, bitmap=None, palette=None): :param object palette: Type to store the palette. Must have API similar to `displayio.Palette`. Will be skipped if None""" header = file.read(6) - if header not in {b'GIF87a', b'GIF89a'}: + if header not in {b"GIF87a", b"GIF89a"}: raise ValueError("Not a GIF file") - width, height, flags, _, _ = struct.unpack('= 1 << self.code_len and - self.code_len < 12): + if ( + len(self.codes) + self.end_code + 1 >= 1 << self.code_len + and self.code_len < 12 + ): self.code_len += 1 self.last = value return value @@ -151,7 +154,7 @@ def lzw_decode(data, code_size): """Decode LZW-compressed data.""" dictionary = LZWDict(code_size) bit = 0 - byte = next(data) # pylint: disable=stop-iteration-return + byte = next(data) # pylint: disable=stop-iteration-return try: while True: code = 0 @@ -160,8 +163,8 @@ def lzw_decode(data, code_size): bit += 1 if bit >= 8: bit = 0 - byte = next(data) # pylint: disable=stop-iteration-return + byte = next(data) # pylint: disable=stop-iteration-return yield dictionary.decode(code) except EndOfData: while True: - next(data) # pylint: disable=stop-iteration-return + next(data) # pylint: disable=stop-iteration-return diff --git a/adafruit_imageload/pnm/__init__.py b/adafruit_imageload/pnm/__init__.py index fec8726..802ab2a 100644 --- a/adafruit_imageload/pnm/__init__.py +++ b/adafruit_imageload/pnm/__init__.py @@ -28,6 +28,7 @@ * Author(s): Matt Land, Brooke Storm, Sam McGahan """ +# pylint: disable=import-outside-toplevel __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git" diff --git a/adafruit_imageload/pnm/pgm/__init__.py b/adafruit_imageload/pnm/pgm/__init__.py index f05d536..167213f 100644 --- a/adafruit_imageload/pnm/pgm/__init__.py +++ b/adafruit_imageload/pnm/pgm/__init__.py @@ -28,6 +28,7 @@ * Author(s): Matt Land, Brooke Storm, Sam McGahan """ +# pylint: disable=import-outside-toplevel def load(file, magic_number, header, *, bitmap=None, palette=None): diff --git a/docs/conf.py b/docs/conf.py index ba93d8f..0e79813 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,7 +2,8 @@ import os import sys -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ @@ -10,10 +11,10 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.napoleon', - 'sphinx.ext.todo', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx.ext.todo", ] # TODO: Please Read! @@ -23,29 +24,32 @@ autodoc_mock_imports = ["displayio"] -intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} +intersphinx_mapping = { + "python": ("https://docs.python.org/3.4", None), + "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), +} # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Adafruit_ ImageLoad Library' -copyright = u'2018 Scott Shawcroft' -author = u'Scott Shawcroft' +project = "Adafruit_ ImageLoad Library" +copyright = "2018 Scott Shawcroft" +author = "Scott Shawcroft" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = u'1.0' +version = "1.0" # The full version, including alpha/beta/rc tags. -release = u'1.0' +release = "1.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -57,7 +61,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -69,7 +73,7 @@ add_function_parentheses = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -84,59 +88,62 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +on_rtd = os.environ.get("READTHEDOCS", None) == "True" if not on_rtd: # only import and set the theme if we're building docs locally try: import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.'] + + html_theme = "sphinx_rtd_theme" + html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] except: - html_theme = 'default' - html_theme_path = ['.'] + html_theme = "default" + html_theme_path = ["."] else: - html_theme_path = ['.'] + html_theme_path = ["."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # -html_favicon = '_static/favicon.ico' +html_favicon = "_static/favicon.ico" # Output file base name for HTML help builder. -htmlhelp_basename = 'Adafruit_ImageloadLibrarydoc' +htmlhelp_basename = "Adafruit_ImageloadLibrarydoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'Adafruit_ImageLoadLibrary.tex', u'Adafruit_ImageLoad Library Documentation', - author, 'manual'), + ( + master_doc, + "Adafruit_ImageLoadLibrary.tex", + "Adafruit_ImageLoad Library Documentation", + author, + "manual", + ), ] # -- Options for manual page output --------------------------------------- @@ -144,8 +151,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'Adafruit_ImageLoadlibrary', u'Adafruit_ ImageLoad Library Documentation', - [author], 1) + ( + master_doc, + "Adafruit_ImageLoadlibrary", + "Adafruit_ ImageLoad Library Documentation", + [author], + 1, + ) ] # -- Options for Texinfo output ------------------------------------------- @@ -154,7 +166,13 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'Adafruit_ImageLoadLibrary', u'Adafruit_ ImageLoad Library Documentation', - author, 'Adafruit_ImageLoadLibrary', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "Adafruit_ImageLoadLibrary", + "Adafruit_ ImageLoad Library Documentation", + author, + "Adafruit_ImageLoadLibrary", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/examples/imageload_colorwheel.py b/examples/imageload_colorwheel.py index a60f5b9..ceb23d2 100644 --- a/examples/imageload_colorwheel.py +++ b/examples/imageload_colorwheel.py @@ -4,9 +4,9 @@ display = board.DISPLAY -bitmap, palette = adafruit_imageload.load("images/color_wheel.bmp", - bitmap=displayio.Bitmap, - palette=displayio.Palette) +bitmap, palette = adafruit_imageload.load( + "images/color_wheel.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette +) tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette) diff --git a/examples/imageload_netpbm.py b/examples/imageload_netpbm.py index a29396c..9d3ed51 100644 --- a/examples/imageload_netpbm.py +++ b/examples/imageload_netpbm.py @@ -26,11 +26,11 @@ # Make the display context splash = displayio.Group(max_size=10) display.show(splash) -#image = "images/netpbm_p1_mono_ascii.pbm" -#image = "images/netpbm_p2_ascii.pgm" -#image = "images/netpbm_p3_rgb_ascii.ppm" -#image = "images/netpbm_p4_mono_binary.pbm" -#image = "images/netpbm_p5_binary.pgm" +# image = "images/netpbm_p1_mono_ascii.pbm" +# image = "images/netpbm_p2_ascii.pgm" +# image = "images/netpbm_p3_rgb_ascii.ppm" +# image = "images/netpbm_p4_mono_binary.pbm" +# image = "images/netpbm_p5_binary.pgm" image = "images/netpbm_p6_binary.ppm" bitmap, palette = adafruit_imageload.load( diff --git a/examples/imageload_simpletest.py b/examples/imageload_simpletest.py index 17dc3bf..d53646a 100644 --- a/examples/imageload_simpletest.py +++ b/examples/imageload_simpletest.py @@ -1,6 +1,6 @@ import displayio import adafruit_imageload -image, palette = adafruit_imageload.load("images/4bit.bmp", - bitmap=displayio.Bitmap, - palette=displayio.Palette) +image, palette = adafruit_imageload.load( + "images/4bit.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette +) diff --git a/tests/displayio_shared_bindings.py b/tests/displayio_shared_bindings.py index bcbc49d..141059e 100644 --- a/tests/displayio_shared_bindings.py +++ b/tests/displayio_shared_bindings.py @@ -76,7 +76,9 @@ def __setitem__(self, key: Union[tuple, int], value: int) -> None: if value > 255: raise ValueError(f"pixel value {value} too large") if self.data.get(key): - raise ValueError(f"pixel {self._decode(key)}/{key} already set, cannot set again") + raise ValueError( + f"pixel {self._decode(key)}/{key} already set, cannot set again" + ) self.data[key] = value def __getitem__(self, item: Union[tuple, int]) -> bytearray: @@ -100,11 +102,13 @@ def validate(self, detect_empty_image=True) -> None: for y in range(self.height): for x in range(self.width): try: - seen_colors.add(self[x,y]) + seen_colors.add(self[x, y]) except KeyError: raise ValueError(f"missing data at {x},{y}") if detect_empty_image and len(seen_colors) < 2: - raise ValueError('image detected as only one color. set detect_empty_image=False to ignore') + raise ValueError( + "image detected as only one color. set detect_empty_image=False to ignore" + ) def __str__(self) -> str: """ diff --git a/tests/test_pbm_load.py b/tests/test_pbm_load.py index b7f8bb9..d7eca5c 100644 --- a/tests/test_pbm_load.py +++ b/tests/test_pbm_load.py @@ -123,16 +123,16 @@ def test_load_works_p4_binary_high_res(self): palette.validate() def test_iterbits(self): - k = b'k' + k = b"k" bits = [] for byte in iterbits(k): bits.append(byte) - #self.assertEqual([0,1,1,0,1,0,1,1], bits[::-1]) - self.assertEqual([0,1,1,0,1,0,1,1], bits) + # self.assertEqual([0,1,1,0,1,0,1,1], bits[::-1]) + self.assertEqual([0, 1, 1, 0, 1, 0, 1, 1], bits) def test_reverse(self): # 00110100 to 00101100 self.assertEqual(reverse(0x34), 0x2C) self.assertEqual(reverse(0xFF), 0xFF) self.assertEqual(reverse(0x00), 0x00) - self.assertEqual(reverse(0x0E), 0x70) \ No newline at end of file + self.assertEqual(reverse(0x0E), 0x70) diff --git a/tests/test_ppm_load.py b/tests/test_ppm_load.py index 5cd8854..bb8de7b 100644 --- a/tests/test_ppm_load.py +++ b/tests/test_ppm_load.py @@ -51,7 +51,7 @@ def test_load_works_p3_ascii(self): self.assertTrue(isinstance(palette, Palette_C_Interface)) self.assertEqual(6, palette.num_colors) palette.validate() - #self.fail(str(palette)) + # self.fail(str(palette)) self.assertTrue(isinstance(bitmap, Bitmap_C_Interface), bitmap) self.assertEqual(6, bitmap.colors) self.assertEqual(16, bitmap.width) @@ -79,12 +79,12 @@ def test_load_works_p6_binary(self): bitmap.validate() def test_load_three_colors_tail(self): - buffer = BytesIO(b'211 222 233') + buffer = BytesIO(b"211 222 233") for i in read_three_colors(buffer): - self.assertEqual(b'\xd3\xde\xe9', i) + self.assertEqual(b"\xd3\xde\xe9", i) def test_load_three_colors_middle(self): - buffer = BytesIO(b'0 128 255 45 55 25') + buffer = BytesIO(b"0 128 255 45 55 25") for i in iter(read_three_colors(buffer)): - self.assertEqual(b'\x00\x80\xff', i) + self.assertEqual(b"\x00\x80\xff", i) break