/ Pi_SSD_Media_Server / gif-player.py
gif-player.py
1 # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 # SPDX-License-Identifier: MIT 3 4 import os 5 import time 6 import digitalio 7 import board 8 from PIL import Image, ImageOps 9 import numpy # pylint: disable=unused-import 10 from adafruit_rgb_display import st7789 # pylint: disable=unused-import 11 12 # Change to match your display 13 BUTTON_NEXT = board.D17 14 BUTTON_PREVIOUS = board.D22 15 16 # Configuration for CS and DC pins (these are PiTFT defaults): 17 cs_pin = digitalio.DigitalInOut(board.CE0) 18 dc_pin = digitalio.DigitalInOut(board.D25) 19 reset_pin = digitalio.DigitalInOut(board.D24) 20 21 def init_button(pin): 22 button = digitalio.DigitalInOut(pin) 23 button.switch_to_input() 24 button.pull = digitalio.Pull.UP 25 return button 26 27 # pylint: disable=too-few-public-methods 28 class Frame: 29 def __init__(self, duration=0): 30 self.duration = duration 31 self.image = None 32 33 # pylint: enable=too-few-public-methods 34 35 class AnimatedGif: 36 def __init__(self, display, width=None, height=None, folder=None): 37 self._frame_count = 0 38 self._loop = 0 39 self._index = 0 40 self._duration = 0 41 self._gif_files = [] 42 self._frames = [] 43 44 if width is not None: 45 self._width = width 46 else: 47 self._width = display.width 48 if height is not None: 49 self._height = height 50 else: 51 self._height = display.height 52 self.display = display 53 self.advance_button = init_button(BUTTON_NEXT) 54 self.back_button = init_button(BUTTON_PREVIOUS) 55 if folder is not None: 56 self.load_files(folder) 57 self.run() 58 59 def advance(self): 60 self._index = (self._index + 1) % len(self._gif_files) 61 62 def back(self): 63 self._index = (self._index - 1 + len(self._gif_files)) % len(self._gif_files) 64 65 def load_files(self, folder): 66 gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")] 67 for gif_file in gif_files: 68 gif_file = os.path.join(folder, gif_file) 69 image = Image.open(gif_file) 70 # Only add animated Gifs 71 if image.is_animated: 72 self._gif_files.append(gif_file) 73 74 print("Found", self._gif_files) 75 if not self._gif_files: 76 print("No Gif files found in current folder") 77 exit() # pylint: disable=consider-using-sys-exit 78 79 def preload(self): 80 image = Image.open(self._gif_files[self._index]) 81 print("Loading {}...".format(self._gif_files[self._index])) 82 if "duration" in image.info: 83 self._duration = image.info["duration"] 84 else: 85 self._duration = 0 86 if "loop" in image.info: 87 self._loop = image.info["loop"] 88 else: 89 self._loop = 1 90 self._frame_count = image.n_frames 91 self._frames.clear() 92 for frame in range(self._frame_count): 93 image.seek(frame) 94 # Create blank image for drawing. 95 # Make sure to create image with mode 'RGB' for full color. 96 frame_object = Frame(duration=self._duration) 97 if "duration" in image.info: 98 frame_object.duration = image.info["duration"] 99 frame_object.image = ImageOps.pad( # pylint: disable=no-member 100 image.convert("RGB"), 101 (self._width, self._height), 102 method=Image.Resampling.NEAREST, 103 color=(0, 0, 0), 104 centering=(0.5, 0.5), 105 ) 106 self._frames.append(frame_object) 107 108 def play(self): 109 self.preload() 110 111 _prev_advance_btn_val = self.advance_button.value 112 _prev_back_btn_val = self.back_button.value 113 # Check if we have loaded any files first 114 if not self._gif_files: 115 print("There are no Gif Images loaded to Play") 116 return False 117 while True: 118 for frame_object in self._frames: 119 start_time = time.monotonic() 120 self.display.image(frame_object.image) 121 _cur_advance_btn_val = self.advance_button.value 122 _cur_back_btn_val = self.back_button.value 123 if not _cur_advance_btn_val and _prev_advance_btn_val: 124 self.advance() 125 return False 126 if not _cur_back_btn_val and _prev_back_btn_val: 127 self.back() 128 return False 129 130 _prev_back_btn_val = _cur_back_btn_val 131 _prev_advance_btn_val = _cur_advance_btn_val 132 while time.monotonic() < (start_time + frame_object.duration / 1000): 133 pass 134 135 if self._loop == 1: 136 return True 137 if self._loop > 0: 138 self._loop -= 1 139 140 def run(self): 141 while True: 142 auto_advance = self.play() 143 if auto_advance: 144 self.advance() 145 146 147 # Config for display baudrate (default max is 64mhz): 148 BAUDRATE = 64000000 149 150 # Setup SPI bus using hardware SPI: 151 spi = board.SPI() 152 153 disp = st7789.ST7789( 154 spi, 155 rotation=270, 156 width=170, 157 height=320, 158 x_offset=35, 159 cs=cs_pin, 160 dc=dc_pin, 161 rst=reset_pin, 162 baudrate=BAUDRATE, 163 ) 164 # pylint: enable=line-too-long 165 166 if disp.rotation % 180 == 90: 167 disp_height = disp.width # we swap height/width to rotate it to landscape! 168 disp_width = disp.height 169 else: 170 disp_width = disp.width 171 disp_height = disp.height 172 173 gif_player = AnimatedGif(disp, width=disp_width, height=disp_height, folder="gifs")