rgb_display_pillow_animated_gif.py
1 """ 2 Example to extract the frames and other parameters from an animated gif 3 and then run the animation on the display. 4 5 Usage: 6 python3 rgb_display_pillow_animated_gif.py 7 8 This example is for use on (Linux) computers that are using CPython with 9 Adafruit Blinka to support CircuitPython libraries. CircuitPython does 10 not support PIL/pillow (python imaging library)! 11 12 Author(s): Melissa LeBlanc-Williams for Adafruit Industries 13 """ 14 import os 15 import time 16 import digitalio 17 import board 18 from PIL import Image, ImageOps 19 import numpy # pylint: disable=unused-import 20 import adafruit_rgb_display.ili9341 as ili9341 21 import adafruit_rgb_display.st7789 as st7789 # pylint: disable=unused-import 22 import adafruit_rgb_display.hx8357 as hx8357 # pylint: disable=unused-import 23 import adafruit_rgb_display.st7735 as st7735 # pylint: disable=unused-import 24 import adafruit_rgb_display.ssd1351 as ssd1351 # pylint: disable=unused-import 25 import adafruit_rgb_display.ssd1331 as ssd1331 # pylint: disable=unused-import 26 27 # Change to match your display 28 BUTTON_NEXT = board.D17 29 BUTTON_PREVIOUS = board.D22 30 31 # Configuration for CS and DC pins (these are PiTFT defaults): 32 cs_pin = digitalio.DigitalInOut(board.CE0) 33 dc_pin = digitalio.DigitalInOut(board.D25) 34 35 # Set this to None on the Mini PiTFT 36 reset_pin = digitalio.DigitalInOut(board.D24) 37 38 39 def init_button(pin): 40 button = digitalio.DigitalInOut(pin) 41 button.switch_to_input() 42 button.pull = digitalio.Pull.UP 43 return button 44 45 46 # pylint: disable=too-few-public-methods 47 class Frame: 48 def __init__(self, duration=0): 49 self.duration = duration 50 self.image = None 51 52 53 # pylint: enable=too-few-public-methods 54 55 56 class AnimatedGif: 57 def __init__(self, display, width=None, height=None, folder=None): 58 self._frame_count = 0 59 self._loop = 0 60 self._index = 0 61 self._duration = 0 62 self._gif_files = [] 63 self._frames = [] 64 65 if width is not None: 66 self._width = width 67 else: 68 self._width = display.width 69 if height is not None: 70 self._height = height 71 else: 72 self._height = display.height 73 self.display = display 74 self.advance_button = init_button(BUTTON_NEXT) 75 self.back_button = init_button(BUTTON_PREVIOUS) 76 if folder is not None: 77 self.load_files(folder) 78 self.run() 79 80 def advance(self): 81 self._index = (self._index + 1) % len(self._gif_files) 82 83 def back(self): 84 self._index = (self._index - 1 + len(self._gif_files)) % len(self._gif_files) 85 86 def load_files(self, folder): 87 gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")] 88 for gif_file in gif_files: 89 image = Image.open(gif_file) 90 # Only add animated Gifs 91 if image.is_animated: 92 self._gif_files.append(gif_file) 93 94 print("Found", self._gif_files) 95 if not self._gif_files: 96 print("No Gif files found in current folder") 97 exit() # pylint: disable=consider-using-sys-exit 98 99 def preload(self): 100 image = Image.open(self._gif_files[self._index]) 101 print("Loading {}...".format(self._gif_files[self._index])) 102 if "duration" in image.info: 103 self._duration = image.info["duration"] 104 else: 105 self._duration = 0 106 if "loop" in image.info: 107 self._loop = image.info["loop"] 108 else: 109 self._loop = 1 110 self._frame_count = image.n_frames 111 self._frames.clear() 112 for frame in range(self._frame_count): 113 image.seek(frame) 114 # Create blank image for drawing. 115 # Make sure to create image with mode 'RGB' for full color. 116 frame_object = Frame(duration=self._duration) 117 if "duration" in image.info: 118 frame_object.duration = image.info["duration"] 119 frame_object.image = ImageOps.pad( # pylint: disable=no-member 120 image.convert("RGB"), 121 (self._width, self._height), 122 method=Image.NEAREST, 123 color=(0, 0, 0), 124 centering=(0.5, 0.5), 125 ) 126 self._frames.append(frame_object) 127 128 def play(self): 129 self.preload() 130 131 # Check if we have loaded any files first 132 if not self._gif_files: 133 print("There are no Gif Images loaded to Play") 134 return False 135 while True: 136 for frame_object in self._frames: 137 start_time = time.monotonic() 138 self.display.image(frame_object.image) 139 if not self.advance_button.value: 140 self.advance() 141 return False 142 if not self.back_button.value: 143 self.back() 144 return False 145 while time.monotonic() < (start_time + frame_object.duration / 1000): 146 pass 147 148 if self._loop == 1: 149 return True 150 if self._loop > 0: 151 self._loop -= 1 152 153 def run(self): 154 while True: 155 auto_advance = self.play() 156 if auto_advance: 157 self.advance() 158 159 160 # Config for display baudrate (default max is 64mhz): 161 BAUDRATE = 64000000 162 163 # Setup SPI bus using hardware SPI: 164 spi = board.SPI() 165 166 # pylint: disable=line-too-long 167 # Create the display: 168 # disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789 169 # disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789 170 # disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789 171 # disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357 172 # disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R 173 # disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R 174 # disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R 175 # disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351 176 # disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351 177 # disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331 178 disp = ili9341.ILI9341( 179 spi, 180 rotation=90, # 2.2", 2.4", 2.8", 3.2" ILI9341 181 cs=cs_pin, 182 dc=dc_pin, 183 rst=reset_pin, 184 baudrate=BAUDRATE, 185 ) 186 # pylint: enable=line-too-long 187 188 if disp.rotation % 180 == 90: 189 disp_height = disp.width # we swap height/width to rotate it to landscape! 190 disp_width = disp.height 191 else: 192 disp_width = disp.width 193 disp_height = disp.height 194 195 gif_player = AnimatedGif(disp, width=disp_width, height=disp_height, folder=".")