Skip to content

Commit 056568e

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 056568e

File tree

1 file changed

+73
-8
lines changed

1 file changed

+73
-8
lines changed

adafruit_imageload/png.py

Lines changed: 73 additions & 8 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+
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
116+
return bmp, pal
117+
# RGB, RGBA or Grayscale
118+
import displayio
119+
120+
if depth != 8:
121+
raise ValueError("Must be 8bit depth.")
122+
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB888)
123+
bmp = bitmap(width, height, 65536)
124+
prev = bytearray(scanline)
125+
line = bytearray(scanline)
109126
for y in range(height):
110-
dst = y * scanline
111-
src = y * (scanline + 1) + 1
112-
filter_ = data_bytes[src - 1]
127+
src = y * (scanline + 1)
128+
filter_ = data_bytes[src]
129+
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] + a) & 0xFF
136+
src += 1
137+
elif filter_ == 2: # up
138+
for i in range(scanline):
139+
b = prev[i]
140+
line[i] = (data_bytes[src] + b) & 0xFF
141+
src += 1
142+
elif filter_ == 3: # average
143+
for i in range(scanline):
144+
a = line[i - unit] if i >= unit else 0
145+
b = prev[i]
146+
line[i] = (data_bytes[src] + ((a + b) >> 1)) & 0xFF
147+
src += 1
148+
elif filter_ == 4: # paeth
149+
for i in range(scanline):
150+
a = line[i - unit] if i >= unit else 0
151+
b = prev[i]
152+
c = prev[i - unit] if i >= unit else 0
153+
p = a + b - c
154+
pa = abs(p - a)
155+
pb = abs(p - b)
156+
pc = abs(p - c)
157+
if pa <= pb and pa <= pc:
158+
p = a
159+
elif pb <= pc:
160+
p = b
161+
else:
162+
p = c
163+
line[i] = (data_bytes[src] + p) & 0xFF
164+
src += 1
165+
else:
166+
raise ValueError("Wrong filter.")
167+
prev, line = line, prev
168+
if mode in (0, 4): # grayscale
169+
for x in range(width):
170+
c = line[x * unit]
171+
bmp[x, y] = pal.convert((c << 16) | (c << 8) | c)
172+
elif mode in {2, 6}: # rgb
173+
for x in range(width):
174+
bmp[x, y] = pal.convert(
175+
(line[x * unit + 0] << 16)
176+
| (line[x * unit + 1] << 8)
177+
| line[x * unit + 2]
178+
)
115179
else:
116-
raise NotImplementedError("Filters not supported")
180+
raise ValueError("Unsupported color mode.")
181+
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565)
117182
return bmp, pal

0 commit comments

Comments
 (0)