Skip to content

Commit 088d68f

Browse files
committed
Add support for PNG images
1 parent 44dfe8d commit 088d68f

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

stage.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import array
33
import digitalio
44
import struct
5+
try:
6+
import zlib
7+
except ImportError:
8+
pass
59

610
import _stage
711

@@ -329,6 +333,62 @@ def read_data(self, buffer=None):
329333
return buffer
330334

331335

336+
class PNG16:
337+
"""Read 16-color PNG files."""
338+
339+
def __init__(self, filename):
340+
self.filename = filename
341+
342+
def read_header(self):
343+
with open(self.filename, 'rb') as f:
344+
magic = f.read(8)
345+
assert magic == b'\x89PNG\r\n\x1a\n'
346+
(
347+
size, chunk, self.width, self.height, self.depth, self.mode,
348+
self.compression, self.filters, self.interlaced, crc
349+
) = struct.unpack(">I4sIIBBBBBI", f.read(25))
350+
assert size == 13 # header length
351+
assert chunk == b'IHDR'
352+
if self.depth != 4 or self.mode != 3 or self.interlaced != 0:
353+
raise ValueError("16-color non-interaced PNG expected")
354+
355+
def read_palette(self, palette=None):
356+
if palette is None:
357+
palette = array.array('H', (0 for i in range(16)))
358+
with open(self.filename, 'rb') as f:
359+
f.seek(8 + 25)
360+
size, chunk = struct.unpack(">I4s", f.read(8))
361+
assert chunk == b'PLTE'
362+
for color in range(size // 3):
363+
c = color565(*struct.unpack("BBB", f.read(3)))
364+
palette[color] = ((c << 8) | (c >> 8)) & 0xffff
365+
return palette
366+
367+
def read_data(self, buffer=None):
368+
data = bytearray()
369+
with open(self.filename, 'rb') as f:
370+
f.seek(8 + 25)
371+
while True:
372+
size, chunk = struct.unpack(">I4s", f.read(8))
373+
if chunk == b'IEND':
374+
break
375+
elif chunk != b'IDAT':
376+
f.seek(size + 4, 1)
377+
continue
378+
data.extend(f.read(size))
379+
f.seek(4, 1) # skip CRC
380+
data = zlib.decompress(data)
381+
line_size = self.width >> 1
382+
if buffer is None:
383+
buffer = bytearray(line_size * self.height)
384+
for line in range(self.height):
385+
a = line * line_size
386+
b = line * (line_size + 1)
387+
assert data[b] == 0 # no filter
388+
buffer[a:a + line_size] = data[b + 1:b + 1 + line_size]
389+
return buffer
390+
391+
332392
class Bank:
333393
"""
334394
Store graphics for the tiles and sprites.
@@ -355,6 +415,8 @@ def from_image(cls, filename):
355415
image = GIF16(filename)
356416
elif filename.lower().endswith(".bmp"):
357417
image = BMP16(filename)
418+
elif filename.lower().endswith(".png"):
419+
image = PNG16(filename)
358420
else:
359421
raise ValueError("Unsupported format")
360422
image.read_header()

0 commit comments

Comments
 (0)