Skip to content

Commit 3936f44

Browse files
committed
Add support for RGB, RGBA and Grayscale PNGs
This adds support for filters, which lets us decode grayscale and RGB images. I only support 8bit depth for now (and 24bit for RGB) but with better color conversion code, other depths could be supported.
1 parent 7a68dcd commit 3936f44

File tree

1 file changed

+71
-6
lines changed

1 file changed

+71
-6
lines changed

adafruit_imageload/png.py

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,80 @@ def load(
103103
file.seek(size, 1) # skip unknown chunks
104104
file.seek(4, 1) # skip CRC
105105
data_bytes = zlib.decompress(data)
106-
bmp = bitmap(width, height, 1 << depth)
107-
scanline = (width * depth + 7) // 8
108-
mem = memoryview(bmp)
106+
unit = (1, 0, 3, 1, 2, 0, 4)[mode]
107+
scanline = (width * depth * unit + 7) // 8
108+
colors = 1 << (depth * unit)
109+
if mode == 3: # indexed
110+
bmp = bitmap(width, height, colors)
111+
mem = memoryview(bmp)
112+
for y in range(height):
113+
dst = y * scanline
114+
src = y * (scanline + 1) + 1
115+
filter_ = data_bytes[src - 1]
116+
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
117+
return bmp, pal
118+
# RGB, RGBA or Grayscale
119+
import displayio
120+
if depth != 8:
121+
raise ValueError("Must be 8bit depth.")
122+
pal = displayio.ColorConverter(
123+
input_colorspace=displayio.Colorspace.RGB888)
124+
bmp = bitmap(width, height, 65536)
125+
prev = bytearray(scanline)
109126
for y in range(height):
110-
dst = y * scanline
127+
line = bytearray(scanline)
111128
src = y * (scanline + 1) + 1
112129
filter_ = data_bytes[src - 1]
113130
if filter_ == 0:
114-
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
131+
line[0 : scanline] = data_bytes[src : src + scanline]
132+
elif filter_ == 1: # sub
133+
for i in range(scanline):
134+
a = line[i - unit] if i >= unit else 0
135+
line[i] = (data_bytes[src + i] + a) & 0xff
136+
elif filter_ == 2: # up
137+
for i in range(scanline):
138+
b = prev[i]
139+
line[i] = (data_bytes[src + i] + b) & 0xff
140+
elif filter_ == 3: # average
141+
for i in range(scanline):
142+
a = line[i - unit] if i >= unit else 0
143+
b = prev[i]
144+
line[i] = (data_bytes[src + i] + ((a + b) >> 1)) & 0xff
145+
elif filter_ == 4: # paeth
146+
for i in range(scanline):
147+
a = line[i - unit] if i >= unit else 0
148+
b = prev[i]
149+
c = prev[i - unit] if i >= unit else 0
150+
p = a + b - c
151+
pa = abs(p - a)
152+
pb = abs(p - b)
153+
pc = abs(p - c)
154+
if pa <= pb and pa <= pc:
155+
p = a
156+
elif pb <= pc:
157+
p = b
158+
else:
159+
p = c
160+
line[i] = (data_bytes[src + i] + p) & 0xff
161+
else:
162+
raise ValueError("Wrong filter.")
163+
prev = line
164+
if mode in (0, 4): # grayscale
165+
for x in range(width):
166+
bmp[x, y] = pal.convert(
167+
(line[x * unit] << 16) |
168+
(line[x * unit] << 8) |
169+
line[x * unit]
170+
)
171+
elif mode in {2, 6}: # rgb
172+
for x in range(width):
173+
bmp[x, y] = pal.convert(
174+
(line[x * unit + 0] << 16) |
175+
(line[x * unit + 1] << 8) |
176+
line[x * unit + 2]
177+
)
115178
else:
116-
raise NotImplementedError("Filters not supported")
179+
raise ValueError("Unsupported color mode.")
180+
pal = displayio.ColorConverter(
181+
input_colorspace=displayio.Colorspace.RGB565)
117182
return bmp, pal

0 commit comments

Comments
 (0)