Skip to content

Commit 1e2762d

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 1e2762d

File tree

1 file changed

+77
-8
lines changed

1 file changed

+77
-8
lines changed

adafruit_imageload/png.py

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,84 @@ 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+
121+
if depth != 8:
122+
raise ValueError("Must be 8bit depth.")
123+
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB888)
124+
bmp = bitmap(width, height, 65536)
125+
prev = bytearray(scanline)
126+
line = bytearray(scanline)
109127
for y in range(height):
110-
dst = y * scanline
111-
src = y * (scanline + 1) + 1
112-
filter_ = data_bytes[src - 1]
128+
src = y * (scanline + 1)
129+
filter_ = data_bytes[src]
130+
src += 1
113131
if filter_ == 0:
114-
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
132+
line[0:scanline] = data_bytes[src : src + scanline]
133+
elif filter_ == 1: # sub
134+
for i in range(scanline):
135+
a = line[i - unit] if i >= unit else 0
136+
line[i] = (data_bytes[src] + a) & 0xFF
137+
src += 1
138+
elif filter_ == 2: # up
139+
for i in range(scanline):
140+
b = prev[i]
141+
line[i] = (data_bytes[src] + b) & 0xFF
142+
src += 1
143+
elif filter_ == 3: # average
144+
for i in range(scanline):
145+
a = line[i - unit] if i >= unit else 0
146+
b = prev[i]
147+
line[i] = (data_bytes[src] + ((a + b) >> 1)) & 0xFF
148+
src += 1
149+
elif filter_ == 4: # paeth
150+
for i in range(scanline):
151+
a = line[i - unit] if i >= unit else 0
152+
b = prev[i]
153+
c = prev[i - unit] if i >= unit else 0
154+
p = a + b - c
155+
pa = abs(p - a)
156+
pb = abs(p - b)
157+
pc = abs(p - c)
158+
if pa <= pb and pa <= pc:
159+
p = a
160+
elif pb <= pc:
161+
p = b
162+
else:
163+
p = c
164+
line[i] = (data_bytes[src] + p) & 0xFF
165+
src += 1
166+
else:
167+
raise ValueError("Wrong filter.")
168+
prev, line = line, prev
169+
if mode in (0, 4): # grayscale
170+
for x in range(width):
171+
bmp[x, y] = pal.convert(
172+
(line[x * unit] << 16)
173+
| (line[x * unit] << 8)
174+
| line[x * unit]
175+
)
176+
elif mode in {2, 6}: # rgb
177+
for x in range(width):
178+
bmp[x, y] = pal.convert(
179+
(line[x * unit + 0] << 16)
180+
| (line[x * unit + 1] << 8)
181+
| line[x * unit + 2]
182+
)
115183
else:
116-
raise NotImplementedError("Filters not supported")
184+
raise ValueError("Unsupported color mode.")
185+
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565)
117186
return bmp, pal

0 commit comments

Comments
 (0)