Skip to content

Commit

Permalink
Custom Image Processor
Browse files Browse the repository at this point in the history
Modify frames using CIImage/CGImage callback
  • Loading branch information
starkdmi committed Apr 21, 2024
1 parent 6d3272e commit efe310a
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 15 deletions.
23 changes: 20 additions & 3 deletions Sources/Extensions/CGImage+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public extension CGImage {
return nil
}

/// Resize `CGImage`
/// Resize `CGImage` to fit, with aspect ration preserving
func resizing(to size: CGSize) -> CGImage? {
let size = self.size.fit(in: size)

Expand All @@ -44,6 +44,17 @@ public extension CGImage {
return context.makeImage()
}

/// Scale `CGImage` to fill
func scaling(to size: CGSize) -> CGImage? {
guard let context = CGContext.make(self, width: Int(size.width), height: Int(size.height)) else {
return nil
}

context.draw(self, in: CGRect(origin: .zero, size: size))

return context.makeImage()
}

/// Mirror `CGImage`
func mirroring() -> CGImage? {
return self.reflect(horizontally: true, vertically: false)
Expand Down Expand Up @@ -255,6 +266,7 @@ internal extension CGImage {
}

// Apply operations in sorted order
var imageProcessor: ImageProcessor?
for operation in operations.sorted() {
switch operation {
case let .rotate(value, fill):
Expand Down Expand Up @@ -285,8 +297,8 @@ internal extension CGImage {
cgImage = mirrored
}
}
/*case .imageProcessing(let function):
cgImage = function(cgImage, index)*/
case .imageProcessing(let processor):
imageProcessor = processor
}
}

Expand All @@ -299,6 +311,11 @@ internal extension CGImage {
}
}

// Apply custom image processor
if let imageProcessor = imageProcessor, let image = imageProcessor(nil, cgImage, orientation, index).cgImage {
cgImage = image
}

// Return modified image
return cgImage
}
Expand Down
10 changes: 8 additions & 2 deletions Sources/Extensions/CIImage+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ internal extension CIImage {
}

// Apply operations in sorted order
var imageProcessor: ImageProcessor?
for operation in operations.sorted() {
switch operation {
case let .rotate(value, fill):
Expand All @@ -195,8 +196,8 @@ internal extension CIImage {
ciImage = ciImage
.transformed(by: CGAffineTransform(scaleX: -1.0, y: 1.0))
}
/*case .imageProcessing(let function):
ciImage = function(ciImage, index)*/
case .imageProcessing(let processor):
imageProcessor = processor
}
}

Expand All @@ -213,6 +214,11 @@ internal extension CIImage {
ciImage = ciImage.premultiplyingAlpha().composited(over: background)
}

// Apply custom image processor
if let imageProcessor = imageProcessor, let image = imageProcessor(ciImage, nil, orientation, index).ciImage {
ciImage = image
}

// Return modified image
return ciImage
}
Expand Down
11 changes: 8 additions & 3 deletions Sources/Extensions/VImage+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ internal extension vImage {
}

// Apply operations in sorted order
var imageProcessor: ImageProcessor?
let operations = operations.sorted()
for operation in operations {
switch operation {
Expand Down Expand Up @@ -196,10 +197,9 @@ internal extension vImage {
premultipliedAlpha = true
}*/
}
/*case .imageProcessing(let function):
case .imageProcessing(let processor):
// Custom image processing callback, executed after all the other image operations
break
*/
imageProcessor = processor
}
}

Expand All @@ -222,6 +222,11 @@ internal extension vImage {
}
try error.check()*/

// Apply custom image processor
if let imageProcessor = imageProcessor, let cgImage = imageProcessor(nil, image, orientation, index).cgImage {
image = cgImage
}

return image
}
}
Expand Down
19 changes: 13 additions & 6 deletions Sources/Types/Image/ImageOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public enum RotationFill {
case color(alpha: UInt8, red: UInt8, green: UInt8, blue: UInt8)
}

/// Image processor type
/// Only one image passed in based on `preferredFramework` and internal framework support (`CIImage` doesn't support animations)
/// Return image of the same type to be written - when `CGImage` is not `nil`, modify and return `CGImage` while passing `nil` for `CIImage`
/// Index used as a frame number (starts with zero), `0` for static images
public typealias ImageProcessor = (_ ciImage: CIImage?, _ cgImage: CGImage?, _ orientation: CGImagePropertyOrientation?, _ index: Int) -> (ciImage: CIImage?, cgImage: CGImage?)

/// Image operations
public enum ImageOperation: Equatable, Hashable, Comparable {
/// Rotation
Expand All @@ -29,8 +35,7 @@ public enum ImageOperation: Equatable, Hashable, Comparable {
case mirror

/// Custom image processing function, appplied after all the other image operations
/// Index used as a frame number, `0` for static images
// case imageProcessing((_ image: CGImage, _ index: Int) -> CGImage) // vImage, CIImage
case imageProcessing(ImageProcessor)

/// Operation priority
private var priority: Int {
Expand All @@ -41,9 +46,9 @@ public enum ImageOperation: Equatable, Hashable, Comparable {
return 2
case .mirror:
return 3
/*case .imageProcessing(_):
case .imageProcessing(_):
// Should be executed after all the operations
return 100*/
return 100
}
}

Expand All @@ -65,8 +70,8 @@ public enum ImageOperation: Equatable, Hashable, Comparable {
hasher.combine("flip")
case .mirror:
hasher.combine("mirror")
/*case .imageProcessing(_):
hasher.combine("ImageProcessing")*/
case .imageProcessing(_):
hasher.combine("ImageProcessing")
}
}

Expand All @@ -79,6 +84,8 @@ public enum ImageOperation: Equatable, Hashable, Comparable {
return true
case (.mirror, .mirror):
return true
case (.imageProcessing, .imageProcessing):
return true
default:
return false
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Types/Image/ImageSize.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public enum ImageSize: Equatable {
/// Size to fit in
case fit(CGSize)

/// Scale (fill) - no aspect ration preserving
/// Scale (fill) - no aspect ratio preserving
// case scale(CGSize)

/// Cropping size and alignment, `fit` primarly used in video thumbnails
Expand Down

0 comments on commit efe310a

Please sign in to comment.