Skip to content

Commit 8ab825f

Browse files
authored
Merge pull request #61 from deshipu/png
Add PNG support
2 parents 3da18b6 + 9dd5315 commit 8ab825f

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

adafruit_imageload/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,8 @@ def load(
8080
from . import gif
8181

8282
return gif.load(file, bitmap=bitmap, palette=palette)
83+
if header.startswith(b"\x89PN"):
84+
from . import png
85+
86+
return png.load(file, bitmap=bitmap, palette=palette)
8387
raise RuntimeError("Unsupported image format")

adafruit_imageload/png.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# SPDX-FileCopyrightText: 2022 Radomir Dopieralski
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""
6+
`adafruit_imageload.png`
7+
====================================================
8+
9+
Load pixel values (indices or colors) into a bitmap and colors into a palette
10+
from a PNG file.
11+
12+
* Author(s): Radomir Dopieralski
13+
14+
"""
15+
16+
import struct
17+
import zlib
18+
19+
20+
__version__ = "0.0.0-auto.0"
21+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
22+
23+
24+
def load(
25+
file, *, bitmap, palette=None
26+
): # pylint: disable=too-many-locals,too-many-branches
27+
"""Loads a PNG image from the open ``file``.
28+
29+
Returns tuple of bitmap object and palette object.
30+
31+
:param file: The *.png file being loaded
32+
:param object bitmap: Type to store bitmap data. Must have API similar to
33+
`displayio.Bitmap`.
34+
:param object palette: Type to store the palette. Must have API similar to
35+
`displayio.Palette`. Will be skipped if None"""
36+
header = file.read(8)
37+
if header != b"\x89PNG\r\n\x1a\n":
38+
raise ValueError("Not a PNG file")
39+
del header
40+
data = bytearray()
41+
pal = None
42+
mode = None
43+
depth = None
44+
while True:
45+
size, chunk = struct.unpack(">I4s", file.read(8))
46+
if chunk == b"IHDR":
47+
(
48+
width,
49+
height,
50+
depth,
51+
mode,
52+
compression,
53+
filters,
54+
interlaced,
55+
) = struct.unpack(">IIBBBBB", file.read(13))
56+
if interlaced:
57+
raise NotImplementedError("Interlaced images unsupported")
58+
# compression and filters must be 0 with current spec
59+
assert compression == 0
60+
assert filters == 0
61+
elif chunk == b"PLTE":
62+
if palette is None:
63+
file.seek(size, 1)
64+
else:
65+
if mode != 3:
66+
raise NotImplementedError("Palette in non-indexed image")
67+
pal_size = size // 3
68+
pal = palette(pal_size)
69+
for i in range(pal_size):
70+
pal[i] = file.read(3)
71+
elif chunk == b"IDAT":
72+
data.extend(file.read(size))
73+
elif chunk == b"IEND":
74+
break
75+
else:
76+
file.seek(size, 1) # skip unknown chunks
77+
file.seek(4, 1) # skip CRC
78+
data = zlib.decompress(data)
79+
bmp = bitmap(width, height, 1 << depth)
80+
scanline = (width * depth + 7) // 8
81+
mem = memoryview(bmp)
82+
for y in range(height):
83+
dst = y * scanline
84+
src = y * (scanline + 1) + 1
85+
filter_ = data[src - 1]
86+
if filter_ == 0:
87+
mem[dst : dst + scanline] = data[src : src + scanline]
88+
else:
89+
raise NotImplementedError("Filters not supported")
90+
return bmp, pal

0 commit comments

Comments
 (0)