def IntermezzoInvaders(previous_frame: Frame, next_frame: Frame): """ Show Invaders from the top to the bottom switching programs. """ config = Config() prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() frame_delay = config['INVADERS_FRAME_DELAY'] height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] size = height * width invader_height = int(len(_invaders(0)) / width) for step in range(height + invader_height + 4): # lets go from top to bottom img = bytearray().join([ nxt, bytearray(width), # Empty. bytearray(width), # Empty. _invaders(step), bytearray(width), # Empty. bytearray(width), # Empty. prv ]) if step == 0: frame_data = img[-1 * size - (step * width):] else: frame_data = img[-1 * size - (step * width):-(step * width)] seq.add_frame(Frame(frame_data, frame_delay)) return seq
def AnimateVerticalScroll(image: bytearray, line_duration: int) -> list: """ I let the content of a longer image scroll vertically up. :param image: The image that there is to scroll. :type image: bytearray :param line_duration: The duration in ms that each line should be shown. :type line_duration: int :return: List of frames that make up the scrolling motion. :rtype: list """ config = Config() display_width = config['DISPLAY_WIDTH'] animate_duration = config['TYPESETTER_ANIMATE_VERTICAL_SCROLL_DELAY'] nr_of_lines = len(image) / display_width # nr of lines does the whole image has. nr_of_scroll = int(nr_of_lines - config['DISPLAY_HEIGHT']) # number of lines there are to scroll f_start = 0 f_end = config['DISPLAY_SIZE'] frames = [] for nr in range(nr_of_scroll): if nr % 8 == 0: # On a full line, show for longer. duration = line_duration else: duration = animate_duration frames.append(Frame(image[f_start:f_end], duration=duration)) f_start += display_width f_end += display_width frames.append(Frame(image[-config['DISPLAY_SIZE']:], duration=line_duration)) return frames
def test_pacman(self): config = Config() image_size = config.get('DISPLAY_SIZE') prev_frame = Frame(bytearray(b'0' * image_size), 1) next_frame = Frame(bytearray(b'1' * image_size), 1) seq = IntermezzoPacman(prev_frame, next_frame) verify_length(seq, image_size)
def test_fram_length(self): config = Config() image_size = config.get('DISPLAY_SIZE') prev_frame = Frame(bytearray([0x66]*image_size), 100) next_frame = Frame(bytearray([0x66]*image_size), 100) seq = IntermezzoInvaders(prev_frame, next_frame) verify_length(seq, image_size)
def test_intermezzo_wipe(self): catalog = Catalog() catalog.add_intermezzo(IntermezzoWipe) self._create_and_add_sequence(catalog, 'second', [Frame(bytearray(b'\x00' * 3456), 10)]) self._create_and_add_sequence(catalog, 'first', [Frame(bytearray(b'\xff' * 3456), 10)]) f_iter = catalog.frames_iter() res = next(f_iter) assert 0xff == res.raw()[0] res2 = next(f_iter) assert 0x0 == res2.raw()[0]
def test_AnimateStill(self, sched): seq = FrameSequence() img_data = bytearray(Config().get('DISPLAY_SIZE')) seq.add_frame(Frame(img_data, 2000)) animated_seq = AnimateStill(seq[0]) assert Config().get('DISPLAY_HEIGHT') == len(animated_seq) assert sum([frame.duration for frame in animated_seq.frames]) == 2000 seq.add_frame(Frame(img_data, None)) animated_seq = AnimateStill(seq[1]) assert Config()['DISPLAY_DEFAULT_DELAY'] == sum( [frame.duration for frame in animated_seq.frames])
def test_mark_program_progress(self): catalog = Catalog() seq = self._create_and_add_sequence( catalog, 'first', [Frame(bytearray(b'\xff' * 3456), 10)]) gen = catalog.mark_program_progress(seq, 0, 5) next(gen) gen = catalog.mark_program_progress(seq, 4, 5) next(gen)
def typeset_alert(self, topic: str, msg: TextAlertLayout) -> FrameSequence: assert topic.split('/')[-1] == "spacealert" text = msg.text who = msg.who fs = FrameSequence() char_width = self._char_display_width() fs.program = msg.program alert = self.typeset_1line("Space Alert!", 20) alert_neg = bytearray([(~x & 0xff) for x in alert]) fs.add_frame(Frame(alert, duration=200)) fs.add_frame(Frame(alert_neg, duration=200)) fs.add_frame(Frame(alert, duration=200)) fs.add_frame(Frame(alert_neg, duration=200)) if text: three_line_msg = TextTripleLinesLayout() three_line_msg.lines = ["From %s" % who, text[:char_width], text[char_width:]] three_line_msg.duration = 2000 self.typeset_3lines(fs, three_line_msg) fs.prio = "alert" return fs
def IntermezzoWipe(previous_frame: Frame, next_frame: Frame): config = Config() wipe_frame_delay = config['INTERMEZZO_WIPE_FRAME_DELAY'] wipe_frame_step_size = config['INTERMEZZO_WIPE_FRAME_STEP_SIZE'] prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] sep = bytearray([0x00, 0x00, 0x40, 0x60, 0x80, 0x80, 0xff, 0x00]) sep_len = len(sep) for step in range(wipe_frame_step_size, width - wipe_frame_step_size - sep_len, wipe_frame_step_size): img_data = bytearray() for row in range(0, height): start = width * row img_data.extend(nxt[start:start + step] + sep + prv[start + step + sep_len:start + width]) seq.add_frame(Frame(img_data, wipe_frame_delay)) return seq
def gif(): f = request.files['f'] program = request.form['program'] try: im = Image.open(f) except OSError: raise UnsupportedMediaType() sequence = FrameSequence() for frame_raw in ImageSequence.Iterator(im): image_data, duration = process_frame(frame_raw) sequence.add_frame(Frame(image_data, duration)) payload = send_image(sequence, program) return Response(payload, mimetype='application/json')
def AnimateStill(still: Frame): """ I take a frame and create a sequence where the duration of the frame is visible. :param still: The image to animate. :type still: Frame :return: The sequence of frames with the time animation. :rtype: FrameSequence """ seq = FrameSequence() width, height = Config().get('DISPLAY_WIDTH'), Config().get('DISPLAY_HEIGHT') seq_duration = still.duration if not seq_duration: seq_duration = Config()['DISPLAY_DEFAULT_DELAY'] steps_ms = int(seq_duration / height) still_img = still.raw() still.duration = steps_ms for nr in range(height): frame = bytearray(still_img) frame[width*nr-1] = 0xff seq.add_frame(Frame(frame, steps_ms)) seq[-1].duration += seq_duration - seq.duration # Add the steps_ms missing because of the division. return seq
def IntermezzoPacman(previous_frame: Frame, next_frame: Frame): config = Config() frame_move = config['PACMAN_MOVE'] frame_delay = config['PACMAN_DELAY'] prv = previous_frame.raw() nxt = next_frame.raw() seq = FrameSequence() height = config['DISPLAY_HEIGHT'] width = config['DISPLAY_WIDTH'] spacer = bytearray([0x00, 0x00, 0x00]) pacmans = [Pacman1, Pacman2] i = 0 for step in range(0, width + len(spacer) + len(Pacman1[0]), frame_move): i += 1 img_data = bytearray() for row_nr in range(height): prv_row = prv[row_nr * width:(row_nr + 1) * width] nxt_row = nxt[row_nr * width:(row_nr + 1) * width] row = prv_row + pacmans[i % 2][row_nr] + spacer + nxt_row img_data.extend(row[step:step + width]) seq.add_frame(Frame(img_data, frame_delay)) return seq
def _create_and_add_sequence(self, catalog, program_name, sequence_content, valid_time=None): seq = FrameSequence() # Frame(bytearray(b'Bar') * int(3456/3) seq.program = program_name if isinstance(sequence_content[0], Frame): seq.frames = sequence_content else: seq.frames = [ Frame(bytearray(f.encode() * int(3456 / len(f))), 10) for f in sequence_content ] seq.valid_time = valid_time if valid_time is not None else Config( )['PROGRAM_RETIREMENT_AGE'] catalog.add_program(program_name, seq) return seq
def typeset_3lines(self, seq: FrameSequence, msg: TextTripleLinesLayout)-> FrameSequence: font = FontMapping[msg.size] lines = msg.lines if not lines: return seq # lines = lines[0:3] # Limit for now. image = bytearray() for line in lines: # off all the lines MarkupLine(image, line, font) duration = msg.duration if msg.duration is not None else self.config['DISPLAY_DEFAULT_DELAY'] if len(lines) <= 3: if len(lines) % 3 != 0: # Append empty lines if not all lines are complete. missing_lines = 3 - len(lines) % 3 for c in range(missing_lines): MarkupLine(image, "", font) seq.add_frame(Frame(image, duration=duration)) else: line_duration = msg.line_duration if msg.line_duration is not None else self.config['DISPLAY_LINE_DURATION'] seq.extend(AnimateVerticalScroll(image, line_duration)) return seq
def publishProgress(self): def _logFailure(failure): self.log.debug("reported {message}", message=failure.getErrorMessage()) return failure def _logAll(*args): self.log.debug("all publishing complete args={args!r}", args=args) # self.log.debug(" >< Starting one round of publishing >< ") now = datetime.now() seq = FrameSequence() image = bytearray() image.extend(self._create_graph_line(self._create_day_progress(now))) image.extend(self._create_graph_line(self._create_month_progress(now))) image.extend(self._create_graph_line(self._create_year_progress(now))) frame = Frame(image, duration=self.config['PROGRESS_DISPLAY_DURATION']) seq.add_frame(frame) seq.program = "progress" d = self.publish(topic=LEDSLIE_TOPIC_SEQUENCES_PROGRAMS[:-1] + seq.program, message=seq) d.addCallbacks(_logAll, _logFailure) return d