11import time
22import array
3- import digitalio
43import struct
54try :
65 import zlib
@@ -155,8 +154,6 @@ def __init__(self, filename):
155154 self .colors = 0
156155
157156 def read_header (self ):
158- """Read the file's header information."""
159-
160157 if self .colors :
161158 return
162159 with open (self .filename , 'rb' ) as f :
@@ -168,10 +165,9 @@ def read_header(self):
168165 f .seek (46 )
169166 self .colors = int .from_bytes (f .read (4 ), 'little' )
170167
171- def read_palette (self ):
172- """Read the color palette information."""
173-
174- palette = array .array ('H' , (0 for i in range (16 )))
168+ def read_palette (self , palette = None ):
169+ if palette is None :
170+ palette = array .array ('H' , (0 for i in range (16 )))
175171 with open (self .filename , 'rb' ) as f :
176172 f .seek (self .data - self .colors * 4 )
177173 for color in range (self .colors ):
@@ -181,7 +177,6 @@ def read_palette(self):
181177 return palette
182178
183179 def read_data (self , buffer = None ):
184- """Read the image data."""
185180 line_size = self .width >> 1
186181 if buffer is None :
187182 buffer = bytearray (line_size * self .height )
@@ -196,143 +191,6 @@ def read_data(self, buffer=None):
196191 return buffer
197192
198193
199- def read_blockstream (f ):
200- while True :
201- size = f .read (1 )[0 ]
202- if size == 0 :
203- break
204- for i in range (size ):
205- yield f .read (1 )[0 ]
206-
207-
208- class EndOfData (Exception ):
209- pass
210-
211-
212- class LZWDict :
213- def __init__ (self , code_size ):
214- self .code_size = code_size
215- self .clear_code = 1 << code_size
216- self .end_code = self .clear_code + 1
217- self .codes = []
218- self .clear ()
219-
220- def clear (self ):
221- self .last = b''
222- self .code_len = self .code_size + 1
223- self .codes [:] = []
224-
225- def decode (self , code ):
226- if code == self .clear_code :
227- self .clear ()
228- return b''
229- elif code == self .end_code :
230- raise EndOfData ()
231- elif code < self .clear_code :
232- value = bytes ([code ])
233- elif code <= len (self .codes ) + self .end_code :
234- value = self .codes [code - self .end_code - 1 ]
235- else :
236- value = self .last + self .last [0 :1 ]
237- if self .last :
238- self .codes .append (self .last + value [0 :1 ])
239- if (len (self .codes ) + self .end_code + 1 >= 1 << self .code_len and
240- self .code_len < 12 ):
241- self .code_len += 1
242- self .last = value
243- return value
244-
245-
246- def lzw_decode (data , code_size ):
247- dictionary = LZWDict (code_size )
248- bit = 0
249- try :
250- byte = next (data )
251- try :
252- while True :
253- code = 0
254- for i in range (dictionary .code_len ):
255- code |= ((byte >> bit ) & 0x01 ) << i
256- bit += 1
257- if bit >= 8 :
258- bit = 0
259- byte = next (data )
260- yield dictionary .decode (code )
261- except EndOfData :
262- while True :
263- next (data )
264- except StopIteration :
265- return
266-
267-
268- class GIF16 :
269- """Read 16-color GIF files."""
270-
271- def __init__ (self , filename ):
272- self .filename = filename
273-
274- def read_header (self ):
275- with open (self .filename , 'rb' ) as f :
276- header = f .read (6 )
277- if header not in {b'GIF87a' , b'GIF89a' }:
278- raise ValueError ("Not GIF file" )
279- self .width , self .height , flags , self .background , self .aspect = (
280- struct .unpack ('<HHBBB' , f .read (7 )))
281- self .palette_size = 1 << ((flags & 0x07 ) + 1 )
282- if not flags & 0x80 :
283- raise NotImplementedError ()
284- if self .palette_size > 16 :
285- raise ValueError ("Too many colors (%d/16)." % self .palette_size )
286-
287- def read_palette (self ):
288- palette = array .array ('H' , (0 for i in range (16 )))
289- with open (self .filename , 'rb' ) as f :
290- f .seek (13 )
291- for color in range (self .palette_size ):
292- buffer = f .read (3 )
293- c = color565 (buffer [0 ], buffer [1 ], buffer [2 ])
294- palette [color ] = ((c << 8 ) | (c >> 8 )) & 0xffff
295- return palette
296-
297- def read_data (self , buffer = None ):
298- line_size = (self .width + 1 ) >> 1
299- if buffer is None :
300- buffer = bytearray (line_size * self .height )
301- with open (self .filename , 'rb' ) as f :
302- f .seek (13 + self .palette_size * 3 )
303- while True : # skip to first frame
304- block_type = f .read (1 )[0 ]
305- if block_type == 0x2c :
306- break
307- elif block_type == 0x21 : # skip extension
308- extension_type = f .read (1 )[0 ]
309- while True :
310- size = f .read (1 )[0 ]
311- if size == 0 :
312- break
313- f .seek (1 , size )
314- elif block_type == 0x3b :
315- raise NotImplementedError ()
316- x , y , w , h , flags = struct .unpack ('<HHHHB' , f .read (9 ))
317- if (flags & 0x80 or flags & 0x40 or
318- w != self .width or h != self .height or x != 0 or y != 0 ):
319- raise NotImplementedError ()
320- min_code_size = f .read (1 )[0 ]
321- x = 0
322- y = 0
323- for decoded in lzw_decode (read_blockstream (f ), min_code_size ):
324- for pixel in decoded :
325- if x & 0x01 :
326- buffer [(x >> 1 ) + y * line_size ] |= pixel
327- else :
328- buffer [(x >> 1 ) + y * line_size ] = pixel << 4
329- x += 1
330- if (x >= self .width ):
331- x = 0
332- y += 1
333- return buffer
334-
335-
336194class PNG16 :
337195 """Read 16-color PNG files."""
338196
@@ -564,6 +422,7 @@ def __init__(self, width, height, font=None, palette=None, buffer=None):
564422
565423 def char (self , x , y , c = None , hightlight = False ):
566424 """Get or set the character at the given location."""
425+
567426 if not 0 <= x < self .width or not 0 <= y < self .height :
568427 return
569428 if c is None :
@@ -575,6 +434,7 @@ def char(self, x, y, c=None, hightlight=False):
575434
576435 def move (self , x , y , z = None ):
577436 """Shift the whole layer respective to the screen."""
437+
578438 self .x = x
579439 self .y = y
580440 if z is not None :
@@ -583,6 +443,7 @@ def move(self, x, y, z=None):
583443
584444 def cursor (self , x = None , y = None ):
585445 """Move the text cursor to the specified row and column."""
446+
586447 if y is not None :
587448 self .row = min (max (0 , y ), self .width - 1 )
588449 if x is not None :
@@ -593,6 +454,7 @@ def text(self, text, hightlight=False):
593454 Display text starting at the current cursor location.
594455 Return the dimensions of the rendered text.
595456 """
457+
596458 longest = 0
597459 tallest = 0
598460 for c in text :
@@ -612,6 +474,7 @@ def text(self, text, hightlight=False):
612474
613475 def clear (self ):
614476 """Clear all text from the layer."""
477+
615478 for i in range (self .width * self .height ):
616479 self .buffer [i ] = 0
617480
@@ -647,6 +510,7 @@ def __init__(self, display, fps=6, scale=None):
647510
648511 def tick (self ):
649512 """Wait for the start of the next frame."""
513+
650514 self .last_tick += self .tick_delay
651515 wait = max (0 , self .last_tick - time .monotonic ())
652516 if wait :
@@ -656,6 +520,7 @@ def tick(self):
656520
657521 def render_block (self , x0 = None , y0 = None , x1 = None , y1 = None ):
658522 """Update a rectangle of the screen."""
523+
659524 if x0 is None :
660525 x0 = self .vx
661526 if y0 is None :
@@ -676,6 +541,7 @@ def render_block(self, x0=None, y0=None, x1=None, y1=None):
676541
677542 def render_sprites (self , sprites ):
678543 """Update the spots taken by all the sprites in the list."""
544+
679545 layers = [l .layer for l in self .layers ]
680546 for sprite in sprites :
681547 x = int (sprite .x ) - self .vx
0 commit comments