KFOptionsSetter.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. //
  2. // KFOptionsSetter.swift
  3. // Kingfisher
  4. //
  5. // Created by onevcat on 2020/12/22.
  6. //
  7. // Copyright (c) 2020 Wei Wang <onevcat@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import Foundation
  27. import CoreGraphics
  28. public protocol KFOptionSetter {
  29. var options: KingfisherParsedOptionsInfo { get nonmutating set }
  30. var onFailureDelegate: Delegate<KingfisherError, Void> { get }
  31. var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { get }
  32. var onProgressDelegate: Delegate<(Int64, Int64), Void> { get }
  33. var delegateObserver: AnyObject { get }
  34. }
  35. extension KF.Builder: KFOptionSetter {
  36. public var delegateObserver: AnyObject { self }
  37. }
  38. @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
  39. extension KFImage: KFOptionSetter {
  40. public var options: KingfisherParsedOptionsInfo {
  41. get { binder.options }
  42. nonmutating set { binder.options = newValue }
  43. }
  44. public var onFailureDelegate: Delegate<KingfisherError, Void> { binder.onFailureDelegate }
  45. public var onSuccessDelegate: Delegate<RetrieveImageResult, Void> { binder.onSuccessDelegate }
  46. public var onProgressDelegate: Delegate<(Int64, Int64), Void> { binder.onProgressDelegate }
  47. public var delegateObserver: AnyObject { binder }
  48. }
  49. // MARK: - Life cycles
  50. extension KFOptionSetter {
  51. /// Sets the progress block to current builder.
  52. /// - Parameter block: Called when the image downloading progress gets updated. If the response does not contain an
  53. /// `expectedContentLength`, this block will not be called. If `block` is `nil`, the callback
  54. /// will be reset.
  55. /// - Returns: A `Self` value with changes applied.
  56. public func onProgress(_ block: DownloadProgressBlock?) -> Self {
  57. onProgressDelegate.delegate(on: delegateObserver) { (observer, result) in
  58. block?(result.0, result.1)
  59. }
  60. return self
  61. }
  62. /// Sets the the done block to current builder.
  63. /// - Parameter block: Called when the image task successfully completes and the the image set is done. If `block`
  64. /// is `nil`, the callback will be reset.
  65. /// - Returns: A `KF.Builder` with changes applied.
  66. public func onSuccess(_ block: ((RetrieveImageResult) -> Void)?) -> Self {
  67. onSuccessDelegate.delegate(on: delegateObserver) { (observer, result) in
  68. block?(result)
  69. }
  70. return self
  71. }
  72. /// Sets the catch block to current builder.
  73. /// - Parameter block: Called when an error happens during the image task. If `block`
  74. /// is `nil`, the callback will be reset.
  75. /// - Returns: A `KF.Builder` with changes applied.
  76. public func onFailure(_ block: ((KingfisherError) -> Void)?) -> Self {
  77. onFailureDelegate.delegate(on: delegateObserver) { (observer, error) in
  78. block?(error)
  79. }
  80. return self
  81. }
  82. }
  83. // MARK: - Basic options settings.
  84. extension KFOptionSetter {
  85. /// Sets the target image cache for this task.
  86. /// - Parameter cache: The target cache is about to be used for the task.
  87. /// - Returns: A `Self` value with changes applied.
  88. ///
  89. /// Kingfisher will use the associated `ImageCache` object when handling related operations,
  90. /// including trying to retrieve the cached images and store the downloaded image to it.
  91. ///
  92. public func targetCache(_ cache: ImageCache) -> Self {
  93. options.targetCache = cache
  94. return self
  95. }
  96. /// Sets the target image cache to store the original downloaded image for this task.
  97. /// - Parameter cache: The target cache is about to be used for storing the original downloaded image from the task.
  98. /// - Returns: A `Self` value with changes applied.
  99. ///
  100. /// The `ImageCache` for storing and retrieving original images. If `originalCache` is
  101. /// contained in the options, it will be preferred for storing and retrieving original images.
  102. /// If there is no `.originalCache` in the options, `.targetCache` will be used to store original images.
  103. ///
  104. /// When using KingfisherManager to download and store an image, if `cacheOriginalImage` is
  105. /// applied in the option, the original image will be stored to this `originalCache`. At the
  106. /// same time, if a requested final image (with processor applied) cannot be found in `targetCache`,
  107. /// Kingfisher will try to search the original image to check whether it is already there. If found,
  108. /// it will be used and applied with the given processor. It is an optimization for not downloading
  109. /// the same image for multiple times.
  110. ///
  111. public func originalCache(_ cache: ImageCache) -> Self {
  112. options.originalCache = cache
  113. return self
  114. }
  115. /// Sets the downloader used to perform the image download task.
  116. /// - Parameter downloader: The downloader which is about to be used for downloading.
  117. /// - Returns: A `Self` value with changes applied.
  118. ///
  119. /// Kingfisher will use the set `ImageDownloader` object to download the requested images.
  120. public func downloader(_ downloader: ImageDownloader) -> Self {
  121. options.downloader = downloader
  122. return self
  123. }
  124. /// Sets the download priority for the image task.
  125. /// - Parameter priority: The download priority of image download task.
  126. /// - Returns: A `Self` value with changes applied.
  127. ///
  128. /// The `priority` value will be set as the priority of the image download task. The value for it should be
  129. /// between 0.0~1.0. You can choose a value between `URLSessionTask.defaultPriority`, `URLSessionTask.lowPriority`
  130. /// or `URLSessionTask.highPriority`. If this option not set, the default value (`URLSessionTask.defaultPriority`)
  131. /// will be used.
  132. public func downloadPriority(_ priority: Float) -> Self {
  133. options.downloadPriority = priority
  134. return self
  135. }
  136. /// Sets whether Kingfisher should ignore the cache and try to start a download task for the image source.
  137. /// - Parameter enabled: Enable the force refresh or not.
  138. /// - Returns: A `Self` value with changes applied.
  139. public func forceRefresh(_ enabled: Bool = true) -> Self {
  140. options.forceRefresh = enabled
  141. return self
  142. }
  143. /// Sets whether Kingfisher should try to retrieve the image from memory cache first. If not found, it ignores the
  144. /// disk cache and starts a download task for the image source.
  145. /// - Parameter enabled: Enable the memory-only cache searching or not.
  146. /// - Returns: A `Self` value with changes applied.
  147. ///
  148. /// This is useful when you want to display a changeable image behind the same url at the same app session, while
  149. /// avoiding download it for multiple times.
  150. public func fromMemoryCacheOrRefresh(_ enabled: Bool = true) -> Self {
  151. options.fromMemoryCacheOrRefresh = enabled
  152. return self
  153. }
  154. /// Sets whether the image should only be cached in memory but not in disk.
  155. /// - Parameter enabled: Whether the image should be only cache in memory or not.
  156. /// - Returns: A `Self` value with changes applied.
  157. public func cacheMemoryOnly(_ enabled: Bool = true) -> Self {
  158. options.cacheMemoryOnly = enabled
  159. return self
  160. }
  161. /// Sets whether Kingfisher should wait for caching operation to be completed before calling the
  162. /// `onSuccess` or `onFailure` block.
  163. /// - Parameter enabled: Whether Kingfisher should wait for caching operation.
  164. /// - Returns: A `Self` value with changes applied.
  165. public func waitForCache(_ enabled: Bool = true) -> Self {
  166. options.waitForCache = enabled
  167. return self
  168. }
  169. /// Sets whether Kingfisher should only try to retrieve the image from cache, but not from network.
  170. /// - Parameter enabled: Whether Kingfisher should only try to retrieve the image from cache.
  171. /// - Returns: A `Self` value with changes applied.
  172. ///
  173. /// If the image is not in cache, the image retrieving will fail with the
  174. /// `KingfisherError.cacheError` with `.imageNotExisting` as its reason.
  175. public func onlyFromCache(_ enabled: Bool = true) -> Self {
  176. options.onlyFromCache = enabled
  177. return self
  178. }
  179. /// Sets whether the image should be decoded in a background thread before using.
  180. /// - Parameter enabled: Whether the image should be decoded in a background thread before using.
  181. /// - Returns: A `Self` value with changes applied.
  182. ///
  183. /// Setting to `true` will decode the downloaded image data and do a off-screen rendering to extract pixel
  184. /// information in background. This can speed up display, but will cost more time and memory to prepare the image
  185. /// for using.
  186. public func backgroundDecode(_ enabled: Bool = true) -> Self {
  187. options.backgroundDecode = enabled
  188. return self
  189. }
  190. /// Sets the callback queue which is used as the target queue of dispatch callbacks when retrieving images from
  191. /// cache. If not set, Kingfisher will use main queue for callbacks.
  192. /// - Parameter queue: The target queue which the cache retrieving callback will be invoked on.
  193. /// - Returns: A `Self` value with changes applied.
  194. ///
  195. /// - Note:
  196. /// This option does not affect the callbacks for UI related extension methods. You will always get the
  197. /// callbacks called from main queue.
  198. public func callbackQueue(_ queue: CallbackQueue) -> Self {
  199. options.callbackQueue = queue
  200. return self
  201. }
  202. /// Sets the scale factor value when converting retrieved data to an image.
  203. /// - Parameter factor: The scale factor value.
  204. /// - Returns: A `Self` value with changes applied.
  205. ///
  206. /// Specify the image scale, instead of your screen scale. You may need to set the correct scale when you dealing
  207. /// with 2x or 3x retina images. Otherwise, Kingfisher will convert the data to image object at `scale` 1.0.
  208. ///
  209. public func scaleFactor(_ factor: CGFloat) -> Self {
  210. options.scaleFactor = factor
  211. return self
  212. }
  213. /// Sets whether the original image should be cached even when the original image has been processed by any other
  214. /// `ImageProcessor`s.
  215. /// - Parameter enabled: Whether the original image should be cached.
  216. /// - Returns: A `Self` value with changes applied.
  217. ///
  218. /// If set and an `ImageProcessor` is used, Kingfisher will try to cache both the final result and original
  219. /// image. Kingfisher will have a chance to use the original image when another processor is applied to the same
  220. /// resource, instead of downloading it again. You can use `.originalCache` to specify a cache or the original
  221. /// images if necessary.
  222. ///
  223. /// The original image will be only cached to disk storage.
  224. ///
  225. public func cacheOriginalImage(_ enabled: Bool = true) -> Self {
  226. options.cacheOriginalImage = enabled
  227. return self
  228. }
  229. /// Sets whether the disk storage loading should happen in the same calling queue.
  230. /// - Parameter enabled: Whether the disk storage loading should happen in the same calling queue.
  231. /// - Returns: A `Self` value with changes applied.
  232. ///
  233. /// By default, disk storage file loading
  234. /// happens in its own queue with an asynchronous dispatch behavior. Although it provides better non-blocking disk
  235. /// loading performance, it also causes a flickering when you reload an image from disk, if the image view already
  236. /// has an image set.
  237. ///
  238. /// Set this options will stop that flickering by keeping all loading in the same queue (typically the UI queue
  239. /// if you are using Kingfisher's extension methods to set an image), with a tradeoff of loading performance.
  240. ///
  241. public func loadDiskFileSynchronously(_ enabled: Bool = true) -> Self {
  242. options.loadDiskFileSynchronously = enabled
  243. return self
  244. }
  245. /// Sets a queue on which the image processing should happen.
  246. /// - Parameter queue: The queue on which the image processing should happen.
  247. /// - Returns: A `Self` value with changes applied.
  248. ///
  249. /// By default, Kingfisher uses a pre-defined serial
  250. /// queue to process images. Use this option to change this behavior. For example, specify a `.mainCurrentOrAsync`
  251. /// to let the image be processed in main queue to prevent a possible flickering (but with a possibility of
  252. /// blocking the UI, especially if the processor needs a lot of time to run).
  253. public func processingQueue(_ queue: CallbackQueue?) -> Self {
  254. options.processingQueue = queue
  255. return self
  256. }
  257. /// Sets the alternative sources that will be used when loading of the original input `Source` fails.
  258. /// - Parameter sources: The alternative sources will be used.
  259. /// - Returns: A `Self` value with changes applied.
  260. ///
  261. /// Values of the `sources` array will be used to start a new image loading task if the previous task
  262. /// fails due to an error. The image source loading process will stop as soon as a source is loaded successfully.
  263. /// If all `sources` are used but the loading is still failing, an `imageSettingError` with
  264. /// `alternativeSourcesExhausted` as its reason will be given out in the `catch` block.
  265. ///
  266. /// This is useful if you want to implement a fallback solution for setting image.
  267. ///
  268. /// User cancellation will not trigger the alternative source loading.
  269. public func alternativeSources(_ sources: [Source]?) -> Self {
  270. options.alternativeSources = sources
  271. return self
  272. }
  273. /// Sets a retry strategy that will be used when something gets wrong during the image retrieving.
  274. /// - Parameter strategy: The provided strategy to define how the retrying should happen.
  275. /// - Returns: A `Self` value with changes applied.
  276. public func retry(_ strategy: RetryStrategy) -> Self {
  277. options.retryStrategy = strategy
  278. return self
  279. }
  280. /// Sets a retry strategy with a max retry count and retrying interval.
  281. /// - Parameters:
  282. /// - maxCount: The maximum count before the retry stops.
  283. /// - interval: The time interval between each retry attempt.
  284. /// - Returns: A `Self` value with changes applied.
  285. ///
  286. /// This defines the simplest retry strategy, which retry a failing request for several times, with some certain
  287. /// interval between each time. For example, `.retry(maxCount: 3, interval: .second(3))` means attempt for at most
  288. /// three times, and wait for 3 seconds if a previous retry attempt fails, then start a new attempt.
  289. public func retry(maxCount: Int, interval: DelayRetryStrategy.Interval = .seconds(3)) -> Self {
  290. let strategy = DelayRetryStrategy(maxRetryCount: maxCount, retryInterval: interval)
  291. options.retryStrategy = strategy
  292. return self
  293. }
  294. }
  295. // MARK: - Request Modifier
  296. extension KFOptionSetter {
  297. /// Sets an `ImageDownloadRequestModifier` to change the image download request before it being sent.
  298. /// - Parameter modifier: The modifier will be used to change the request before it being sent.
  299. /// - Returns: A `Self` value with changes applied.
  300. ///
  301. /// This is the last chance you can modify the image download request. You can modify the request for some
  302. /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping.
  303. ///
  304. public func requestModifier(_ modifier: ImageDownloadRequestModifier) -> Self {
  305. options.requestModifier = modifier
  306. return self
  307. }
  308. /// Sets a block to change the image download request before it being sent.
  309. /// - Parameter modifyBlock: The modifying block will be called to change the request before it being sent.
  310. /// - Returns: A `Self` value with changes applied.
  311. ///
  312. /// This is the last chance you can modify the image download request. You can modify the request for some
  313. /// customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url mapping.
  314. ///
  315. public func requestModifier(_ modifyBlock: @escaping (inout URLRequest) -> Void) -> Self {
  316. options.requestModifier = AnyModifier { r -> URLRequest? in
  317. var request = r
  318. modifyBlock(&request)
  319. return request
  320. }
  321. return self
  322. }
  323. }
  324. // MARK: - Redirect Handler
  325. extension KFOptionSetter {
  326. /// The `ImageDownloadRedirectHandler` argument will be used to change the request before redirection.
  327. /// This is the possibility you can modify the image download request during redirect. You can modify the request for
  328. /// some customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url
  329. /// mapping.
  330. /// The original redirection request will be sent without any modification by default.
  331. /// - Parameter handler: The handler will be used for redirection.
  332. /// - Returns: A `Self` value with changes applied.
  333. public func redirectHandler(_ handler: ImageDownloadRedirectHandler) -> Self {
  334. options.redirectHandler = handler
  335. return self
  336. }
  337. /// The `block` will be used to change the request before redirection.
  338. /// This is the possibility you can modify the image download request during redirect. You can modify the request for
  339. /// some customizing purpose, such as adding auth token to the header, do basic HTTP auth or something like url
  340. /// mapping.
  341. /// The original redirection request will be sent without any modification by default.
  342. /// - Parameter block: The block will be used for redirection.
  343. /// - Returns: A `Self` value with changes applied.
  344. public func redirectHandler(_ block: @escaping (KF.RedirectPayload) -> Void) -> Self {
  345. let redirectHandler = AnyRedirectHandler { (task, response, request, handler) in
  346. let payload = KF.RedirectPayload(
  347. task: task, response: response, newRequest: request, completionHandler: handler
  348. )
  349. block(payload)
  350. }
  351. options.redirectHandler = redirectHandler
  352. return self
  353. }
  354. }
  355. // MARK: - Processor
  356. extension KFOptionSetter {
  357. /// Sets an image processor for the image task. It replaces the current image processor settings.
  358. ///
  359. /// - Parameter processor: The processor you want to use to process the image after it is downloaded.
  360. /// - Returns: A `Self` value with changes applied.
  361. ///
  362. /// - Note:
  363. /// To append a processor to current ones instead of replacing them all, use `appendProcessor(_:)`.
  364. public func setProcessor(_ processor: ImageProcessor) -> Self {
  365. options.processor = processor
  366. return self
  367. }
  368. /// Sets an array of image processors for the image task. It replaces the current image processor settings.
  369. /// - Parameter processors: An array of processors. The processors inside this array will be concatenated one by one
  370. /// to form a processor pipeline.
  371. /// - Returns: A `Self` value with changes applied.
  372. ///
  373. /// - Note:
  374. /// To append processors to current ones instead of replacing them all, concatenate them by `|>`, then use
  375. /// `appendProcessor(_:)`.
  376. public func setProcessors(_ processors: [ImageProcessor]) -> Self {
  377. switch processors.count {
  378. case 0:
  379. options.processor = DefaultImageProcessor.default
  380. case 1...:
  381. options.processor = processors.dropFirst().reduce(processors[0]) { $0 |> $1 }
  382. default:
  383. assertionFailure("Never happen")
  384. }
  385. return self
  386. }
  387. /// Appends a processor to the current set processors.
  388. /// - Parameter processor: The processor which will be appended to current processor settings.
  389. /// - Returns: A `Self` value with changes applied.
  390. public func appendProcessor(_ processor: ImageProcessor) -> Self {
  391. options.processor = options.processor |> processor
  392. return self
  393. }
  394. /// Appends a `RoundCornerImageProcessor` to current processors.
  395. /// - Parameters:
  396. /// - radius: The radius will be applied in processing. Specify a certain point value with `.point`, or a fraction
  397. /// of the target image with `.widthFraction`. or `.heightFraction`. For example, given a square image
  398. /// with width and height equals, `.widthFraction(0.5)` means use half of the length of size and makes
  399. /// the final image a round one.
  400. /// - targetSize: Target size of output image should be. If `nil`, the image will keep its original size after processing.
  401. /// - corners: The target corners which will be applied rounding.
  402. /// - backgroundColor: Background color of the output image. If `nil`, it will use a transparent background.
  403. /// - Returns: A `Self` value with changes applied.
  404. public func roundCorner(
  405. radius: RoundCornerImageProcessor.Radius,
  406. targetSize: CGSize? = nil,
  407. roundingCorners corners: RectCorner = .all,
  408. backgroundColor: KFCrossPlatformColor? = nil
  409. ) -> Self
  410. {
  411. let processor = RoundCornerImageProcessor(
  412. radius: radius,
  413. targetSize: targetSize,
  414. roundingCorners: corners,
  415. backgroundColor: backgroundColor
  416. )
  417. return appendProcessor(processor)
  418. }
  419. /// Appends a `BlurImageProcessor` to current processors.
  420. /// - Parameter radius: Blur radius for the simulated Gaussian blur.
  421. /// - Returns: A `Self` value with changes applied.
  422. public func blur(radius: CGFloat) -> Self {
  423. appendProcessor(
  424. BlurImageProcessor(blurRadius: radius)
  425. )
  426. }
  427. /// Appends a `OverlayImageProcessor` to current processors.
  428. /// - Parameters:
  429. /// - color: Overlay color will be used to overlay the input image.
  430. /// - fraction: Fraction will be used when overlay the color to image.
  431. /// - Returns: A `Self` value with changes applied.
  432. public func overlay(color: KFCrossPlatformColor, fraction: CGFloat = 0.5) -> Self {
  433. appendProcessor(
  434. OverlayImageProcessor(overlay: color, fraction: fraction)
  435. )
  436. }
  437. /// Appends a `TintImageProcessor` to current processors.
  438. /// - Parameter color: Tint color will be used to tint the input image.
  439. /// - Returns: A `Self` value with changes applied.
  440. public func tint(color: KFCrossPlatformColor) -> Self {
  441. appendProcessor(
  442. TintImageProcessor(tint: color)
  443. )
  444. }
  445. /// Appends a `BlackWhiteProcessor` to current processors.
  446. /// - Returns: A `Self` value with changes applied.
  447. public func blackWhite() -> Self {
  448. appendProcessor(
  449. BlackWhiteProcessor()
  450. )
  451. }
  452. /// Appends a `CroppingImageProcessor` to current processors.
  453. /// - Parameters:
  454. /// - size: Target size of output image should be.
  455. /// - anchor: Anchor point from which the output size should be calculate. The anchor point is consisted by two
  456. /// values between 0.0 and 1.0. It indicates a related point in current image.
  457. /// See `CroppingImageProcessor.init(size:anchor:)` for more.
  458. /// - Returns: A `Self` value with changes applied.
  459. public func cropping(size: CGSize, anchor: CGPoint = .init(x: 0.5, y: 0.5)) -> Self {
  460. appendProcessor(
  461. CroppingImageProcessor(size: size, anchor: anchor)
  462. )
  463. }
  464. /// Appends a `DownsamplingImageProcessor` to current processors.
  465. ///
  466. /// Compared to `ResizingImageProcessor`, the `DownsamplingImageProcessor` does not render the original images and
  467. /// then resize it. Instead, it downsamples the input data directly to a thumbnail image. So it is a more efficient
  468. /// than `ResizingImageProcessor`. Prefer to use `DownsamplingImageProcessor` as possible
  469. /// as you can than the `ResizingImageProcessor`.
  470. ///
  471. /// Only CG-based images are supported. Animated images (like GIF) is not supported.
  472. ///
  473. /// - Parameter size: Target size of output image should be. It should be smaller than the size of input image.
  474. /// If it is larger, the result image will be the same size of input data without downsampling.
  475. /// - Returns: A `Self` value with changes applied.
  476. public func downsampling(size: CGSize) -> Self {
  477. let processor = DownsamplingImageProcessor(size: size)
  478. if options.processor == DefaultImageProcessor.default {
  479. return setProcessor(processor)
  480. } else {
  481. return appendProcessor(processor)
  482. }
  483. }
  484. /// Appends a `ResizingImageProcessor` to current processors.
  485. ///
  486. /// If you need to resize a data represented image to a smaller size, use `DownsamplingImageProcessor`
  487. /// instead, which is more efficient and uses less memory.
  488. ///
  489. /// - Parameters:
  490. /// - referenceSize: The reference size for resizing operation in point.
  491. /// - mode: Target content mode of output image should be. Default is `.none`.
  492. /// - Returns: A `Self` value with changes applied.
  493. public func resizing(referenceSize: CGSize, mode: ContentMode = .none) -> Self {
  494. appendProcessor(
  495. ResizingImageProcessor(referenceSize: referenceSize, mode: mode)
  496. )
  497. }
  498. }
  499. // MARK: - Cache Serializer
  500. extension KFOptionSetter {
  501. /// Uses a given `CacheSerializer` to convert some data to an image object for retrieving from disk cache or vice
  502. /// versa for storing to disk cache.
  503. /// - Parameter cacheSerializer: The `CacheSerializer` which will be used.
  504. /// - Returns: A `Self` value with changes applied.
  505. public func serialize(by cacheSerializer: CacheSerializer) -> Self {
  506. options.cacheSerializer = cacheSerializer
  507. return self
  508. }
  509. /// Uses a given format to serializer the image data to disk. It converts the image object to the give data format.
  510. /// - Parameters:
  511. /// - format: The desired data encoding format when store the image on disk.
  512. /// - jpegCompressionQuality: If the format is `.JPEG`, it specify the compression quality when converting the
  513. /// image to a JPEG data. Otherwise, it is ignored.
  514. /// - Returns: A `Self` value with changes applied.
  515. public func serialize(as format: ImageFormat, jpegCompressionQuality: CGFloat? = nil) -> Self {
  516. let cacheSerializer: FormatIndicatedCacheSerializer
  517. switch format {
  518. case .JPEG:
  519. cacheSerializer = .jpeg(compressionQuality: jpegCompressionQuality ?? 1.0)
  520. case .PNG:
  521. cacheSerializer = .png
  522. case .GIF:
  523. cacheSerializer = .gif
  524. case .unknown:
  525. cacheSerializer = .png
  526. }
  527. options.cacheSerializer = cacheSerializer
  528. return self
  529. }
  530. }
  531. // MARK: - Image Modifier
  532. extension KFOptionSetter {
  533. /// Sets an `ImageModifier` to the image task. Use this to modify the fetched image object properties if needed.
  534. ///
  535. /// If the image was fetched directly from the downloader, the modifier will run directly after the
  536. /// `ImageProcessor`. If the image is being fetched from a cache, the modifier will run after the `CacheSerializer`.
  537. /// - Parameter modifier: The `ImageModifier` which will be used to modify the image object.
  538. /// - Returns: A `Self` value with changes applied.
  539. public func imageModifier(_ modifier: ImageModifier?) -> Self {
  540. options.imageModifier = modifier
  541. return self
  542. }
  543. /// Sets a block to modify the image object. Use this to modify the fetched image object properties if needed.
  544. ///
  545. /// If the image was fetched directly from the downloader, the modifier block will run directly after the
  546. /// `ImageProcessor`. If the image is being fetched from a cache, the modifier will run after the `CacheSerializer`.
  547. ///
  548. /// - Parameter block: The block which is used to modify the image object.
  549. /// - Returns: A `Self` value with changes applied.
  550. public func imageModifier(_ block: @escaping (inout KFCrossPlatformImage) throws -> Void) -> Self {
  551. let modifier = AnyImageModifier { image -> KFCrossPlatformImage in
  552. var image = image
  553. try block(&image)
  554. return image
  555. }
  556. options.imageModifier = modifier
  557. return self
  558. }
  559. }
  560. // MARK: - Cache Expiration
  561. extension KFOptionSetter {
  562. /// Sets the expiration setting for memory cache of this image task.
  563. ///
  564. /// By default, the underlying `MemoryStorage.Backend` uses the
  565. /// expiration in its config for all items. If set, the `MemoryStorage.Backend` will use this value to overwrite
  566. /// the config setting for this caching item.
  567. ///
  568. /// - Parameter expiration: The expiration setting used in cache storage.
  569. /// - Returns: A `Self` value with changes applied.
  570. public func memoryCacheExpiration(_ expiration: StorageExpiration?) -> Self {
  571. options.memoryCacheExpiration = expiration
  572. return self
  573. }
  574. /// Sets the expiration extending setting for memory cache. The item expiration time will be incremented by this
  575. /// value after access.
  576. ///
  577. /// By default, the underlying `MemoryStorage.Backend` uses the initial cache expiration as extending
  578. /// value: .cacheTime.
  579. ///
  580. /// To disable extending option at all, sets `.none` to it.
  581. ///
  582. /// - Parameter extending: The expiration extending setting used in cache storage.
  583. /// - Returns: A `Self` value with changes applied.
  584. public func memoryCacheAccessExtending(_ extending: ExpirationExtending) -> Self {
  585. options.memoryCacheAccessExtendingExpiration = extending
  586. return self
  587. }
  588. /// Sets the expiration setting for disk cache of this image task.
  589. ///
  590. /// By default, the underlying `DiskStorage.Backend` uses the expiration in its config for all items. If set,
  591. /// the `DiskStorage.Backend` will use this value to overwrite the config setting for this caching item.
  592. ///
  593. /// - Parameter expiration: The expiration setting used in cache storage.
  594. /// - Returns: A `Self` value with changes applied.
  595. public func diskCacheExpiration(_ expiration: StorageExpiration?) -> Self {
  596. options.diskCacheExpiration = expiration
  597. return self
  598. }
  599. /// Sets the expiration extending setting for disk cache. The item expiration time will be incremented by this
  600. /// value after access.
  601. ///
  602. /// By default, the underlying `DiskStorage.Backend` uses the initial cache expiration as extending
  603. /// value: .cacheTime.
  604. ///
  605. /// To disable extending option at all, sets `.none` to it.
  606. ///
  607. /// - Parameter extending: The expiration extending setting used in cache storage.
  608. /// - Returns: A `Self` value with changes applied.
  609. public func diskCacheAccessExtending(_ extending: ExpirationExtending) -> Self {
  610. options.diskCacheAccessExtendingExpiration = extending
  611. return self
  612. }
  613. }