ImageProgressive.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. //
  2. // ImageProgressive.swift
  3. // Kingfisher
  4. //
  5. // Created by lixiang on 2019/5/10.
  6. //
  7. // Copyright (c) 2019 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. private let sharedProcessingQueue: CallbackQueue =
  29. .dispatch(DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process"))
  30. public struct ImageProgressive {
  31. /// A default `ImageProgressive` could be used across.
  32. public static let `default` = ImageProgressive(
  33. isBlur: true,
  34. isFastestScan: true,
  35. scanInterval: 0
  36. )
  37. /// Whether to enable blur effect processing
  38. let isBlur: Bool
  39. /// Whether to enable the fastest scan
  40. let isFastestScan: Bool
  41. /// Minimum time interval for each scan
  42. let scanInterval: TimeInterval
  43. public init(isBlur: Bool,
  44. isFastestScan: Bool,
  45. scanInterval: TimeInterval) {
  46. self.isBlur = isBlur
  47. self.isFastestScan = isFastestScan
  48. self.scanInterval = scanInterval
  49. }
  50. }
  51. protocol ImageSettable: AnyObject {
  52. var image: Image? { get set }
  53. }
  54. final class ImageProgressiveProvider: DataReceivingSideEffect {
  55. var onShouldApply: () -> Bool = { return true }
  56. func onDataReceived(_ session: URLSession, task: SessionDataTask, data: Data) {
  57. update(data: task.mutableData, with: task.callbacks)
  58. }
  59. private let option: ImageProgressive
  60. private let refresh: (Image) -> Void
  61. private let decoder: ImageProgressiveDecoder
  62. private let queue = ImageProgressiveSerialQueue(.main)
  63. init?(_ options: KingfisherParsedOptionsInfo,
  64. refresh: @escaping (Image) -> Void) {
  65. guard let option = options.progressiveJPEG else { return nil }
  66. self.option = option
  67. self.refresh = refresh
  68. self.decoder = ImageProgressiveDecoder(
  69. option,
  70. processingQueue: options.processingQueue ?? sharedProcessingQueue,
  71. creatingOptions: options.imageCreatingOptions
  72. )
  73. }
  74. func update(data: Data, with callbacks: [SessionDataTask.TaskCallback]) {
  75. guard !data.isEmpty else { return }
  76. let interval = option.scanInterval
  77. let isFastest = option.isFastestScan
  78. func add(decode data: Data) {
  79. queue.add(minimum: interval) { completion in
  80. guard self.onShouldApply() else {
  81. self.queue.clean()
  82. completion()
  83. return
  84. }
  85. self.decoder.decode(data, with: callbacks) { image in
  86. defer { completion() }
  87. guard self.onShouldApply() else { return }
  88. guard let image = image else { return }
  89. self.refresh(image)
  90. }
  91. }
  92. }
  93. if isFastest {
  94. add(decode: decoder.scanning(data) ?? Data())
  95. } else {
  96. decoder.scanning(data).forEach { add(decode: $0) }
  97. }
  98. }
  99. }
  100. private final class ImageProgressiveDecoder {
  101. private let option: ImageProgressive
  102. private let processingQueue: CallbackQueue
  103. private let creatingOptions: ImageCreatingOptions
  104. private(set) var scannedCount = 0
  105. private(set) var scannedIndex = -1
  106. init(_ option: ImageProgressive,
  107. processingQueue: CallbackQueue,
  108. creatingOptions: ImageCreatingOptions) {
  109. self.option = option
  110. self.processingQueue = processingQueue
  111. self.creatingOptions = creatingOptions
  112. }
  113. func scanning(_ data: Data) -> [Data] {
  114. guard data.kf.contains(jpeg: .SOF2) else {
  115. return []
  116. }
  117. guard scannedIndex + 1 < data.count else {
  118. return []
  119. }
  120. var datas: [Data] = []
  121. var index = scannedIndex + 1
  122. var count = scannedCount
  123. while index < data.count - 1 {
  124. scannedIndex = index
  125. // 0xFF, 0xDA - Start Of Scan
  126. let SOS = ImageFormat.JPEGMarker.SOS.bytes
  127. if data[index] == SOS[0], data[index + 1] == SOS[1] {
  128. if count > 0 {
  129. datas.append(data[0 ..< index])
  130. }
  131. count += 1
  132. }
  133. index += 1
  134. }
  135. // Found more scans this the previous time
  136. guard count > scannedCount else { return [] }
  137. scannedCount = count
  138. // `> 1` checks that we've received a first scan (SOS) and then received
  139. // and also received a second scan (SOS). This way we know that we have
  140. // at least one full scan available.
  141. guard count > 1 else { return [] }
  142. return datas
  143. }
  144. func scanning(_ data: Data) -> Data? {
  145. guard data.kf.contains(jpeg: .SOF2) else {
  146. return nil
  147. }
  148. guard scannedIndex + 1 < data.count else {
  149. return nil
  150. }
  151. var index = scannedIndex + 1
  152. var count = scannedCount
  153. var lastSOSIndex = 0
  154. while index < data.count - 1 {
  155. scannedIndex = index
  156. // 0xFF, 0xDA - Start Of Scan
  157. let SOS = ImageFormat.JPEGMarker.SOS.bytes
  158. if data[index] == SOS[0], data[index + 1] == SOS[1] {
  159. lastSOSIndex = index
  160. count += 1
  161. }
  162. index += 1
  163. }
  164. // Found more scans this the previous time
  165. guard count > scannedCount else { return nil }
  166. scannedCount = count
  167. // `> 1` checks that we've received a first scan (SOS) and then received
  168. // and also received a second scan (SOS). This way we know that we have
  169. // at least one full scan available.
  170. guard count > 1 && lastSOSIndex > 0 else { return nil }
  171. return data[0 ..< lastSOSIndex]
  172. }
  173. func decode(_ data: Data,
  174. with callbacks: [SessionDataTask.TaskCallback],
  175. completion: @escaping (Image?) -> Void) {
  176. guard data.kf.contains(jpeg: .SOF2) else {
  177. CallbackQueue.mainCurrentOrAsync.execute { completion(nil) }
  178. return
  179. }
  180. func processing(_ data: Data) {
  181. let processor = ImageDataProcessor(
  182. data: data,
  183. callbacks: callbacks,
  184. processingQueue: processingQueue
  185. )
  186. processor.onImageProcessed.delegate(on: self) { (self, result) in
  187. guard let image = try? result.0.get() else {
  188. CallbackQueue.mainCurrentOrAsync.execute { completion(nil) }
  189. return
  190. }
  191. CallbackQueue.mainCurrentOrAsync.execute { completion(image) }
  192. }
  193. processor.process()
  194. }
  195. // Blur partial images.
  196. let count = scannedCount
  197. if option.isBlur, count < 6 {
  198. processingQueue.execute {
  199. // Progressively reduce blur as we load more scans.
  200. let image = KingfisherWrapper<Image>.image(
  201. data: data,
  202. options: self.creatingOptions
  203. )
  204. let radius = max(2, 14 - count * 4)
  205. let temp = image?.kf.blurred(withRadius: CGFloat(radius))
  206. processing(temp?.kf.data(format: .JPEG) ?? data)
  207. }
  208. } else {
  209. processing(data)
  210. }
  211. }
  212. }
  213. private final class ImageProgressiveSerialQueue {
  214. typealias ClosureCallback = ((@escaping () -> Void)) -> Void
  215. private let queue: DispatchQueue
  216. private var items: [DispatchWorkItem] = []
  217. private var notify: (() -> Void)?
  218. private var lastTime: TimeInterval?
  219. var count: Int { return items.count }
  220. init(_ queue: DispatchQueue) {
  221. self.queue = queue
  222. }
  223. func add(minimum interval: TimeInterval, closure: @escaping ClosureCallback) {
  224. let completion = {
  225. self.queue.async {
  226. guard !self.items.isEmpty else { return }
  227. self.items.removeFirst()
  228. if let next = self.items.first {
  229. self.queue.asyncAfter(
  230. deadline: .now() + interval,
  231. execute: next
  232. )
  233. } else {
  234. self.lastTime = Date().timeIntervalSince1970
  235. self.notify?()
  236. self.notify = nil
  237. }
  238. }
  239. }
  240. let item = DispatchWorkItem {
  241. closure(completion)
  242. }
  243. if items.isEmpty {
  244. let difference = Date().timeIntervalSince1970 - (lastTime ?? 0)
  245. let delay = difference < interval ? interval - difference : 0
  246. queue.asyncAfter(deadline: .now() + delay, execute: item)
  247. }
  248. items.append(item)
  249. }
  250. func notify(_ closure: @escaping () -> Void) {
  251. self.notify = closure
  252. }
  253. func clean() {
  254. items.forEach { $0.cancel() }
  255. items.removeAll()
  256. }
  257. }