Skip to content

Commit

Permalink
Add support for RGB, RGBA and Grayscale PNGs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
deshipu committed Aug 3, 2024
1 parent 7a68dcd commit 056568e
Showing 1 changed file with 73 additions and 8 deletions.
81 changes: 73 additions & 8 deletions adafruit_imageload/png.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,80 @@ def load(
file.seek(size, 1) # skip unknown chunks
file.seek(4, 1) # skip CRC
data_bytes = zlib.decompress(data)
bmp = bitmap(width, height, 1 << depth)
scanline = (width * depth + 7) // 8
mem = memoryview(bmp)
unit = (1, 0, 3, 1, 2, 0, 4)[mode]
scanline = (width * depth * unit + 7) // 8
colors = 1 << (depth * unit)
if mode == 3: # indexed
bmp = bitmap(width, height, colors)
mem = memoryview(bmp)
for y in range(height):
dst = y * scanline
src = y * (scanline + 1) + 1
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
return bmp, pal
# RGB, RGBA or Grayscale
import displayio

Check failure on line 118 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Import outside toplevel (displayio)

if depth != 8:
raise ValueError("Must be 8bit depth.")
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB888)
bmp = bitmap(width, height, 65536)
prev = bytearray(scanline)
line = bytearray(scanline)
for y in range(height):
dst = y * scanline
src = y * (scanline + 1) + 1
filter_ = data_bytes[src - 1]
src = y * (scanline + 1)
filter_ = data_bytes[src]
src += 1
if filter_ == 0:
mem[dst : dst + scanline] = data_bytes[src : src + scanline]
line[0:scanline] = data_bytes[src : src + scanline]
elif filter_ == 1: # sub
for i in range(scanline):
a = line[i - unit] if i >= unit else 0

Check failure on line 134 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "a" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
line[i] = (data_bytes[src] + a) & 0xFF
src += 1
elif filter_ == 2: # up
for i in range(scanline):
b = prev[i]
line[i] = (data_bytes[src] + b) & 0xFF
src += 1
elif filter_ == 3: # average
for i in range(scanline):
a = line[i - unit] if i >= unit else 0

Check failure on line 144 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "a" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
b = prev[i]
line[i] = (data_bytes[src] + ((a + b) >> 1)) & 0xFF
src += 1
elif filter_ == 4: # paeth
for i in range(scanline):
a = line[i - unit] if i >= unit else 0

Check failure on line 150 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "a" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
b = prev[i]
c = prev[i - unit] if i >= unit else 0

Check failure on line 152 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "c" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
p = a + b - c

Check failure on line 153 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "p" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
pa = abs(p - a)

Check failure on line 154 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "pa" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
pb = abs(p - b)

Check failure on line 155 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "pb" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
pc = abs(p - c)

Check failure on line 156 in adafruit_imageload/png.py

View workflow job for this annotation

GitHub Actions / test

Variable name "pc" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
if pa <= pb and pa <= pc:
p = a
elif pb <= pc:
p = b
else:
p = c
line[i] = (data_bytes[src] + p) & 0xFF
src += 1
else:
raise ValueError("Wrong filter.")
prev, line = line, prev
if mode in (0, 4): # grayscale
for x in range(width):
c = line[x * unit]
bmp[x, y] = pal.convert((c << 16) | (c << 8) | c)
elif mode in {2, 6}: # rgb
for x in range(width):
bmp[x, y] = pal.convert(
(line[x * unit + 0] << 16)
| (line[x * unit + 1] << 8)
| line[x * unit + 2]
)
else:
raise NotImplementedError("Filters not supported")
raise ValueError("Unsupported color mode.")
pal = displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565)
return bmp, pal

0 comments on commit 056568e

Please sign in to comment.