added iOS source code
[wl-app.git] / iOS / Pods / Kingfisher / Sources / ImageProcessor.swift
1 //
2 //  ImageProcessor.swift
3 //  Kingfisher
4 //
5 //  Created by Wei Wang on 2016/08/26.
6 //
7 //  Copyright (c) 2018 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
27 import Foundation
28 import CoreGraphics
29
30 #if os(macOS)
31 import AppKit
32 #endif
33
34 /// The item which could be processed by an `ImageProcessor`
35 ///
36 /// - image: Input image
37 /// - data:  Input data
38 public enum ImageProcessItem {
39     case image(Image)
40     case data(Data)
41 }
42
43 /// An `ImageProcessor` would be used to convert some downloaded data to an image.
44 public protocol ImageProcessor {
45     /// Identifier of the processor. It will be used to identify the processor when 
46     /// caching and retrieving an image. You might want to make sure that processors with
47     /// same properties/functionality have the same identifiers, so correct processed images
48     /// could be retrived with proper key.
49     /// 
50     /// - Note: Do not supply an empty string for a customized processor, which is already taken by
51     /// the `DefaultImageProcessor`. It is recommended to use a reverse domain name notation
52     /// string of your own for the identifier.
53     var identifier: String { get }
54     
55     /// Process an input `ImageProcessItem` item to an image for this processor.
56     ///
57     /// - parameter item:    Input item which will be processed by `self`
58     /// - parameter options: Options when processing the item.
59     ///
60     /// - returns: The processed image.
61     ///
62     /// - Note: The return value will be `nil` if processing failed while converting data to image.
63     ///         If input item is already an image and there is any errors in processing, the input 
64     ///         image itself will be returned.
65     /// - Note: Most processor only supports CG-based images. 
66     ///         watchOS is not supported for processers containing filter, the input image will be returned directly on watchOS.
67     func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image?
68 }
69
70 typealias ProcessorImp = ((ImageProcessItem, KingfisherOptionsInfo) -> Image?)
71
72 public extension ImageProcessor {
73     
74     /// Append an `ImageProcessor` to another. The identifier of the new `ImageProcessor` 
75     /// will be "\(self.identifier)|>\(another.identifier)".
76     ///
77     /// - parameter another: An `ImageProcessor` you want to append to `self`.
78     ///
79     /// - returns: The new `ImageProcessor` will process the image in the order
80     ///            of the two processors concatenated.
81     public func append(another: ImageProcessor) -> ImageProcessor {
82         let newIdentifier = identifier.appending("|>\(another.identifier)")
83         return GeneralProcessor(identifier: newIdentifier) {
84             item, options in
85             if let image = self.process(item: item, options: options) {
86                 return another.process(item: .image(image), options: options)
87             } else {
88                 return nil
89             }
90         }
91     }
92 }
93
94 func ==(left: ImageProcessor, right: ImageProcessor) -> Bool {
95     return left.identifier == right.identifier
96 }
97
98 func !=(left: ImageProcessor, right: ImageProcessor) -> Bool {
99     return !(left == right)
100 }
101
102 fileprivate struct GeneralProcessor: ImageProcessor {
103     let identifier: String
104     let p: ProcessorImp
105     func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
106         return p(item, options)
107     }
108 }
109
110 /// The default processor. It convert the input data to a valid image.
111 /// Images of .PNG, .JPEG and .GIF format are supported.
112 /// If an image is given, `DefaultImageProcessor` will do nothing on it and just return that image.
113 public struct DefaultImageProcessor: ImageProcessor {
114     
115     /// A default `DefaultImageProcessor` could be used across.
116     public static let `default` = DefaultImageProcessor()
117     
118     /// Identifier of the processor.
119     /// - Note: See documentation of `ImageProcessor` protocol for more.
120     public let identifier = ""
121     
122     /// Initialize a `DefaultImageProcessor`
123     public init() {}
124     
125     /// Process an input `ImageProcessItem` item to an image for this processor.
126     ///
127     /// - parameter item:    Input item which will be processed by `self`
128     /// - parameter options: Options when processing the item.
129     ///
130     /// - returns: The processed image.
131     /// 
132     /// - Note: See documentation of `ImageProcessor` protocol for more.
133     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
134         switch item {
135         case .image(let image):
136             return image.kf.scaled(to: options.scaleFactor)
137         case .data(let data):
138             return Kingfisher<Image>.image(
139                 data: data,
140                 scale: options.scaleFactor,
141                 preloadAllAnimationData: options.preloadAllAnimationData,
142                 onlyFirstFrame: options.onlyLoadFirstFrame)
143         }
144     }
145 }
146
147 public struct RectCorner: OptionSet {
148     public let rawValue: Int
149     public static let topLeft = RectCorner(rawValue: 1 << 0)
150     public static let topRight = RectCorner(rawValue: 1 << 1)
151     public static let bottomLeft = RectCorner(rawValue: 1 << 2)
152     public static let bottomRight = RectCorner(rawValue: 1 << 3)
153     public static let all: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight]
154     
155     public init(rawValue: Int) {
156         self.rawValue = rawValue
157     }
158     
159     var cornerIdentifier: String {
160         if self == .all {
161             return ""
162         }
163         return "_corner(\(rawValue))"
164     }
165 }
166
167 #if !os(macOS)
168 /// Processor for adding an blend mode to images. Only CG-based images are supported.
169 public struct BlendImageProcessor: ImageProcessor {
170
171     /// Identifier of the processor.
172     /// - Note: See documentation of `ImageProcessor` protocol for more.
173     public let identifier: String
174
175     /// Blend Mode will be used to blend the input image.
176     public let blendMode: CGBlendMode
177     /// Alpha will be used when blend image.
178     public let alpha: CGFloat
179
180     /// Background color of the output image. If `nil`, it will stay transparent.
181     public let backgroundColor: Color?
182
183     /// Initialize an `BlendImageProcessor`
184     ///
185     /// - parameter blendMode:       Blend Mode will be used to blend the input image.
186     /// - parameter alpha:           Alpha will be used when blend image.
187     ///                              From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
188     ///                              Default is 1.0.
189     /// - parameter backgroundColor: Backgroud color to apply for the output image. Default is `nil`.
190     public init(blendMode: CGBlendMode, alpha: CGFloat = 1.0, backgroundColor: Color? = nil) {
191         self.blendMode = blendMode
192         self.alpha = alpha
193         self.backgroundColor = backgroundColor
194         var identifier = "com.onevcat.Kingfisher.BlendImageProcessor(\(blendMode.rawValue),\(alpha))"
195         if let color = backgroundColor {
196             identifier.append("_\(color.hex)")
197         }
198         self.identifier = identifier
199     }
200
201     /// Process an input `ImageProcessItem` item to an image for this processor.
202     ///
203     /// - parameter item:    Input item which will be processed by `self`
204     /// - parameter options: Options when processing the item.
205     ///
206     /// - returns: The processed image.
207     ///
208     /// - Note: See documentation of `ImageProcessor` protocol for more.
209     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
210         switch item {
211         case .image(let image):
212             return image.kf.scaled(to: options.scaleFactor)
213                         .kf.image(withBlendMode: blendMode, alpha: alpha, backgroundColor: backgroundColor)
214         case .data(_):
215             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
216         }
217     }
218 }
219 #endif
220
221 #if os(macOS)
222 /// Processor for adding an compositing operation to images. Only CG-based images are supported in macOS.
223 public struct CompositingImageProcessor: ImageProcessor {
224
225     /// Identifier of the processor.
226     /// - Note: See documentation of `ImageProcessor` protocol for more.
227     public let identifier: String
228
229     /// Compositing operation will be used to the input image.
230     public let compositingOperation: NSCompositingOperation
231
232     /// Alpha will be used when compositing image.
233     public let alpha: CGFloat
234
235     /// Background color of the output image. If `nil`, it will stay transparent.
236     public let backgroundColor: Color?
237
238     /// Initialize an `CompositingImageProcessor`
239     ///
240     /// - parameter compositingOperation: Compositing operation will be used to the input image.
241     /// - parameter alpha:                Alpha will be used when compositing image.
242     ///                                   From 0.0 to 1.0. 1.0 means solid image, 0.0 means transparent image.
243     ///                                   Default is 1.0.
244     /// - parameter backgroundColor:      Backgroud color to apply for the output image. Default is `nil`.
245     public init(compositingOperation: NSCompositingOperation,
246                 alpha: CGFloat = 1.0,
247                 backgroundColor: Color? = nil)
248     {
249         self.compositingOperation = compositingOperation
250         self.alpha = alpha
251         self.backgroundColor = backgroundColor
252         var identifier = "com.onevcat.Kingfisher.CompositingImageProcessor(\(compositingOperation.rawValue),\(alpha))"
253         if let color = backgroundColor {
254             identifier.append("_\(color.hex)")
255         }
256         self.identifier = identifier
257     }
258
259     /// Process an input `ImageProcessItem` item to an image for this processor.
260     ///
261     /// - parameter item:    Input item which will be processed by `self`
262     /// - parameter options: Options when processing the item.
263     ///
264     /// - returns: The processed image.
265     ///
266     /// - Note: See documentation of `ImageProcessor` protocol for more.
267     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
268         switch item {
269         case .image(let image):
270             return image.kf.scaled(to: options.scaleFactor)
271                         .kf.image(withCompositingOperation: compositingOperation, alpha: alpha, backgroundColor: backgroundColor)
272         case .data(_):
273             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
274         }
275     }
276 }
277 #endif
278
279 /// Processor for making round corner images. Only CG-based images are supported in macOS, 
280 /// if a non-CG image passed in, the processor will do nothing.
281 public struct RoundCornerImageProcessor: ImageProcessor {
282     
283     /// Identifier of the processor.
284     /// - Note: See documentation of `ImageProcessor` protocol for more.
285     public let identifier: String
286
287     /// Corner radius will be applied in processing.
288     public let cornerRadius: CGFloat
289     
290     /// The target corners which will be applied rounding.
291     public let roundingCorners: RectCorner
292     
293     /// Target size of output image should be. If `nil`, the image will keep its original size after processing.
294     public let targetSize: CGSize?
295
296     /// Background color of the output image. If `nil`, it will stay transparent.
297     public let backgroundColor: Color?
298
299     /// Initialize a `RoundCornerImageProcessor`
300     ///
301     /// - parameter cornerRadius:    Corner radius will be applied in processing.
302     /// - parameter targetSize:      Target size of output image should be. If `nil`, 
303     ///                              the image will keep its original size after processing.
304     ///                              Default is `nil`.
305     /// - parameter corners:         The target corners which will be applied rounding. Default is `.all`.
306     /// - parameter backgroundColor: Backgroud color to apply for the output image. Default is `nil`.
307     public init(cornerRadius: CGFloat, targetSize: CGSize? = nil, roundingCorners corners: RectCorner = .all, backgroundColor: Color? = nil) {
308         self.cornerRadius = cornerRadius
309         self.targetSize = targetSize
310         self.roundingCorners = corners
311         self.backgroundColor = backgroundColor
312
313         self.identifier = {
314             var identifier = ""
315
316             if let size = targetSize {
317                 identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size)\(corners.cornerIdentifier))"
318             } else {
319                 identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)\(corners.cornerIdentifier))"
320             }
321             if let backgroundColor = backgroundColor {
322                 identifier += "_\(backgroundColor)"
323             }
324
325             return identifier
326         }()
327     }
328     
329     /// Process an input `ImageProcessItem` item to an image for this processor.
330     ///
331     /// - parameter item:    Input item which will be processed by `self`
332     /// - parameter options: Options when processing the item.
333     ///
334     /// - returns: The processed image.
335     ///
336     /// - Note: See documentation of `ImageProcessor` protocol for more.
337     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
338         switch item {
339         case .image(let image):
340             let size = targetSize ?? image.kf.size
341             return image.kf.scaled(to: options.scaleFactor)
342                         .kf.image(withRoundRadius: cornerRadius, fit: size, roundingCorners: roundingCorners, backgroundColor: backgroundColor)
343         case .data(_):
344             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
345         }
346     }
347 }
348
349
350 /// Specify how a size adjusts itself to fit a target size.
351 ///
352 /// - none: Not scale the content.
353 /// - aspectFit: Scale the content to fit the size of the view by maintaining the aspect ratio.
354 /// - aspectFill: Scale the content to fill the size of the view
355 public enum ContentMode {
356     case none
357     case aspectFit
358     case aspectFill
359 }
360
361 /// Processor for resizing images. Only CG-based images are supported in macOS.
362 public struct ResizingImageProcessor: ImageProcessor {
363     
364     /// Identifier of the processor.
365     /// - Note: See documentation of `ImageProcessor` protocol for more.
366     public let identifier: String
367     
368     /// The reference size for resizing operation.
369     public let referenceSize: CGSize
370     
371     /// Target content mode of output image should be.
372     /// Default to ContentMode.none
373     public let targetContentMode: ContentMode
374     
375     /// Initialize a `ResizingImageProcessor`.
376     ///
377     /// - Parameters:
378     ///   - referenceSize: The reference size for resizing operation.
379     ///   - mode: Target content mode of output image should be.
380     ///
381     /// - Note:
382     ///   The instance of `ResizingImageProcessor` will follow its `mode` property
383     ///   and try to resizing the input images to fit or fill the `referenceSize`.
384     ///   That means if you are using a `mode` besides of `.none`, you may get an
385     ///   image with its size not be the same as the `referenceSize`.
386     ///
387     ///   **Example**: With input image size: {100, 200}, 
388     ///   `referenceSize`: {100, 100}, `mode`: `.aspectFit`,
389     ///   you will get an output image with size of {50, 100}, which "fit"s
390     ///   the `referenceSize`.
391     ///
392     ///   If you need an output image exactly to be a specified size, append or use
393     ///   a `CroppingImageProcessor`.
394     public init(referenceSize: CGSize, mode: ContentMode = .none) {
395         self.referenceSize = referenceSize
396         self.targetContentMode = mode
397         
398         if mode == .none {
399             self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize))"
400         } else {
401             self.identifier = "com.onevcat.Kingfisher.ResizingImageProcessor(\(referenceSize), \(mode))"
402         }
403     }
404     
405     /// Process an input `ImageProcessItem` item to an image for this processor.
406     ///
407     /// - parameter item:    Input item which will be processed by `self`
408     /// - parameter options: Options when processing the item.
409     ///
410     /// - returns: The processed image.
411     ///
412     /// - Note: See documentation of `ImageProcessor` protocol for more.
413     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
414         switch item {
415         case .image(let image):
416             return image.kf.scaled(to: options.scaleFactor)
417                         .kf.resize(to: referenceSize, for: targetContentMode)
418         case .data(_):
419             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
420         }
421     }
422 }
423
424 /// Processor for adding blur effect to images. `Accelerate.framework` is used underhood for 
425 /// a better performance. A simulated Gaussian blur with specified blur radius will be applied.
426 public struct BlurImageProcessor: ImageProcessor {
427     
428     /// Identifier of the processor.
429     /// - Note: See documentation of `ImageProcessor` protocol for more.
430     public let identifier: String
431     
432     /// Blur radius for the simulated Gaussian blur.
433     public let blurRadius: CGFloat
434
435     /// Initialize a `BlurImageProcessor`
436     ///
437     /// - parameter blurRadius: Blur radius for the simulated Gaussian blur.
438     public init(blurRadius: CGFloat) {
439         self.blurRadius = blurRadius
440         self.identifier = "com.onevcat.Kingfisher.BlurImageProcessor(\(blurRadius))"
441     }
442     
443     /// Process an input `ImageProcessItem` item to an image for this processor.
444     ///
445     /// - parameter item:    Input item which will be processed by `self`
446     /// - parameter options: Options when processing the item.
447     ///
448     /// - returns: The processed image.
449     ///
450     /// - Note: See documentation of `ImageProcessor` protocol for more.
451     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
452         switch item {
453         case .image(let image):
454             let radius = blurRadius * options.scaleFactor
455             return image.kf.scaled(to: options.scaleFactor)
456                         .kf.blurred(withRadius: radius)
457         case .data(_):
458             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
459         }
460     }
461 }
462
463 /// Processor for adding an overlay to images. Only CG-based images are supported in macOS.
464 public struct OverlayImageProcessor: ImageProcessor {
465     
466     /// Identifier of the processor.
467     /// - Note: See documentation of `ImageProcessor` protocol for more.
468     public let identifier: String
469     
470     /// Overlay color will be used to overlay the input image.
471     public let overlay: Color
472     
473     /// Fraction will be used when overlay the color to image.
474     public let fraction: CGFloat
475     
476     /// Initialize an `OverlayImageProcessor`
477     ///
478     /// - parameter overlay:  Overlay color will be used to overlay the input image.
479     /// - parameter fraction: Fraction will be used when overlay the color to image. 
480     ///                       From 0.0 to 1.0. 0.0 means solid color, 1.0 means transparent overlay.
481     public init(overlay: Color, fraction: CGFloat = 0.5) {
482         self.overlay = overlay
483         self.fraction = fraction
484         self.identifier = "com.onevcat.Kingfisher.OverlayImageProcessor(\(overlay.hex)_\(fraction))"
485     }
486     
487     /// Process an input `ImageProcessItem` item to an image for this processor.
488     ///
489     /// - parameter item:    Input item which will be processed by `self`
490     /// - parameter options: Options when processing the item.
491     ///
492     /// - returns: The processed image.
493     ///
494     /// - Note: See documentation of `ImageProcessor` protocol for more.
495     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
496         switch item {
497         case .image(let image):
498             return image.kf.scaled(to: options.scaleFactor)
499                         .kf.overlaying(with: overlay, fraction: fraction)
500         case .data(_):
501             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
502         }
503     }
504 }
505
506 /// Processor for tint images with color. Only CG-based images are supported.
507 public struct TintImageProcessor: ImageProcessor {
508     
509     /// Identifier of the processor.
510     /// - Note: See documentation of `ImageProcessor` protocol for more.
511     public let identifier: String
512     
513     /// Tint color will be used to tint the input image.
514     public let tint: Color
515     
516     /// Initialize a `TintImageProcessor`
517     ///
518     /// - parameter tint: Tint color will be used to tint the input image.
519     public init(tint: Color) {
520         self.tint = tint
521         self.identifier = "com.onevcat.Kingfisher.TintImageProcessor(\(tint.hex))"
522     }
523     
524     /// Process an input `ImageProcessItem` item to an image for this processor.
525     ///
526     /// - parameter item:    Input item which will be processed by `self`
527     /// - parameter options: Options when processing the item.
528     ///
529     /// - returns: The processed image.
530     ///
531     /// - Note: See documentation of `ImageProcessor` protocol for more.
532     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
533         switch item {
534         case .image(let image):
535             return image.kf.scaled(to: options.scaleFactor)
536                         .kf.tinted(with: tint)
537         case .data(_):
538             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
539         }
540     }
541 }
542
543 /// Processor for applying some color control to images. Only CG-based images are supported.
544 /// watchOS is not supported.
545 public struct ColorControlsProcessor: ImageProcessor {
546     
547     /// Identifier of the processor.
548     /// - Note: See documentation of `ImageProcessor` protocol for more.
549     public let identifier: String
550     
551     /// Brightness changing to image.
552     public let brightness: CGFloat
553     
554     /// Contrast changing to image.
555     public let contrast: CGFloat
556     
557     /// Saturation changing to image.
558     public let saturation: CGFloat
559     
560     /// InputEV changing to image.
561     public let inputEV: CGFloat
562     
563     /// Initialize a `ColorControlsProcessor`
564     ///
565     /// - parameter brightness: Brightness changing to image.
566     /// - parameter contrast:   Contrast changing to image.
567     /// - parameter saturation: Saturation changing to image.
568     /// - parameter inputEV:    InputEV changing to image.
569     public init(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) {
570         self.brightness = brightness
571         self.contrast = contrast
572         self.saturation = saturation
573         self.inputEV = inputEV
574         self.identifier = "com.onevcat.Kingfisher.ColorControlsProcessor(\(brightness)_\(contrast)_\(saturation)_\(inputEV))"
575     }
576     
577     /// Process an input `ImageProcessItem` item to an image for this processor.
578     ///
579     /// - parameter item:    Input item which will be processed by `self`
580     /// - parameter options: Options when processing the item.
581     ///
582     /// - returns: The processed image.
583     ///
584     /// - Note: See documentation of `ImageProcessor` protocol for more.
585     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
586         switch item {
587         case .image(let image):
588             return image.kf.scaled(to: options.scaleFactor)
589                         .kf.adjusted(brightness: brightness, contrast: contrast, saturation: saturation, inputEV: inputEV)
590         case .data(_):
591             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
592         }
593     }
594 }
595
596 /// Processor for applying black and white effect to images. Only CG-based images are supported.
597 /// watchOS is not supported.
598 public struct BlackWhiteProcessor: ImageProcessor {
599     
600     /// Identifier of the processor.
601     /// - Note: See documentation of `ImageProcessor` protocol for more.
602     public let identifier = "com.onevcat.Kingfisher.BlackWhiteProcessor"
603     
604     /// Initialize a `BlackWhiteProcessor`
605     public init() {}
606     
607     /// Process an input `ImageProcessItem` item to an image for this processor.
608     ///
609     /// - parameter item:    Input item which will be processed by `self`
610     /// - parameter options: Options when processing the item.
611     ///
612     /// - returns: The processed image.
613     ///
614     /// - Note: See documentation of `ImageProcessor` protocol for more.
615     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
616         return ColorControlsProcessor(brightness: 0.0, contrast: 1.0, saturation: 0.0, inputEV: 0.7)
617             .process(item: item, options: options)
618     }
619 }
620
621 /// Processor for cropping an image. Only CG-based images are supported.
622 /// watchOS is not supported.
623 public struct CroppingImageProcessor: ImageProcessor {
624     
625     /// Identifier of the processor.
626     /// - Note: See documentation of `ImageProcessor` protocol for more.
627     public let identifier: String
628     
629     /// Target size of output image should be.
630     public let size: CGSize
631     
632     /// Anchor point from which the output size should be calculate.
633     /// The anchor point is consisted by two values between 0.0 and 1.0.
634     /// It indicates a related point in current image. 
635     /// See `CroppingImageProcessor.init(size:anchor:)` for more.
636     public let anchor: CGPoint
637     
638     /// Initialize a `CroppingImageProcessor`
639     ///
640     /// - Parameters:
641     ///   - size: Target size of output image should be.
642     ///   - anchor: The anchor point from which the size should be calculated.
643     ///             Default is `CGPoint(x: 0.5, y: 0.5)`, which means the center of input image.
644     /// - Note:
645     ///   The anchor point is consisted by two values between 0.0 and 1.0.
646     ///   It indicates a related point in current image, eg: (0.0, 0.0) for top-left
647     ///   corner, (0.5, 0.5) for center and (1.0, 1.0) for bottom-right corner.
648     ///   The `size` property of `CroppingImageProcessor` will be used along with
649     ///   `anchor` to calculate a target rectange in the size of image.
650     ///    
651     ///   The target size will be automatically calculated with a reasonable behavior.
652     ///   For example, when you have an image size of `CGSize(width: 100, height: 100)`,
653     ///   and a target size of `CGSize(width: 20, height: 20)`: 
654     ///   - with a (0.0, 0.0) anchor (top-left), the crop rect will be `{0, 0, 20, 20}`; 
655     ///   - with a (0.5, 0.5) anchor (center), it will be `{40, 40, 20, 20}`
656     ///   - while with a (1.0, 1.0) anchor (bottom-right), it will be `{80, 80, 20, 20}`
657     public init(size: CGSize, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5)) {
658         self.size = size
659         self.anchor = anchor
660         self.identifier = "com.onevcat.Kingfisher.CroppingImageProcessor(\(size)_\(anchor))"
661     }
662     
663     /// Process an input `ImageProcessItem` item to an image for this processor.
664     ///
665     /// - parameter item:    Input item which will be processed by `self`
666     /// - parameter options: Options when processing the item.
667     ///
668     /// - returns: The processed image.
669     ///
670     /// - Note: See documentation of `ImageProcessor` protocol for more.
671     public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
672         switch item {
673         case .image(let image):
674             return image.kf.scaled(to: options.scaleFactor)
675                         .kf.crop(to: size, anchorOn: anchor)
676         case .data(_): return (DefaultImageProcessor.default >> self).process(item: item, options: options)
677         }
678     }
679 }
680
681 /// Concatenate two `ImageProcessor`s. `ImageProcessor.appen(another:)` is used internally.
682 ///
683 /// - parameter left:  First processor.
684 /// - parameter right: Second processor.
685 ///
686 /// - returns: The concatenated processor.
687 public func >>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor {
688     return left.append(another: right)
689 }
690
691 extension Color {
692     var hex: String {
693         var r: CGFloat = 0
694         var g: CGFloat = 0
695         var b: CGFloat = 0
696         var a: CGFloat = 0
697
698         #if os(macOS)
699         (usingColorSpace(.sRGB) ?? self).getRed(&r, green: &g, blue: &b, alpha: &a)
700         #else
701         getRed(&r, green: &g, blue: &b, alpha: &a)
702         #endif
703
704         let rInt = Int(r * 255) << 24
705         let gInt = Int(g * 255) << 16
706         let bInt = Int(b * 255) << 8
707         let aInt = Int(a * 255)
708         
709         let rgba = rInt | gInt | bInt | aInt
710         
711         return String(format:"#%08x", rgba)
712     }
713 }