123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // UIImage+AlamofireImage.swift
- //
- // Copyright (c) 2015-2016 Alamofire Software Foundation (http://alamofire.org/)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- import CoreGraphics
- import Foundation
- import UIKit
- #if os(iOS) || os(tvOS)
- import CoreImage
- #endif
- // MARK: Initialization
- private let lock = NSLock()
- extension UIImage {
- /**
- Initializes and returns the image object with the specified data in a thread-safe manner.
- It has been reported that there are thread-safety issues when initializing large amounts of images
- simultaneously. In the event of these issues occurring, this method can be used in place of
- the `init?(data:)` method.
- - parameter data: The data object containing the image data.
- - returns: An initialized `UIImage` object, or `nil` if the method failed.
- */
- public static func af_threadSafeImageWithData(data: NSData) -> UIImage? {
- lock.lock()
- let image = UIImage(data: data)
- lock.unlock()
- return image
- }
- /**
- Initializes and returns the image object with the specified data and scale in a thread-safe manner.
- It has been reported that there are thread-safety issues when initializing large amounts of images
- simultaneously. In the event of these issues occurring, this method can be used in place of
- the `init?(data:scale:)` method.
- - parameter data: The data object containing the image data.
- - parameter scale: The scale factor to assume when interpreting the image data. Applying a scale factor of 1.0
- results in an image whose size matches the pixel-based dimensions of the image. Applying a
- different scale factor changes the size of the image as reported by the size property.
- - returns: An initialized `UIImage` object, or `nil` if the method failed.
- */
- public static func af_threadSafeImageWithData(data: NSData, scale: CGFloat) -> UIImage? {
- lock.lock()
- let image = UIImage(data: data, scale: scale)
- lock.unlock()
- return image
- }
- }
- // MARK: - Inflation
- extension UIImage {
- private struct AssociatedKeys {
- static var InflatedKey = "af_UIImage.Inflated"
- }
- /// Returns whether the image is inflated.
- public var af_inflated: Bool {
- get {
- if let inflated = objc_getAssociatedObject(self, &AssociatedKeys.InflatedKey) as? Bool {
- return inflated
- } else {
- return false
- }
- }
- set(inflated) {
- objc_setAssociatedObject(self, &AssociatedKeys.InflatedKey, inflated, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
- }
- }
- /**
- Inflates the underlying compressed image data to be backed by an uncompressed bitmap representation.
- Inflating compressed image formats (such as PNG or JPEG) can significantly improve drawing performance as it
- allows a bitmap representation to be constructed in the background rather than on the main thread.
- */
- public func af_inflate() {
- guard !af_inflated else { return }
- af_inflated = true
- CGDataProviderCopyData(CGImageGetDataProvider(CGImage))
- }
- }
- // MARK: - Alpha
- extension UIImage {
- /// Returns whether the image contains an alpha component.
- public var af_containsAlphaComponent: Bool {
- let alphaInfo = CGImageGetAlphaInfo(CGImage)
- return (
- alphaInfo == .First ||
- alphaInfo == .Last ||
- alphaInfo == .PremultipliedFirst ||
- alphaInfo == .PremultipliedLast
- )
- }
- /// Returns whether the image is opaque.
- public var af_isOpaque: Bool { return !af_containsAlphaComponent }
- }
- // MARK: - Scaling
- extension UIImage {
- /**
- Returns a new version of the image scaled to the specified size.
- - parameter size: The size to use when scaling the new image.
- - returns: A new image object.
- */
- public func af_imageScaledToSize(size: CGSize) -> UIImage {
- UIGraphicsBeginImageContextWithOptions(size, af_isOpaque, 0.0)
- drawInRect(CGRect(origin: CGPointZero, size: size))
- let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
- UIGraphicsEndImageContext()
- return scaledImage
- }
- /**
- Returns a new version of the image scaled from the center while maintaining the aspect ratio to fit within
- a specified size.
- The resulting image contains an alpha component used to pad the width or height with the necessary transparent
- pixels to fit the specified size. In high performance critical situations, this may not be the optimal approach.
- To maintain an opaque image, you could compute the `scaledSize` manually, then use the `af_imageScaledToSize`
- method in conjunction with a `.Center` content mode to achieve the same visual result.
- - parameter size: The size to use when scaling the new image.
- - returns: A new image object.
- */
- public func af_imageAspectScaledToFitSize(size: CGSize) -> UIImage {
- let imageAspectRatio = self.size.width / self.size.height
- let canvasAspectRatio = size.width / size.height
- var resizeFactor: CGFloat
- if imageAspectRatio > canvasAspectRatio {
- resizeFactor = size.width / self.size.width
- } else {
- resizeFactor = size.height / self.size.height
- }
- let scaledSize = CGSize(width: self.size.width * resizeFactor, height: self.size.height * resizeFactor)
- let origin = CGPoint(x: (size.width - scaledSize.width) / 2.0, y: (size.height - scaledSize.height) / 2.0)
- UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
- drawInRect(CGRect(origin: origin, size: scaledSize))
- let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
- UIGraphicsEndImageContext()
- return scaledImage
- }
- /**
- Returns a new version of the image scaled from the center while maintaining the aspect ratio to fill a
- specified size. Any pixels that fall outside the specified size are clipped.
- - parameter size: The size to use when scaling the new image.
- - returns: A new image object.
- */
- public func af_imageAspectScaledToFillSize(size: CGSize) -> UIImage {
- let imageAspectRatio = self.size.width / self.size.height
- let canvasAspectRatio = size.width / size.height
- var resizeFactor: CGFloat
- if imageAspectRatio > canvasAspectRatio {
- resizeFactor = size.height / self.size.height
- } else {
- resizeFactor = size.width / self.size.width
- }
- let scaledSize = CGSize(width: self.size.width * resizeFactor, height: self.size.height * resizeFactor)
- let origin = CGPoint(x: (size.width - scaledSize.width) / 2.0, y: (size.height - scaledSize.height) / 2.0)
- UIGraphicsBeginImageContextWithOptions(size, af_isOpaque, 0.0)
- drawInRect(CGRect(origin: origin, size: scaledSize))
- let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
- UIGraphicsEndImageContext()
- return scaledImage
- }
- }
- // MARK: - Rounded Corners
- extension UIImage {
- /**
- Returns a new version of the image with the corners rounded to the specified radius.
- - parameter radius: The radius to use when rounding the new image.
- - parameter divideRadiusByImageScale: Whether to divide the radius by the image scale. Set to `true` when the
- image has the same resolution for all screen scales such as @1x, @2x and
- @3x (i.e. single image from web server). Set to `false` for images loaded
- from an asset catalog with varying resolutions for each screen scale.
- `false` by default.
- - returns: A new image object.
- */
- public func af_imageWithRoundedCornerRadius(radius: CGFloat, divideRadiusByImageScale: Bool = false) -> UIImage {
- UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
- let scaledRadius = divideRadiusByImageScale ? radius / scale : radius
- let clippingPath = UIBezierPath(roundedRect: CGRect(origin: CGPointZero, size: size), cornerRadius: scaledRadius)
- clippingPath.addClip()
- drawInRect(CGRect(origin: CGPointZero, size: size))
- let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
- UIGraphicsEndImageContext()
- return roundedImage
- }
- /**
- Returns a new version of the image rounded into a circle.
- - returns: A new image object.
- */
- public func af_imageRoundedIntoCircle() -> UIImage {
- let radius = min(size.width, size.height) / 2.0
- var squareImage = self
- if size.width != size.height {
- let squareDimension = min(size.width, size.height)
- let squareSize = CGSize(width: squareDimension, height: squareDimension)
- squareImage = af_imageAspectScaledToFillSize(squareSize)
- }
- UIGraphicsBeginImageContextWithOptions(squareImage.size, false, 0.0)
- let clippingPath = UIBezierPath(
- roundedRect: CGRect(origin: CGPointZero, size: squareImage.size),
- cornerRadius: radius
- )
- clippingPath.addClip()
- squareImage.drawInRect(CGRect(origin: CGPointZero, size: squareImage.size))
- let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
- UIGraphicsEndImageContext()
- return roundedImage
- }
- }
- #if os(iOS) || os(tvOS)
- // MARK: - Core Image Filters
- extension UIImage {
- /**
- Returns a new version of the image using a CoreImage filter with the specified name and parameters.
- - parameter filterName: The name of the CoreImage filter to use on the new image.
- - parameter filterParameters: The parameters to apply to the CoreImage filter.
- - returns: A new image object, or `nil` if the filter failed for any reason.
- */
- public func af_imageWithAppliedCoreImageFilter(
- filterName: String,
- filterParameters: [String: AnyObject]? = nil) -> UIImage?
- {
- var image: CoreImage.CIImage? = CIImage
- if image == nil, let CGImage = self.CGImage {
- image = CoreImage.CIImage(CGImage: CGImage)
- }
- guard let coreImage = image else { return nil }
- let context = CIContext(options: [kCIContextPriorityRequestLow: true])
- var parameters: [String: AnyObject] = filterParameters ?? [:]
- parameters[kCIInputImageKey] = coreImage
- guard let filter = CIFilter(name: filterName, withInputParameters: parameters) else { return nil }
- guard let outputImage = filter.outputImage else { return nil }
- let cgImageRef = context.createCGImage(outputImage, fromRect: outputImage.extent)
- return UIImage(CGImage: cgImageRef, scale: scale, orientation: imageOrientation)
- }
- }
- #endif
|