wingman-jr-addon / wingman_jr

This is the official repository (https://github.com/wingman-jr-addon/wingman_jr) for the Wingman Jr. Firefox addon, which filters NSFW images in the browser fully client-side: https://addons.mozilla.org/en-US/firefox/addon/wingman-jr-filter/ Optional DNS-blocking using Cloudflare's 1.1.1.1 for families! Also, check out the blog!
https://wingman-jr.blogspot.com/
Other
35 stars 6 forks source link

True GIF support #153

Closed wingman-jr-addon closed 2 years ago

wingman-jr-addon commented 2 years ago

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.

  1. First image support (default, and current status)
  2. Detect adversarial 2 image attack and pull both frames
  3. True support by treating as video

More research needed.

wingman-jr-addon commented 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:

  1. Remux into multiple images something like 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.
  2. Remux into multiple images like 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.
  3. Perhaps a hybrid approach that somehow detects if the full frame has been rewritten so it can know when it no longer has to accumulate from the beginning.

This seems promising, but may also require the analysis of more GIFs to see how modern ones are actually structured when they are encoded.

wingman-jr-addon commented 2 years ago

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.

wingman-jr-addon commented 2 years ago

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

wingman-jr-addon commented 2 years ago

Basic support is now working - just tweaks left!

wingman-jr-addon commented 2 years ago

Fun site for testing: https://gifcities.org

wingman-jr-addon commented 2 years ago

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.