new Image edit API
This commit is contained in:
parent
020ea512f4
commit
fba5894390
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
67186B181EB1DC0500CFAFFB /* ResizeDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */; };
|
||||
6727419D1E65B99E0075836A /* MappableUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */; };
|
||||
672741A01E65C1E00075836A /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419F1E65C1E00075836A /* Post.swift */; };
|
||||
674743941E929A5A00B47671 /* PaginationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674743931E929A5A00B47671 /* PaginationViewModelTests.swift */; };
|
||||
|
|
@ -117,6 +118,7 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
12F36034A5278991B658B53E /* Pods_LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizeDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
671FF1611EAA264B001B882C /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; };
|
||||
6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MappableUserDefaultsTests.swift; sourceTree = "<group>"; };
|
||||
6727419F1E65C1E00075836A /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -303,6 +305,7 @@
|
|||
67A7B19E1EAF646400E5BC59 /* PaddingDrawingOperation.swift */,
|
||||
67A7B1A01EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift */,
|
||||
67A7B1A21EAF6B4600E5BC59 /* CALayerDrawingOperation.swift */,
|
||||
67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */,
|
||||
);
|
||||
path = DrawingOperations;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1015,6 +1018,7 @@
|
|||
95B39A861D9D51250057BD54 /* String+Localization.swift in Sources */,
|
||||
78C36F7E1D801E3E00E7EBEA /* Double+Rounding.swift in Sources */,
|
||||
67DC65061E979B70002F2FFF /* UIView+LoadingIndicator.swift in Sources */,
|
||||
67186B181EB1DC0500CFAFFB /* ResizeDrawingOperation.swift in Sources */,
|
||||
787609221E1403830093CE36 /* Observable+DeferredJust.swift in Sources */,
|
||||
67B305841E8A92E8008169CA /* XibView.swift in Sources */,
|
||||
78C54AFD1E432EEF0051EFBA /* UIViewController+TopVisibleViewController.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ public extension CGSize {
|
|||
/// - resizeMode: Resize mode to use.
|
||||
/// - Returns: A new CGRect instance matching request parameters.
|
||||
public func resizeRect(forNewSize newSize: CGSize, resizeMode: ResizeMode) -> CGRect {
|
||||
let horizontalRatio = newSize.width / CGFloat(width)
|
||||
let verticalRatio = newSize.height / CGFloat(height)
|
||||
let horizontalRatio = newSize.width / width
|
||||
let verticalRatio = newSize.height / height
|
||||
|
||||
let ratio: CGFloat
|
||||
|
||||
|
|
@ -45,8 +45,8 @@ public extension CGSize {
|
|||
ratio = min(horizontalRatio, verticalRatio)
|
||||
}
|
||||
|
||||
let newWidth = resizeMode == .scaleToFill ? newSize.width : CGFloat(width) * ratio
|
||||
let newHeight = resizeMode == .scaleToFill ? newSize.height : CGFloat(height) * ratio
|
||||
let newWidth = resizeMode == .scaleToFill ? newSize.width : width * ratio
|
||||
let newHeight = resizeMode == .scaleToFill ? newSize.height : height * ratio
|
||||
|
||||
let originX: CGFloat
|
||||
let originY: CGFloat
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ public extension UIImage {
|
|||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
|
|
@ -159,37 +159,24 @@ public extension UIImage {
|
|||
/// - Parameters:
|
||||
/// - newSize: The new size of the image.
|
||||
/// - contentMode: The way to handle the content in the new size.
|
||||
/// - cropToImageBounds: Should output image size match resized image size.
|
||||
/// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize, contentMode: ResizeMode = .scaleToFill) -> UIImage {
|
||||
public func resize(newSize: CGSize,
|
||||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> UIImage {
|
||||
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let resizedRect = size.resizeRect(forNewSize: newSize, resizeMode: contentMode)
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: resizedRect.origin)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of image using scaleAspectFit strategy.
|
||||
///
|
||||
/// - Parameter preferredNewSize: The preferred new size of image.
|
||||
/// - Returns: A new image which size may be less then or equals to given preferredSize.
|
||||
public func resizeAspectFit(preferredNewSize: CGSize) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let resizedRect = size.resizeRect(forNewSize: preferredNewSize, resizeMode: .scaleAspectFit)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: .zero)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
|
|
@ -204,7 +191,7 @@ public extension UIImage {
|
|||
newSize: size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
|
|
@ -218,6 +205,17 @@ public extension UIImage {
|
|||
|
||||
let operation = PaddingDrawingOperation(image: image, imageSize: size, padding: padding)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// Workaround to fix flipped image rendering (by Y)
|
||||
private func redraw() -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image, newSize: size)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
|
|
@ -233,10 +231,8 @@ private extension DrawingOperation {
|
|||
format.opaque = opaque
|
||||
format.scale = scale
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: size, format: format)
|
||||
|
||||
return renderer.image { context in
|
||||
self.apply(in: context.cgContext)
|
||||
return UIGraphicsImageRenderer(size: size, format: format).image {
|
||||
self.apply(in: $0.cgContext)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,39 +30,39 @@ public extension Support where Base: UIImage {
|
|||
/// - color: The color to fill
|
||||
/// - size: The size of an new image.
|
||||
/// - Returns: A new instanse of UIImage with given size and color or nil if something goes wrong.
|
||||
public static func imageWith(color: UIColor, size: CGSize) -> UIImage? {
|
||||
public static func imageWith(color: UIColor, size: CGSize) -> Support<UIImage>? {
|
||||
let width = Int(ceil(size.width))
|
||||
let height = Int(ceil(size.height))
|
||||
|
||||
let operation = SolidFillDrawingOperation(color: color.cgColor, width: width, height: height)
|
||||
|
||||
return operation.imageFromNewContext(scale: UIScreen.main.scale)
|
||||
return operation.imageFromNewContext(scale: UIScreen.main.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates an image from a UIView.
|
||||
///
|
||||
/// - Parameter fromView: The source view.
|
||||
/// - Returns: A new instance of UIImage or nil if something goes wrong.
|
||||
public static func imageFrom(view: UIView) -> UIImage? {
|
||||
let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
public static func imageFrom(view: UIView) -> Support<UIImage>? {
|
||||
let layerDrawingOperation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
return operation.imageFromNewContext(scale: UIScreen.main.scale)
|
||||
return layerDrawingOperation.imageFromNewContext(scale: UIScreen.main.scale)?.support.flipY()
|
||||
}
|
||||
|
||||
/// Render current template UIImage into new image using given color.
|
||||
///
|
||||
/// - Parameter color: Color to fill template image.
|
||||
/// - Returns: A new UIImage rendered with given color or nil if something goes wrong.
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage? {
|
||||
public func renderTemplate(withColor color: UIColor) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a new image with rounded corners and border.
|
||||
|
|
@ -76,10 +76,10 @@ public extension Support where Base: UIImage {
|
|||
public func roundCorners(cornerRadius: CGFloat,
|
||||
borderWidth: CGFloat,
|
||||
color: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
extendSize: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
|
|
@ -97,15 +97,15 @@ public extension Support where Base: UIImage {
|
|||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image or nil if something goes wrong.
|
||||
public func roundCornersToCircle() -> UIImage? {
|
||||
public func roundCornersToCircle() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
|
@ -114,7 +114,7 @@ public extension Support where Base: UIImage {
|
|||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
|
|
@ -126,10 +126,10 @@ public extension Support where Base: UIImage {
|
|||
/// - Returns: A new image with rounded corners or nil if something goes wrong.
|
||||
public func roundCornersToCircle(borderWidth: CGFloat,
|
||||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
extendSize: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
|
@ -149,7 +149,7 @@ public extension Support where Base: UIImage {
|
|||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
|
|
@ -157,68 +157,69 @@ public extension Support where Base: UIImage {
|
|||
/// - Parameters:
|
||||
/// - newSize: The new size of the image.
|
||||
/// - contentMode: The way to handle the content in the new size.
|
||||
/// - cropToImageBounds: Should output image size match resized image size.
|
||||
/// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize, contentMode: ResizeMode = .scaleToFill) -> UIImage? {
|
||||
public func resize(newSize: CGSize,
|
||||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let resizedRect = base.size.resizeRect(forNewSize: newSize, resizeMode: contentMode)
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: resizedRect.origin)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of image using scaleAspectFit strategy.
|
||||
///
|
||||
/// - Parameter preferredNewSize: The preferred new size of image.
|
||||
/// - Returns: A new image which size may be less then or equals to given preferredSize.
|
||||
public func resizeAspectFit(preferredNewSize: CGSize) -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let resizedRect = base.size.resizeRect(forNewSize: preferredNewSize, resizeMode: .scaleAspectFit)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: .zero)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
public func applyAlpha() -> UIImage? {
|
||||
public func applyAlpha() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage, !image.hasAlpha else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
///
|
||||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong.
|
||||
public func applyPadding(_ padding: CGFloat) -> UIImage? {
|
||||
public func applyPadding(_ padding: CGFloat) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
padding: padding)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
private func flipY() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let flipOperation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
origin: .zero,
|
||||
opaque: false,
|
||||
flipY: true)
|
||||
|
||||
return flipOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ struct BorderDrawingOperation: DrawingOperation {
|
|||
let width = imageSize.width + offset * 2
|
||||
let height = imageSize.height + offset * 2
|
||||
|
||||
return (width: Int(ceil(width)), height: Int(ceil(height)))
|
||||
return CGSize(width: width, height: height).ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
|
|
|
|||
|
|
@ -37,9 +37,6 @@ struct CALayerDrawingOperation: DrawingOperation {
|
|||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.translateBy(x: 0, y: size.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
|
||||
layer.render(in: context)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,12 +28,19 @@ struct ImageDrawingOperation: DrawingOperation {
|
|||
private let newSize: CGSize
|
||||
private let origin: CGPoint
|
||||
public let opaque: Bool
|
||||
private let flipY: Bool
|
||||
|
||||
public init(image: CGImage,
|
||||
newSize: CGSize,
|
||||
origin: CGPoint = .zero,
|
||||
opaque: Bool = false,
|
||||
flipY: Bool = false) {
|
||||
|
||||
public init(image: CGImage, newSize: CGSize, origin: CGPoint = .zero, opaque: Bool = true) {
|
||||
self.image = image
|
||||
self.newSize = newSize
|
||||
self.origin = origin
|
||||
self.opaque = opaque
|
||||
self.flipY = flipY
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
|
|
@ -41,6 +48,11 @@ struct ImageDrawingOperation: DrawingOperation {
|
|||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
if flipY {
|
||||
context.translateBy(x: 0, y: newSize.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
}
|
||||
|
||||
context.interpolationQuality = .high
|
||||
context.draw(image, in: CGRect(origin: origin, size: newSize))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// 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
|
||||
|
||||
struct ResizeDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let drawRect: CGRect
|
||||
public let contextSize: CGContextSize
|
||||
|
||||
public init(image: CGImage,
|
||||
imageSize: CGSize,
|
||||
preferredNewSize: CGSize,
|
||||
resizeMode: ResizeMode,
|
||||
cropToImageBounds: Bool = false) {
|
||||
|
||||
self.image = image
|
||||
|
||||
let resizedRect = imageSize.resizeRect(forNewSize: preferredNewSize, resizeMode: resizeMode)
|
||||
|
||||
if cropToImageBounds {
|
||||
drawRect = CGRect(origin: .zero, size: resizedRect.size)
|
||||
contextSize = resizedRect.size.ceiledContextSize
|
||||
} else {
|
||||
drawRect = resizedRect
|
||||
contextSize = preferredNewSize.ceiledContextSize
|
||||
}
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.interpolationQuality = .high
|
||||
context.draw(image, in: drawRect)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue