<div dir="ltr"><div>Hi all,</div><div><br></div><div>I am responsible for the re-implementation of GIFReadWriter in Pharo from a few years ago (<a href="https://github.com/pharo-project/pharo/pull/1666">see this PR</a>). Earlier this week I was able to successfully port most of that work to Cuis, so I thought I'd take a stab at Squeak too.</div><div>  <br></div><div>Unlike Pharo and Cuis, however, Squeak already has a class `AnimatedImageMorph`. I wanted to discuss my proposed changes here for this reason, prior to submitting any formal changes to the inbox.</div><div><br></div><div>So here's what I've done so far:</div><div><br></div><div>First, as with Pharo and Cuis, `GIFReadWriter` has been completely refactored to have a more clear and more complete implementation of the GIF format. This includes separating out the compression activity into two helper classes (`LzwGifEncoder` and `LzwGifDecoder`), as well as completely removing the existing `AnimatedGIFReadWriter` class. I have also added the `AnimatedImageFrame` class for composing GIF frame information that can be used by other classes -- both for writing and for displaying.</div><div><br></div><div>Second, I have refactored the existing `AnimatedImageMorph` in Squeak to better deal with displaying real-life animated GIFs. Prior to this, many GIFs would display incorrectly due to the nature of GIF frame compositing and disposal. All of the test GIFs I've tried so far (and there have been a lot) seem to be working well with this class.</div><div>  <br></div><div>Third, I have created an alternative class -- presumptuously named `BetterAnimatedImageMorph` -- that has some optimizations. The regular `AnimatedImageMorph` does the GIF frame/form compositing "on the fly," which results in a lot of extra computation each time the animation loops. The upshot here is that we cannot achieve real framerates for some GIFs -- they always play slower in Squeak than they would in a browser regardless  of how low one sets the stepTime/delay. `BetterAnimatedImageMorph` takes a different approach: it loops through all the GIFReadWriter's read-in frames and first creates a collection of fully composited Forms. It then simply changes the current form to be displayed at each step. In my tests this has resulted in much more performant playback, of which I have provided examples (see below).</div><div>  <br></div><div>One issue with `BetterAnimatedImageMorph` is that the original information about the constituent forms of the GIF is "lost". Attempting to write it back to a GIF file will necessarily result in a larger file than the original, because most animated GIFs are made of frames that are "diffed" in a sense. There are a couple of ways to deal with this though, such as caching the original frame data etc.</div><div>  <br></div><div>My concrete proposals, then, would be the following:</div><div>1. Remove AnimatedGIFReadWriter and replace GIFReadWriter with my updated version (along with helper classes)</div><div>2. Replace AnimatedImageMorph with my new BetterAnimatedImageMorph</div><div><br></div><div>If you've read this far and have the patience to try it out, I have my working code up online at <a href="https://gitlab.com/darth-cheney/squeak-animated-gif">https://gitlab.com/darth-cheney/squeak-animated-gif</a></div><div>  <br></div><div>Load this code in Squot and check it out.  Try some of the examples in the FunGIFExamples class (on the class side). In particular, any class method starting with `example` should be good fun.</div><div>  <br></div><div>For a live comparison of the performance between my `BetterAnimatedImageMorph` and the current `AnimatedImageMorph`, do `FunGIFExamples animatedImageMorphComparison` and you should be able to see the issue right away.</div><div>  <br></div><div>Any feedback is greatly appreciated here, and hopefully we can get this or something like it into the main image. GIFs are too fun to leave out!<br></div><div><br>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div>Eric</div></div></div></div></div>