Kingfisher 사용해서 image downSampling 해보기(.cacheOriginalImage의 의미는 무엇일까?)

jane·2022년 9월 3일
4

iOS

목록 보기
30/32
post-thumbnail

지난 포스팅에서 아래 코드와 같이 downSampling 메서드를 직접 구현하여 다운샘플링된 이미지를 imageView의 image에 할당해주었다.

func downSample(at url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
    let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, imageSourceOptions) else {
        return
    }
    
    let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
    let downsampleOptions = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
    ] as CFDictionary
    
    guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
        return UIImage()
    }
    return UIImage(cgImage: downsampledImage)
}

커스텀 셀 configure 부분

let serialQueue = DispatchQueue(label: "Decode queue")
serialQueue.async { [weak self] in
    
    let downsampled = downSample(
        at: url,
        to: CGSize(width: 60, height: 90),
        scale: UIScreen.main.scale
    )
    DispatchQueue.main.async {
        self?.posterImageView.image = downsampled
    }
}

WWDC 보고 적용해본건데, Kingfisher가 똑같이 구현해놓았기도 하고 기존에 다른 이미지 처리 기능을 위해 Kingfisher을 사용하고 있었기 때문에 Kingfisher의 DownsamplingImageProcessor을 사용해보기로 했다.

Kingfisher cheat sheet를 참고해서 사용해보자.

DownsamplingImageProcessor는 scaleFactor과 cacheOrigialImage와 함께 사용된다고 한다.

scaleFactor에 대해서는 저번에 알아봤고,
cacheOrigialImage에 대해 보도록 하자ㅏㅏㅏ

*이건 번외인데 지난 포스팅에서 말했던 것처럼 아래 사진의 설명에서도 resize를 쓰지말라고 되어있는 모습을 볼수 있음

  • downsampling은 data -> image -> rendering 과정에서 data에서 image로 가기 전에 데이터 자체를 줄이는것이라면
  • resizing은 rendering 이미 다 된 이미지를 다시 rendering해서 메모리를 많이 잡아먹게 됨

자 다시 돌아와서
cacheOrigialImage에 대해서...
한가지 의문점이 들었다.

원본 이미지를 왜 캐시해?

지난번 포스팅에서 알아봤던 것처럼 UIImage가 디코딩된 이미지를 메모리에 영구적으로 들고있는다고 했는데, 원본 이미지를 캐시하면 메모리를 똑같이 사용하는거 아니냐

대체 어떤 이미지를 어디에 캐시해놓는건지...
Araboza

let processor = DownsamplingImageProcessor(size: CGSize(width: 368, height: 500))
self.posterImageView.kf.setImage(
    with: url,
    placeholder: UIImage(),
    options: [
        .processor(processor),
        .scaleFactor(UIScreen.main.scale),
        .cacheOriginalImage],
    completionHandler: nil
)

그래서 cacheOriginalImage가 어떤 이미지를 어디에 캐시하는지 들어가서 봤다.


ImageProcessor가 사용됐을 경우 kingfisher는 원본 이미지랑 다운샘플링 된 이미지 두개 다 저장하는데
나중에 이미지에 다른 ImageProcessor가 사용될 때 디스크에 캐시된 이미지를 가져와서 적용하려는 이유에서 원본 이미지는 디스크에만 저장이 된대

그럼 다운샘플링된 이미지는 메모리에만 저장된다는 거군

원본 이미지는 메모리에 저장되지 않아서 메모리 사용량이 줄었던 것이었구나 🙌

캐시된 이미지를 어떻게 사용하는지도 한번 보자면

kingfisher의 retrieveImageFromCache에서 캐시된 이미지를 찾을 때 먼저 processed된 이미지가 있는지 확인한 후 없는 경우에 원본 이미지가 있는지 확인한다.

processed된 이미지가 있는지는 메모리 캐시에서 찾음

그리고 그 다음으로 원본 이미지가 있는지는 디스크 캐시에서 찾고있는 모습을 볼 수 있음

이렇게 된다면
ListView에서 DetailView로 갈때 이미 디스크 캐시해놓은 원본 이미지가 있으니 DetailView에서는 디스크 캐시에서 가져온 이미지로 downsampling만 진행하면 되겠군!

ListView에서 디스크 캐시해놓으면

func configure(with viewModel: MovieListCellViewModel) {
    self.viewModel = viewModel
    guard let url = viewModel.imageUrl else {
        return
    }
    self.posterImageView.kf.setImage(
        with: url,
        options: [
            .processor(DownsamplingImageProcessor(size: CGSize(width: 200, height: 300))),
            .scaleFactor(UIScreen.main.scale),
            .cacheOriginalImage
    ])
    self.titleLabel.text = viewModel.title
    self.originalLanguageLabel.text = viewModel.originalLanguage
    self.genresLabel.text = viewModel.genres
}
    }

DetailView에서는 네트워크 통신할 필요없이 디스크 캐시에서 원본 이미지 가져와서 바로 다운샘플링 가능 ㅎ
그리고 아래와 같이 cacheOriginalImage 옵션이 있어도 어짜피 이전에 캐시된 원본 이미지가 있기때문에 내부적으로 검사해서 중복이라면 디스크 캐시 또 안함

private func configureImageView(with url: URL) {
    let processor = DownsamplingImageProcessor(size: CGSize(width: 368, height: 500))
    self.posterImageView.kf.setImage(
        with: url,
        placeholder: UIImage(),
        options: [
            .transition(.fade(1)),
            .forceTransition,
            .processor(processor),
            .scaleFactor(UIScreen.main.scale),
            .cacheOriginalImage],
        completionHandler: nil
    )
}

profile
제가 나중에 다시 보려고 기록합니다 ✏️

1개의 댓글

comment-user-thumbnail
2022년 9월 29일

자ㄹ 봤습니다

답글 달기