22import array
33import digitalio
44import struct
5+ try :
6+ import zlib
7+ except ImportError :
8+ pass
59
610import _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'\x89 PNG\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+
332392class 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