damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

KFOptionSetter+.swift (4859B)


      1 //
      2 //  KFOptionSetter+.swift
      3 //  damus
      4 //
      5 //  Created by Oleg Abalonski on 2/15/23.
      6 //
      7 
      8 import UIKit
      9 import Kingfisher
     10 
     11 extension KFOptionSetter {
     12     
     13     func imageContext(_ imageContext: ImageContext) -> Self {
     14         options.callbackQueue = .dispatch(.global(qos: .background))
     15         options.processingQueue = .dispatch(.global(qos: .background))
     16         options.downloader = CustomImageDownloader.shared
     17         options.processor = CustomImageProcessor(
     18             maxSize: imageContext.maxMebibyteSize(),
     19             downsampleSize: imageContext.downsampleSize()
     20         )
     21         options.cacheSerializer = CustomCacheSerializer(
     22             maxSize: imageContext.maxMebibyteSize(),
     23             downsampleSize: imageContext.downsampleSize()
     24         )
     25         options.backgroundDecode = true
     26         options.cacheOriginalImage = true
     27         options.scaleFactor = UIScreen.main.scale
     28         options.onlyLoadFirstFrame = should_disable_image_animation()
     29         
     30         return self
     31     }
     32     
     33     func onFailure(fallbackUrl: URL?, cacheKey: String?) -> Self {
     34         guard let url = fallbackUrl, let key = cacheKey else { return self }
     35         let imageResource = ImageResource(downloadURL: url, cacheKey: key)
     36         let source = imageResource.convertToSource()
     37         options.alternativeSources = [source]
     38         
     39         return self
     40     }
     41 }
     42 
     43 let MAX_FILE_SIZE = 20_971_520 // 20MiB
     44 
     45 enum ImageContext {
     46     case pfp
     47     case banner
     48     case note
     49     
     50     func maxMebibyteSize() -> Int {
     51         switch self {
     52         case .pfp:
     53             return 5_242_880 // 5Mib
     54         case .banner, .note:
     55             return 20_971_520 // 20MiB
     56         }
     57     }
     58     
     59     func downsampleSize() -> CGSize {
     60         switch self {
     61         case .pfp:
     62             return CGSize(width: 200, height: 200)
     63         case .banner:
     64             return CGSize(width: 750, height: 250)
     65         case .note:
     66             return CGSize(width: 500, height: 500)
     67         }
     68     }
     69 }
     70 
     71 struct CustomImageProcessor: ImageProcessor {
     72     
     73     let maxSize: Int
     74     let downsampleSize: CGSize
     75     
     76     let identifier = "com.damus.customimageprocessor"
     77     
     78     func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
     79         
     80         switch item {
     81         case .image(_):
     82             // This case will never run
     83             return DefaultImageProcessor.default.process(item: item, options: options)
     84         case .data(let data):
     85             
     86             // Handle large image size
     87             if data.count > maxSize {
     88                 return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
     89             }
     90             
     91             // Handle SVG image
     92             if let dataString = String(data: data, encoding: .utf8),
     93                 let svg = SVG(dataString) {
     94                 
     95                     let render = UIGraphicsImageRenderer(size: svg.size)
     96                     let image = render.image { context in
     97                         svg.draw(in: context.cgContext)
     98                     }
     99 
    100                     return image.kf.scaled(to: options.scaleFactor)
    101             }
    102             
    103             return DefaultImageProcessor.default.process(item: item, options: options)
    104         }
    105     }
    106 }
    107 
    108 struct CustomCacheSerializer: CacheSerializer {
    109     
    110     let maxSize: Int
    111     let downsampleSize: CGSize
    112 
    113     func data(with image: Kingfisher.KFCrossPlatformImage, original: Data?) -> Data? {
    114         return DefaultCacheSerializer.default.data(with: image, original: original)
    115     }
    116 
    117     func image(with data: Data, options: Kingfisher.KingfisherParsedOptionsInfo) -> Kingfisher.KFCrossPlatformImage? {
    118         if data.count > maxSize {
    119             return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
    120         }
    121 
    122         return DefaultCacheSerializer.default.image(with: data, options: options)
    123     }
    124 }
    125 
    126 class CustomSessionDelegate: SessionDelegate {
    127     override func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
    128         let contentLength = response.expectedContentLength
    129         
    130         // Content-Length header is optional (-1 when missing)
    131         if (contentLength != -1 && contentLength > MAX_FILE_SIZE) {
    132             return super.urlSession(session, dataTask: dataTask, didReceive: URLResponse(), completionHandler: completionHandler)
    133         }
    134         
    135         super.urlSession(session, dataTask: dataTask, didReceive: response, completionHandler: completionHandler)
    136     }
    137 }
    138 
    139 class CustomImageDownloader: ImageDownloader {
    140     
    141     static let shared = CustomImageDownloader(name: "shared")
    142     
    143     override init(name: String) {
    144         super.init(name: name)
    145         sessionDelegate = CustomSessionDelegate()
    146     }
    147 }