kean / Nuke

Image loading system
https://kean.blog/nuke
MIT License
8.07k stars 527 forks source link

Question: How to resize image without losing quality? #723

Closed theedov closed 12 months ago

theedov commented 1 year ago

Hello,

I'm trying to fetch a profile image and show it in TabView(SwiftUI). Before I was doing it using KingFisher but wanted to try Nuke/NukeUI.

Could anyone help me convert my KingFisher code to Nuke?

KingFisher:

    func fetchProfileImage() {
        if let url = accountManager.user?.profilePictureUrl {
            let targetSize = CGSize(width: 25.0, height: 25.0)
            let resize = ResizingImageProcessor(referenceSize: targetSize, mode: .aspectFill)
            let crop = CroppingImageProcessor(size: targetSize)
            let round = RoundCornerImageProcessor(cornerRadius: targetSize.height / 2)

            let processor = ((resize |> crop) |> round)

            KingfisherManager.shared.retrieveImage(
                with: url,
                options: [ .processor(processor), .scaleFactor(UIScreen.main.scale)],
                completionHandler: { result in
                    switch result {
                    case .success(let value):
                        let img = value.image
                        self.profileImage = img.withRenderingMode(.alwaysOriginal)
                    case .failure:
                        self.profileImage = nil
                    }
                })
        }
    }

My Nuke attempt:

    func fetchProfileImage() async {
        if let url = accountManager.user?.profilePictureUrl {
            let scale = UIScreen.main.scale
            let targetSize = CGSize(width: 25.0 * scale, height: 25.0 * scale)
            let resize = ImageProcessors.Resize(size: targetSize, unit: .pixels, contentMode: .aspectFill)
            let crop = ImageProcessors.Resize(size: .init(width: 25, height: 25), unit: .pixels, crop: true)
            let circle = ImageProcessors.Circle()

            let request = ImageRequest(
                url: url,
                processors: [
                    resize,
                    crop,
                    circle
                ],
                priority: .high
            )

            do {
                let image = try await ImagePipeline.shared.image(for: request)
                self.profileImage = image.withRenderingMode(.alwaysOriginal)
            } catch {
                self.profileImage = nil
            }
        }
    }

The issue with my Nuke code is that the image is blurry.

kean commented 1 year ago

Hi,

let scale = UIScreen.main.scale
let targetSize = CGSize(width: 25.0 * scale, height: 25.0 * scale)
let resize = ImageProcessors.Resize(size: targetSize, unit: .pixels, contentMode: .aspectFill)
let crop = ImageProcessors.Resize(size: .init(width: 25, height: 25), unit: .pixels, crop: true)

The second resize filer (crop) sets target size as 25x25 pixels.

You can try this:

let resize = ImageProcessors.Resize(
    size: CGSize(width: 25, height: 25), 
    unit: .points, 
    contentMode: .aspectFill, 
    crop: true
)
theedov commented 1 year ago

Thanks @kean, I tried that but the image doesn't fit in the TabView and it's still blurry. Tried both .points & .pixels.

image

kean commented 1 year ago

Make sure to set the scale to match the screen scale. The scale is one of the image request parameters, and I think there is also a way to set a default one for all downloaded images.

theedov commented 1 year ago

Yeah, I was looking to set the scale, but I'm unable to find it anywhere.

image

Was only able to find the upscale: Bool inside ImageProcessors.Resize init.

theedov commented 1 year ago

Oh wait, it's scaleKey inside userInfo. That made it work. Thanks!

kean commented 1 year ago

Great! The scale changes the logical (or point) size of the image, which is used for layout.