CoreImageExtensions

1.0.1

Useful extensions for Apple's Core Image framework
DigitalMasterpieces/CoreImageExtensions

What's New

Build fixes

2021-05-12T07:02:32Z

Small build fixes related to Xcode 14.5 and Catalyst.

CoreImageExtensions

Useful extensions for Apple's Core Image framework.

Image Lookup

We added a convenience initializer to CIImage that you can use to load an image by its name from an asset catalog or from a bundle directly:

let image = CIImage(named: "myImage")

This provides the same signature as the corresponding UIImage method:

// on iOS, Catalyst, tvOS
init?(named name: String, in bundle: Bundle? = nil, compatibleWith traitCollection: UITraitCollection? = nil)

// on macOS
init?(named name: String, in bundle: Bundle? = nil)

Images with Fixed Values

In Core Image, you can use the init(color: CIColor) initializer of CIImage to create an image with infinite extent that only contains pixels with the given color. This, however, only allows the creation of images filled with values in [0…1] since CIColor clamps values to this range.

We added two new factory methods on CIImage that allow the creation of images filled with arbitrary values:

/// Returns a `CIImage` with infinite extent only containing the given pixel value.
static func containing(values: CIVector) -> CIImage?

/// Returns a `CIImage` with infinite extent only containing the given value in RGB and alpha 1.
/// So `CIImage.containing(42.3)` would result in an image containing the value (42.3, 42.3, 42.3, 1.0) in each pixel.
static func containing(value: Double) -> CIImage?

This is useful, for instance, for passing scalar values into blend filters. For instance, this would create a color inversion effect in RGB:

var inverted = CIBlendKernel.multiply.apply(foreground: image, background: CIImage.containing(value: -1)!)!
inverted = CIBlendKernel.componentAdd.apply(foreground: inverted, background: CIImage.containing(value: 1)!)!

Image Value Access

It can be rather complicated to access the actual pixel values of a CIImage. The image needs to be rendered first and the resulting bitmap memory needs to be accessed properly.

We added some convenience methods to CIContext to do just that in a one-liner:

// get all pixel values of `image` as an array of `SIMD4<UInt8>` values:
let values = context.readUInt8PixelValues(from: image, in: image.extent)
let red: UInt8 = values[42].r // for instance

// get the value of a specific pixel as a `SIMD4<Float32>`:
let value = context.readFloat32PixelValue(from: image, at: CGPoint.zero)
let green: Float32 = value.g // for instance

These methods come in variants for accessing an area of pixels (in a given CGRect) or single pixels (at a given CGPoint). They are also available for three different data types: UInt8 (the normal 8-bit per channel format, with [0…255] range), Float32 (aka float containing arbitrary values, but colors are usually mapped to [0...1]), and Float16 (only on iOS).

OpenEXR Support

OpenEXR is an open standard for storing arbitrary bitmap data that exceed “normal” image color data, like 32-bit high-dynamic range data or negative floating point values (for instance for height fields).

Although Image I/O has native support for the EXR format, Core Image doesn’t provide convenience ways for rendering a CIImage into EXR. We added corresponding methods to CIContext for EXR export that align with the API provided for the other file formats:

// to create a `Data` object containing a 16-bit float EXR representation:
let exrData = try context.exrRepresentation(of: image, format: .RGBAh)

// to write a 32-bit float representation to an EXR file at `url`:
try context.writeEXRRepresentation(of: image, to: url, format: .RGBAf)

For reading EXR files into a CIImage, the usual initializers like CIImage(contentsOf: url) or CIImage(named: “myImage.exr” (see above) can be used.

OpenEXR Test Images

All EXR test images used in this project have been taken from here.

Description

  • Swift Tools 5.3.0

Dependencies

  • None
Last updated: Thu May 13 2021 03:06:51 GMT-0500 (GMT-05:00)