Skip to content

Commit 74b287c

Browse files
authored
Merge pull request #12 from crookedstorm/feature-tests
Working P1-P6 formats
2 parents e4c04af + 115716d commit 74b287c

29 files changed

+1500
-8
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ _build
44
.env
55
build*
66
bundles
7+
.idea/
8+
venv/
9+
10+
.vscode

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extension-pkg-whitelist=
77

88
# Add files or directories to the blacklist. They should be base names, not
99
# paths.
10-
ignore=CVS
10+
ignore=CVS,adafruit_imageload/tests
1111

1212
# Add files or directories matching the regex patterns to the blacklist. The
1313
# regex matches against base names, not paths.

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ install:
2727
- pip install --force-reinstall pylint==1.9.2
2828

2929
script:
30+
- pytest adafruit_imageload
3031
- pylint adafruit_imageload/**
3132
- ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py)
3233
- circuitpython-build-bundles --filename_prefix adafruit-circuitpython-imageload --library_location .

adafruit_imageload/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ def load(filename, *, bitmap=None, palette=None):
4343
"""
4444
with open(filename, "rb") as file:
4545
header = file.read(3)
46+
file.seek(0)
4647
if header.startswith(b"BM"):
4748
from . import bmp
48-
file.seek(0)
4949
return bmp.load(file, bitmap=bitmap, palette=palette)
50+
if header.startswith(b"P"):
51+
from . import pnm
52+
return pnm.load(file, header, bitmap=bitmap, palette=palette)
5053
raise RuntimeError("Unsupported image format")

adafruit_imageload/bmp/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ def load(file, *, bitmap=None, palette=None):
5656

5757
if colors == 0 and color_depth >= 16:
5858
raise NotImplementedError("True color BMP unsupported")
59-
else:
60-
if colors == 0:
61-
colors = 2 ** color_depth
62-
from . import indexed
63-
return indexed.load(file, width, height, data_start, colors, color_depth, bitmap=bitmap,
64-
palette=palette)
59+
60+
if colors == 0:
61+
colors = 2 ** color_depth
62+
from . import indexed
63+
return indexed.load(file, width, height, data_start, colors, color_depth, bitmap=bitmap,
64+
palette=palette)

adafruit_imageload/pnm/__init__.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
`adafruit_imageload.pnm`
24+
====================================================
25+
26+
Load pixel values (indices or colors) into a bitmap and colors into a palette.
27+
28+
* Author(s): Matt Land, Brooke Storm, Sam McGahan
29+
30+
"""
31+
32+
__version__ = "0.0.0-auto.0"
33+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
34+
35+
36+
def load(file, header, *, bitmap=None, palette=None):
37+
"""
38+
Scan for netpbm format info, skip over comments, and and delegate to a submodule
39+
to do the actual data loading.
40+
Formats P1, P4 have two space padded pieces of information: width and height.
41+
All other formats have three: width, height, and max color value.
42+
This load function will move the file stream pointer to the start of data in all cases.
43+
"""
44+
# pylint: disable=too-many-branches
45+
magic_number = header[:2]
46+
file.seek(2)
47+
pnm_header = []
48+
next_value = bytearray()
49+
while True:
50+
# We have all we need at length 3 for formats P2, P3, P5, P6
51+
if len(pnm_header) == 3:
52+
if magic_number in [b"P2", b"P5"]:
53+
from . import pgm
54+
55+
return pgm.load(
56+
file, magic_number, pnm_header, bitmap=bitmap, palette=palette
57+
)
58+
59+
if magic_number == b"P3":
60+
from . import ppm_ascii
61+
62+
return ppm_ascii.load(
63+
file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
64+
)
65+
66+
if magic_number == b"P6":
67+
from . import ppm_binary
68+
69+
return ppm_binary.load(
70+
file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
71+
)
72+
73+
if len(pnm_header) == 2 and magic_number in [b"P1", b"P4"]:
74+
bitmap = bitmap(pnm_header[0], pnm_header[1], 1)
75+
if palette:
76+
palette = palette(1)
77+
palette[0] = b"\xFF\xFF\xFF"
78+
if magic_number.startswith(b"P1"):
79+
from . import pbm_ascii
80+
81+
return pbm_ascii.load(
82+
file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
83+
)
84+
85+
from . import pbm_binary
86+
87+
return pbm_binary.load(
88+
file, pnm_header[0], pnm_header[1], bitmap=bitmap, palette=palette
89+
)
90+
91+
next_byte = file.read(1)
92+
if next_byte == b"":
93+
raise RuntimeError("Unsupported image format {}".format(magic_number))
94+
if next_byte == b"#": # comment found, seek until a newline or EOF is found
95+
while file.read(1) not in [b"", b"\n"]: # EOF or NL
96+
pass
97+
elif not next_byte.isdigit(): # boundary found in header data
98+
if next_value:
99+
# pull values until space is found
100+
pnm_header.append(int("".join(["%c" % char for char in next_value])))
101+
next_value = bytearray() # reset the byte array
102+
else:
103+
next_value += next_byte # push the digit into the byte array

adafruit_imageload/pnm/pbm_ascii.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
`adafruit_imageload.pnm.pbm_ascii`
24+
====================================================
25+
26+
Load pixel values (indices or colors) into a bitmap and for an ascii ppm,
27+
return None for pallet.
28+
29+
* Author(s): Matt Land, Brooke Storm, Sam McGahan
30+
31+
"""
32+
33+
__version__ = "0.0.0-auto.0"
34+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
35+
36+
37+
def load(file, width, height, bitmap=None, palette=None):
38+
"""
39+
Load a P1 'PBM' ascii image into the displayio.Bitmap
40+
"""
41+
next_byte = True
42+
for y in range(height):
43+
x = 0
44+
while next_byte:
45+
next_byte = file.read(1)
46+
if not next_byte.isdigit():
47+
continue
48+
bitmap[x, y] = 1 if next_byte == b"1" else 0
49+
if x == width - 1:
50+
break
51+
x += 1
52+
return bitmap, palette

adafruit_imageload/pnm/pbm_binary.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
`adafruit_imageload.pnm.pbm_binary`
24+
====================================================
25+
26+
Load pixel values (indices or colors) into a bitmap and for an ascii ppm,
27+
return None for pallet.
28+
29+
* Author(s): Matt Land, Brooke Storm, Sam McGahan
30+
31+
"""
32+
33+
__version__ = "0.0.0-auto.0"
34+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
35+
36+
37+
def load(file, width, height, bitmap=None, palette=None):
38+
"""
39+
Load a P4 'PBM' binary image into the displayio.Bitmap
40+
"""
41+
x = 0
42+
y = 0
43+
while True:
44+
next_byte = file.read(1)
45+
if not next_byte:
46+
break # out of bits
47+
for bit in iterbits(next_byte):
48+
bitmap[x, y] = bit
49+
x += 1
50+
if x > width - 1:
51+
y += 1
52+
x = 0
53+
if y > height - 1:
54+
break
55+
return bitmap, palette
56+
57+
58+
def iterbits(b):
59+
"""
60+
generator to iterate over the bits in a byte (character)
61+
"""
62+
in_char = reverse(int.from_bytes(b, "little"))
63+
for i in range(8):
64+
yield (in_char >> i) & 1
65+
66+
67+
def reverse(b):
68+
"""
69+
reverse bit order so the iterbits works
70+
"""
71+
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4
72+
b = (b & 0xCC) >> 2 | (b & 0x33) << 2
73+
b = (b & 0xAA) >> 1 | (b & 0x55) << 1
74+
return b
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
"""
23+
`adafruit_imageload.pnm.pgm`
24+
====================================================
25+
26+
Load pixel values (indices or colors) into a bitmap and colors into a palette.
27+
28+
* Author(s): Matt Land, Brooke Storm, Sam McGahan
29+
30+
"""
31+
32+
33+
def load(file, magic_number, header, *, bitmap=None, palette=None):
34+
"""
35+
Perform the load of Netpbm greyscale images (P2, P5)
36+
"""
37+
if header[2] > 256:
38+
raise NotImplementedError("16 bit files are not supported")
39+
width = header[0]
40+
height = header[1]
41+
42+
if magic_number == b"P2": # To handle ascii PGM files.
43+
from . import ascii as pgm_ascii
44+
45+
return pgm_ascii.load(file, width, height, bitmap=bitmap, palette=palette)
46+
47+
if magic_number == b"P5": # To handle binary PGM files.
48+
from . import binary
49+
50+
return binary.load(file, width, height, bitmap=bitmap, palette=palette)
51+
52+
raise NotImplementedError("Was not able to send image")

0 commit comments

Comments
 (0)