/ 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")