Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some animated GIFS either delay or stop running #9254

Open
RetiredWizard opened this issue May 16, 2024 · 2 comments
Open

Some animated GIFS either delay or stop running #9254

RetiredWizard opened this issue May 16, 2024 · 2 comments

Comments

@RetiredWizard
Copy link

CircuitPython version

Adafruit CircuitPython 9.0.4 on 2024-04-16; LILYGO T-DECK with ESP32S3

Code/REPL

# Playgif 

import sys
import supervisor
import board
import gifio
import bitmaptools
import displayio
import time

if 'DISPLAY' in dir(board):
    display = board.DISPLAY
else:
    try:
        import framebufferio
        import dotclockframebuffer
    except:
        import adafruit_ili9341

    displayio.release_displays()

    if 'TFT_PINS' in dir(board):
        sWdth = board.TFT_TIMINGS['width']
        if sWdth == 1024 and "TFT_TIMINGS1024" in dir(board):
            disp_bus=dotclockframebuffer.DotClockFramebuffer(**board.TFT_PINS,**board.TFT_TIMINGS1024)
        else:
            disp_bus=dotclockframebuffer.DotClockFramebuffer(**board.TFT_PINS,**board.TFT_TIMINGS)
        display=framebufferio.FramebufferDisplay(disp_bus)
    else:
        if 'SPI' in dir(board):
            spi = board.SPI()
        else:
            spi = busio.SPI(clock=board.SCK,MOSI=board.MOSI,MISO=board.MISO)
        disp_bus=displayio.FourWire(spi,command=board.D10,chip_select=board.D9, \
            reset=board.D6)
        display=adafruit_ili9341.ILI9341(disp_bus,width=320,height=240)

splash = displayio.Group()

fname = input("Enter filename:")
input('Press "Enter" to continue, press "q" to quit')

odgcc = gifio.OnDiskGif(fname)
with odgcc as odg:
    colorspace = displayio.Colorspace.RGB565_SWAPPED
    scalefactor = display.width / odg.width
    if display.height/odg.height < scalefactor:
        scalefactor = display.height/odg.height

    if scalefactor < 1:
        print(f'scalefactor: {scalefactor}')
        bitframe = displayio.Bitmap(display.width,display.height,2**odg.bitmap.bits_per_value)
        bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor)
        facecc = displayio.TileGrid(bitframe, \
            pixel_shader=displayio.ColorConverter(input_colorspace=colorspace))
    else:
        facecc = displayio.TileGrid(odg.bitmap, \
            pixel_shader=displayio.ColorConverter(input_colorspace=colorspace))

    splash.append(facecc)

    display.root_group = splash

    start = 0
    next_delay = -1
    cmnd = ""
    # Display repeatedly.
    while cmnd.upper() != "Q":

        if supervisor.runtime.serial_bytes_available:
            cmnd = sys.stdin.read(1)
            print(cmnd, end="", sep="")
            if cmnd in "qQ":
                break
        while time.monotonic() > start and next_delay > time.monotonic()-start:
            pass
        next_delay = odg.next_frame()
        start = time.monotonic()
        if next_delay > 0:
            if scalefactor < 1:
                bitmaptools.rotozoom(bitframe,odg.bitmap,scale=scalefactor)

splash.pop()
odgcc = None
facecc.bitmap.deinit()
facecc = None
if scalefactor < 1:
    bitframe.deinit()
    bitframe = None
display.root_group = displayio.CIRCUITPYTHON_TERMINAL

Behavior

I have run across some animated gifs that either have a noticeable pause at the end of the animation before restarting or just run a single time when displayed by CircuitPython. In both cases the gifs play normally on my desktop computer.

I haven't been able to construct an animated gif that behaves this way and in fact, if I load the gifs in question into GIMP and re-export them they no longer have the issue on CircuitPython, which is obviously a simple workaround.

Here is an example:
classic40

Description

I originally thought the issue was that there were extra frames at the end of the animation that were flagged as unused/inactive and CircuitPython wasn't properly discarding so I looked at OnDiskGif.c to see if I could trigger the restart of the animation, before figuring that out I did try simply restarting the animation on a failed frame display and that seemed to fix the broken gifs. The change I made is:

-    if ((result >= 0) && (setDirty)) {
+   if (result > 0) {
+       if (setDirty) {
+            displayio_area_t dirty_area = {
+                .x1 = 0,
+                .y1 = 0,
+                .x2 = self->bitmap->width,
+                .y2 = self->bitmap->height,
+            };

+            displayio_bitmap_set_dirty_area(self->bitmap, &dirty_area);
+        }
+    } else {
+        GIF_reset(&self->gif);
+        result = GIF_playFrame(&self->gif, &nextDelay, self);
+        if (result > 0) {
+            if (setDirty) {
                displayio_area_t dirty_area = {
                    .x1 = 0,
                    .y1 = 0,
                    .x2 = self->bitmap->width,
                    .y2 = self->bitmap->height,
                };

Additional information

Now that I've discovered that the gifs can be easily repaired I'm not really concerned about this issue but I figured someone that understood more about the OnDiskGif code might see an obvious error in the logic that was accounting for the issue. If not, this issue can certainly be closed as the workaround is an easy fix.

@gamblor21
Copy link
Member

I don't have time to set this up and test it but just for background GIF_playFrame will return 1 for good more frames, 0 for good no more frames, or -1 for an error.

But quickly looking I'm not 100% sure the code will work exactly or you may get 0 at times.

Based on the fact you could re-save the GIF and that version works maybe a type of GIF the library does not handle and poor error handling in OnDiskGif (oops). Exposing GIF_Reset may also have some value in case of errors.

@RetiredWizard
Copy link
Author

Thanks! This info will be helpful when I get a chance to take another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants