Gif frames overlapped, ghosting effect

Comments

12 comments

  • Avatar
    Jon Sneyers

    Hi Kamil,

    Could you share the problematic gif with us so we can investigate?

     

    Thanks,

    -Jon.

    0
    Comment actions Permalink
  • Avatar
    Carrerol

    It's here: https://i.ibb.co/0mQvNtZ/anim-trans.gif
    It's worth mentioning it's displayed in iOS application, so in a web browser you'll see no difference.

    Thanks,
    Kamil 

     

    0
    Comment actions Permalink
  • Avatar
    Eric Portis

    Hi Kamil,

    A few follow-up questions:

    1. How are you displaying the image in an iOS app? In a UIImageView? (This is complicated: https://stackoverflow.com/questions/45745432/ios-11-animated-gif-display-in-uiimageview) In a UIWebView? Some other way?

    2. I'm not clear whether or not the image at https://i.ibb.co/0mQvNtZ/anim-trans.gif has been processed by Cloudinary, or not. Can you give us two URLs:

    - the original gif (does this have a ghosting effect?)

    - a Cloudinary URL showing which tranformations, if any, have been applied, which generates a gif which exhibits the ghosting.

    Thank you!

    Eric

    0
    Comment actions Permalink
  • Avatar
    Carrerol

    Hi Eric,

    1. I need to embed a gif inside a video, so I use a technique similar to: http://www.mikitamanko.com/blog/2017/05/21/swift-how-to-insert-animated-watermark-into-the-video-or-how-to-merge-two-videos/ . What interests us the most is `getFramesAnimation` method. Passed frames parameter (images) is calculated via extracting gif data using Image I/O (CGImageSourceCreateImageAtIndex to create images and kCGImagePropertyGIFDictionary to get delays). What's interesting, when I display one random frame from an array of frames as static image, it has a ghosting effect too.

    2.
    - original gif: https://i.ibb.co/sszWhY9/anim-trans.gif
    it does not have a ghosting effect right now (it had a little ghosting effect before, but I processed it via GIFSicle - I used https://graphicdesign.stackexchange.com/a/44392 except for `transparent` and `02` options)
    - cloudinary url: https://res.cloudinary.com/.../image/fetch/fl_keep_iptc,fl_keep_attribution/https://i.ibb.co/sszWhY9/anim-trans.gif (that's an example, parameters doesn't have any impact)

    Thanks,
    Kamil

    0
    Comment actions Permalink
  • Avatar
    Itai Benari

    Hi Kamil,

    This url https://i.ibb.co/sszWhY9/anim-trans.gif and this https://res.cloudinary.com/.../image/fetch/fl_keep_iptc,fl_keep_attribution/https://i.ibb.co/sszWhY9/anim-trans.gif produce exactly the same file as far as I can see.

    Is the complaint then that Cloudinary does not produce a GIF that is correctly rendered in your iOS code? Are you seeing different "ghosting" effects when using the original URL vs. the one from Cloudinary?

    The GIF format supports several disposal methods (as you might know). Here is an excerpt from https://www.w3.org/Graphics/GIF/spec-gif89a.txt:

    iv) Disposal Method - Indicates the way in which the graphic is to
                be treated after being displayed.
    
                Values :    0 -   No disposal specified. The decoder is
                                  not required to take any action.
                            1 -   Do not dispose. The graphic is to be left
                                  in place.
                            2 -   Restore to background color. The area used by the
                                  graphic must be restored to the background color.
                            3 -   Restore to previous. The decoder is required to
                                  restore the area overwritten by the graphic with
                                  what was there prior to rendering the graphic.
                         4-7 -    To be defined.

     

    I'm guessing you would want Cloudinary to respond with an "unoptimized" GIF (0)? Same as you produced with GIFSicle?

    By default we are targeting browsers and are making sure files are as small as possible without losing quality. As you said, this file displays fine in a browser so we will need to evaluate this particular use-case.

    Thanks!


    Itai

    0
    Comment actions Permalink
  • Avatar
    Eric Portis

    Note that disposal happens on a per frame basis.

    Both of the files that you sent have every frame set to disposal value "3" here – restore to previous. With these settings, every frame is supposed to be painted on a blank canvas.

    I am not sure what GifSicle is doing, but I think that Cloudinary's behavior here – preserving the disposal settings from the original gif passed to it – is correct, and the iOS code is definitely not respecting the disposal values present in the GIF.

    I am not familiar enough with Core Animation to say so definitively, but I strongly suspect that the `getFramesAnimation` method here is assuming a value of "1" for all frames. I do not currently have an environment set up where I can try to replicate the bug or fiddle with the method, but the first thing that I would try, based on this documentation:

    https://developer.apple.com/documentation/quartzcore/camediatiming/fill_modes

    would be to change this line of the method:

    animation.fillMode = kCAFillModeForwards

    to:

    animation.fillMode = kCAFillModeRemoved

    Let us know if that fixes the issue. If it doesn’t, I can only recommend finding a different library to handle animated GIFs, which respects frames' disposal settings.

    (Alternatively, you might be able to achieve the overlay with Cloudinary! See the "Animated GIFs Overlayed Onto Videos" section here: https://cloudinary.com/cookbook/adding_video_watermark_as_an_overlay)

     

    0
    Comment actions Permalink
  • Avatar
    Carrerol

    Hi guys,
    Thanks for your replies. I introduced some inconsistencies, because I firstly thought that parameters passed to cloudinary does not have any impact on output file regarding to ghosting effect, but I was wrong.

    That's a file without a ghosting effect: https://res.cloudinary.com/.../image/fetch/https://i.ibb.co/sszWhY9/anim-trans.gif
    The output looks like that: https://streamable.com/vs6ql

    And that's a resized file with a ghosting effect: https://res.cloudinary.com/.../image/fetch/h_360,w_360/https://i.ibb.co/sszWhY9/anim-trans.gif
    The output is: https://streamable.com/trc3a

    I checked the metadata of the gifs above. The original file has `restoreToPrevious` disposal method and the file after resizing has it set as `doNotDispose`. It looks like you change the disposal method when resizing.

    I added two additional parameters to retain metadata: https://res.cloudinary.com/.../image/fetch/h_360,w_360,fl_keep_iptc,fl_keep_attribution/https://i.ibb.co/sszWhY9/anim-trans.gif but it's still converting to `doNotDispose` disposal method.

    Am I right that `fl_keep_iptc` and `fl_keep_attribution` attributes do not work correctly in this particular case?

    BTW Only one iOS library can display gifs with `doNotDispose` option (it's called FLAnimatedImage), but the mechanism they're using is strictly connected with UIImageView and CADisplaylink and it's useless in my case (I'm using layer's contents to embed a gif in a video).
    Changing animation's fillMode to kCAFillModeRemoved didn't help. Displaying a single frame without animation has a ghosting effect too.

    Thanks,
    Kamil

    0
    Comment actions Permalink
  • Avatar
    Eric Portis

    Hi Kamil,

    Thanks for your patience and sorry for the delayed response.

    Also, sorry for the incorrect information, earlier. You're right that we're changing the disposal modes! Some initial research suggests an answer to why the changes we're making are invisible in browsers but visible in iOS... but let me do some more research and get back to you with a definitive answer, and what we intend to do about it on our side, going forward.

    The keep_iptc and keep_attribution flags deal with EXIF/IPTC metadata -- not the gif-specific metadata that these disposal settings live within. These flags don't (and shouldn't) affect disposal modes.

    0
    Comment actions Permalink
  • Avatar
    Eric Portis

    So, one of the ways that we're optimizing the GIF (which we do whenever we transform it in any way) is by changing it so that only the parts of the gif that have changed are encoded in each frame. This requires changing the disposal mode from "Restore to previous" (which your original gif used to paint each frame entirely from scratch) to "Restore to background". Most GIF decoders, and all web browsers, "restore" to transparency. Apparently, Apple's libraries restore to the background color of the GIF – in this case, white. Thus, the ghosting.

    I don't think we can justify turning this optimization off by default. The bytesize gains are too large.

    If you can't use Cloudinary to do the overlay, would it be possible to convert the GIF to a different format that is properly decoded by Apple's libraries – perhaps APNG?

    https://res.cloudinary.com/.../image/fetch/f_png,fl_apng,h_360,w_360/https://i.ibb.co/sszWhY9/anim-trans.gif

     

    0
    Comment actions Permalink
  • Avatar
    Carrerol

    Hi Eric,
    Thanks for investigation of the issue. I followed your suggestion with using APNG and now it works - the ghosting effect disappeared. The drawback of this solution is time-consuming conversion. For full size gifs it takes about 60~ seconds.

    BTW Do you know if it is possible that the gif itself is malformed (regardless of a disposal method)? I use about hundred of gifs and a ghosting effect happens to only two of them.

    Thanks,
    Kamil

    0
    Comment actions Permalink
  • Avatar
    Eric Portis

    Yeah, APNG conversions can be slow...

    About the frequency -- curious! Can you send the other Gif that exhibited the ghosting, and a couple that did not (but still included transparency)?

    0
    Comment actions Permalink
  • Avatar
    Carrerol

    From what I've noticed:

    - Disposal method of these two gifs: https://res.cloudinary.com/.../image/fetch/https://i.ibb.co/Qfdy1pH/zum-one.gif and https://res.cloudinary.com/.../image/fetch/https://i.ibb.co/74tkVxM/zum-big-one.gif is changed (or remains the same) after resizing via Cloudinary to `doNotDispose` and it causes ghosting effect.
    When I change disposal method to `restoreToPrevious` or `restoreToBackgroundColor` before resizing, it still converts it to `doNotDispose`.

    - Disposal method of gifs (just two examples): https://res.cloudinary.com/.../image/fetch/https://i.ibb.co/XLk94yZ/zum-three.gif and https://res.cloudinary.com/.../image/fetch/https://i.ibb.co/pzLS3GP/zum-three-men-woman.gif is changed (or remains the same) after resizing via Cloudinary to `restoreToBackgroundColor` and it doesn't cause ghosting effect.

    So, the problem is that for two of my gifs (zum-one and zum-big-one) disposal method is always changed to `doNotDispose` after resizing and for the others it's `restoreToBackgroundColor` which is ok.

     

    0
    Comment actions Permalink

Please sign in to leave a comment.