Closed wingman-jr-addon closed 2 years ago
I've read about the GIF format before, but had another thought after reading it again tonight.
The GIF format (in particular, GIF89a) is meant as a streaming format. An over-simplified view of it may be as follows:
Header (DisplayDelayBlock ImageMutator SkippableBlock)* Trailer
It's a bit more complex than that, but at the end of the day we can view it as groups of image mutations separated by delays. Note the image mutations can rewrite only certain rects of the full image, complicating things.
Now, other libraries have worked on supporting this by fully implementing the decoding of the ImageMutator
(actually Local Image Data
) but this requires complexity in the form of LZW decoding, palette lookups, and scanning - a bit of complexity, and things JS does not excel at.
But what may be possible instead is to simply have a particularly lightweight JS library inline that essentially remuxes the data. I think a couple approaches are possible here:
Header ImageMutator+ Trailer
, as separated by the delay blocks. This would work for when the image blocks between delay separators more or less fully recreate the whole image, but would fail for more iterative approaches.Header ImageMutator+ Trailer
, where each image is the accumulation of all ImageMutators
up to that point in the stream with the delay blocks stripped out. This may be more correct, but particularly for larger animations this may prove inefficient.This seems promising, but may also require the analysis of more GIFs to see how modern ones are actually structured when they are encoded.
I wrote up a quick parser utility to try on a few real-world GIFs in the wild, as well as the adversarial GIF. It appears all of them have a basic structure, using only the Graphics Control Extension followed by a Local Image that is the full canvas size. I did not see any images yet that only replaced a partial image, so for basic support it appears that the simple approach of option 1 would work well in the real world.
It is a bit less clear how to handle the fact that GIF is a bit like video but that we have clear, discrete frames in most practical cases so performing true frame-by-frame filtering is definitely possible.
I've mulled over the different ways to implement this and I think it will be cleanest if I simply introduce a new background_gif.js
that looks a bit like background_video.js
, then handle demuxing in it and send it frame by frame via a new message type gif_frame
to processor.js
.
Watch https://github.com/wingman-jr-addon/wingman_jr/tree/true-gif-support
Basic support is now working - just tweaks left!
Fun site for testing: https://gifcities.org
The replacement tech isn't working as often as I'd like. Probably going to leave that for the future though as the blocking seems stable and reliable.
Part of #137
Not only are GIF's more straightforwardly used as a primitive video, they are also a possible basic adversarial attack (see wingman-jr-addon/model#4).
Current GIF support only looks at the first frame because that is how Firefox handles the image type by default. The challenge here is that there exists no standard API for actually getting the GIF frame by frame. Current JS approaches for this type of use case elsewhere actually parse the GIF through the use of an external library. While admirable, this is not ideal.
Note there are potential layers of support here.
More research needed.