SDWebImage / SDWebImageSwiftUI

SwiftUI Image loading and Animation framework powered by SDWebImage
https://sdwebimage.github.io/SDWebImageSwiftUI
MIT License
2.18k stars 224 forks source link

AnimatedImage does not support official AVIFS animation #291

Closed lschaupp closed 9 months ago

lschaupp commented 9 months ago

I am trying to make AVIF work:

https://github.com/SDWebImage/SDWebImageAVIFCoder

However, AnimatedImage does not support at the moment the official AVIFS file from here after proper setup: "https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs"

It will show only a static image.

dreampiggy commented 9 months ago

Can't SDWebImage UIKit show this AVIF images as well ?

I think there are no differences in AnimatedImage in SwiftUI and SDAnimatedImageView in UIKit.

dreampiggy commented 9 months ago

What's the codec config when you use SDWebImageAVIFCoder

In readme it points out that you must declare a CocoaPods subspecs (for Carthage/SPM it use aom only) to choose the codec

dreampiggy commented 9 months ago

Test this wors on UIKit SDAnimatedImageView

image

Podfile use libdav1d as codec

  pod 'SDWebImage/MapKit', :path => './'
  pod 'SDWebImageWebPCoder'
  pod 'SDWebImageAVIFCoder'
  pod 'libavif', :subspecs => ['libdav1d']
dreampiggy commented 9 months ago

Test this works on SDWebImageSwiftUI's WebImage animation image

Test this works on SDWebImageSwiftUI's AnimatedImage animation image

I guess this may because of some wrong integration / setup for AVIF

dreampiggy commented 9 months ago

If you don't know how to add SDWebImage custom coders, check this PR's code changes...

See: https://github.com/SDWebImage/SDWebImageSwiftUI/pull/292

lschaupp commented 9 months ago

Dear Dreampiggy,

Thank you so much for feedback. Yes I made it work yesterday within UIKit as well as you proposed above. However using directly AnimatedImage (SwiftUI) it does not work.

dreampiggy commented 9 months ago

AnimatedImage just use UIViewRepresentable. So I think maybe there are still some other configuration effects this.

Can you try to debug and see what's happended ? Or you can play toy with SDWebImageSwiftUI's own demo (See README.md, which explains how to run this repo's demo)

My guessing is that, for example:

  1. You use some advanced animated image playback control, like pausing, passes a binding to AnimatedImage while the binding is always false.
  2. You load the AVIFS in another code, which cache the non-animated form, like JPEG version into disk cache. Then your AnimatedImage re-query for that disk cache image and loss animating. Try clean the local disk cache and directly show your view?
dreampiggy commented 9 months ago

Another possible case:

iOS 17's ImageIO, introduce the basic support for static AVIF. It embed into the HEIFCoder using a brand (HEIF is a container format, has a concept of brand. The brand can extend to support AV1 codec: https://nokiatech.github.io/heif/technical.html)

Really confusing, HEIF and AVIF can even store the same thing...like a .mp4 video which can use MPEG-4 codec or AVI or H.264 or H.265......)😖

So, can you make sure that the AVIFCoder actually called ? You can set a breakpoint in SDImageAVIFCoder.decodedImage method.

lschaupp commented 9 months ago

AnimatedImage just use UIViewRepresentable. So I think maybe there are still some other configuration effects this.

Can you try to debug and see what's happended ? Or you can play toy with SDWebImageSwiftUI's own demo (See README.md, which explains how to run this repo's demo)

My guessing is that, for example:

  1. You use some advanced animated image playback control, like pausing, passes a binding to AnimatedImage while the binding is always false.
  2. You load the AVIFS in another code, which cache the non-animated form, like JPEG version into disk cache. Then your AnimatedImage re-query for that disk cache image and loss animating. Try clean the local disk cache and directly show your view?

Can you confirm that the animation works on your side with "AnimatedImage"?

  1. I am not using any playback control. I simply call: AnimatedImage(url: URL(string:"https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs"))
  2. Since I am loading the URL directly (and no where else in the code), I assume there is no caching involved (outside of AnimatedImage)
  3. Also I have used webp coder before, and I had zero issues with it (same view etc...)
lschaupp commented 9 months ago

AnimatedImage just use UIViewRepresentable. So I think maybe there are still some other configuration effects this. Can you try to debug and see what's happended ? Or you can play toy with SDWebImageSwiftUI's own demo (See README.md, which explains how to run this repo's demo) My guessing is that, for example:

  1. You use some advanced animated image playback control, like pausing, passes a binding to AnimatedImage while the binding is always false.
  2. You load the AVIFS in another code, which cache the non-animated form, like JPEG version into disk cache. Then your AnimatedImage re-query for that disk cache image and loss animating. Try clean the local disk cache and directly show your view?

Can you confirm that the animation works on your side with "AnimatedImage"?

  1. I am not using any playback control. I simply call: AnimatedImage(url: URL(string:"https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs"))
  2. Since I am loading the URL directly (and no where else in the code), I assume there is no caching involved.

Another possible case:

iOS 17's ImageIO, introduce the basic support for static AVIF. It embed into the HEIFCoder using a brand (HEIF is a container format, has a concept of brand. The brand can extend to support AV1 codec: https://nokiatech.github.io/heif/technical.html)

Really confusing, HEIF and AVIF can even store the same thing...like a .mp4 video which can use MPEG-4 codec or AVI or H.264 or H.265......)😖

So, can you make sure that the AVIFCoder actually called ? You can set a breakpoint in SDImageAVIFCoder.decodedImage method.

I will try and check. However I think it might be quicker if you could let me know whether the default AVIFS animation works for you as well using AnimatedImage. If it does, I will def. go down that rabbit hole of debugging it ;)

dreampiggy commented 9 months ago

However I think it might be quicker if you could let me know whether the default AVIFS animation works for you as well using AnimatedImage

It works...See my comments, which I take 2 screenshots, using SDWebImageSwiftUI's iOS demo, running on Xcode 15.0 iPhone 14 iOS 17 simulator

The screenshot is not showing static image, it's animated. You see that star AVIFS, is not the first frame, right ?

dreampiggy commented 9 months ago

Using code:

struct ContentView: View {
    @State var imageURLs = [
        "https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs",
        "https://raw.githubusercontent.com/link-u/avif-sample-images/master/fox.profile0.8bpc.yuv420.avif",
    ]
    @State var animated: Bool = false // You can change between WebImage/AnimatedImage
    @State var imageIndex : Int = 0
    var body: some View {
        Group {
            Text("\(animated ? "AnimatedImage" : "WebImage") - \((imageURLs[imageIndex] as NSString).lastPathComponent)")
            Spacer()
            #if os(watchOS)
            WebImage(url:URL(string: imageURLs[imageIndex]))
            .resizable()
            .aspectRatio(contentMode: .fit)
            #else
            if self.animated {
                AnimatedImage(url:URL(string: imageURLs[imageIndex]))
                .resizable()
                .aspectRatio(contentMode: .fit)
            } else {
                WebImage(url:URL(string: imageURLs[imageIndex]))
                .resizable()
                .aspectRatio(contentMode: .fit)
            }
            #endif
            Spacer()
            Button("Next") {
                if imageIndex + 1 >= imageURLs.count {
                    imageIndex = 0
                } else {
                    imageIndex += 1
                }
            }
            Button("Reload") {
                SDImageCache.shared.clearMemory()
                SDImageCache.shared.clearDisk(onCompletion: nil)
            }
            Toggle("Switch", isOn: $animated)
        }
    }
}
lschaupp commented 9 months ago

Using code:

struct ContentView: View {
    @State var imageURLs = [
        "https://raw.githubusercontent.com/link-u/avif-sample-images/master/star-12bpc-with-alpha.avifs",
        "https://raw.githubusercontent.com/link-u/avif-sample-images/master/fox.profile0.8bpc.yuv420.avif",
    ]
    @State var animated: Bool = false // You can change between WebImage/AnimatedImage
    @State var imageIndex : Int = 0
    var body: some View {
        Group {
            Text("\(animated ? "AnimatedImage" : "WebImage") - \((imageURLs[imageIndex] as NSString).lastPathComponent)")
            Spacer()
            #if os(watchOS)
            WebImage(url:URL(string: imageURLs[imageIndex]))
            .resizable()
            .aspectRatio(contentMode: .fit)
            #else
            if self.animated {
                AnimatedImage(url:URL(string: imageURLs[imageIndex]))
                .resizable()
                .aspectRatio(contentMode: .fit)
            } else {
                WebImage(url:URL(string: imageURLs[imageIndex]))
                .resizable()
                .aspectRatio(contentMode: .fit)
            }
            #endif
            Spacer()
            Button("Next") {
                if imageIndex + 1 >= imageURLs.count {
                    imageIndex = 0
                } else {
                    imageIndex += 1
                }
            }
            Button("Reload") {
                SDImageCache.shared.clearMemory()
                SDImageCache.shared.clearDisk(onCompletion: nil)
            }
            Toggle("Switch", isOn: $animated)
        }
    }
}

Fixed it on my side. Indeed it was issues with the codec. Refactoring my Podfile (as proposed earlier by you) did the job at the end. Thank you very much for your support!

dreampiggy commented 9 months ago

Do not use pod 'libavif'. Must choose a subspecs as codec

dreampiggy commented 9 months ago

Previouslly old version allows you use to pod "libavif" without subspec, bu it hardcoded to link aom. Aom is uesless in real world Application and will occupy 1MB binary size. So I remove the default codec