A Swift image decoding and encoding library built on Apple's ImageIO framework. Designed as a building block for apps that display images across multiple formats, with an emphasis on low memory overhead and efficient format conversion.
ImageIOKit supports JPEG, PNG, WebP, HEIC, AVIF, and JPEG XL, and provides native sub-region decoding for JPEG via libjpeg-turbo, and lossless JPEG reconstruction from JPEG XL via libjxl.
- Multi-format decoding — Full-resolution, thumbnailed, and cropped decodes from a single
ImageSourceAPI. - JPEG region decode — Decode arbitrary sub-regions of JPEG files without loading the entire image into memory (via TurboJPEG cropped decode).
- JPEG XL reconstruction — Losslessly reconstruct the original JPEG bitstream from JXL-from-JPEG files, with zero quality loss and no decode overhead.
- Zero-decode encoding — Encode and transcode via
CGImageDestinationAddImageFromSource, copying compressed data directly from source to destination when no pixel-level transformation (e.g. alpha stripping) is needed. - Image conditioning — Convert any supported format to JPEG on disk in a single call, giving every image shrink-on-load thumbnailing and region decode for free.
- Raw pixel access — Decode into
PixelBuffer(C-allocated, zero-copyCGImagevia retainedCGDataProvider) in 4 pixel formats: RGBA, RGB, Grayscale, and Grayscale+Alpha. - Metal texture support — Create
MTLTexturedirectly from aPixelBuffer. - Memory budgeting —
estimatedDecodeMemoryprovides per-format estimates of peak memory usage to help schedule concurrent decodes. - Purgeable caching — Full-resolution
CGImageresults are cached viaNSCacheand automatically evicted under memory pressure.
- iOS 18.0+
- Swift 6.0+
- Xcode 16.0+
Add ImageIOKit to your Package.swift:
dependencies: [
.package(url: "https://github.com/TimOliver/ImageIOKit.git", from: "1.0.0")
]Or add it via Xcode: File > Add Package Dependencies and enter the repository URL.
ImageIOKit can also be used as a source folder added directly to an Xcode project. The primary development environment is an Xcode project (ImageIOKit.xcodeproj) with an example app target.
import ImageIOKit
// From a file URL
let source = ImageSource(url: imageURL)
// From in-memory data
let source = ImageSource(data: imageData)
// Deferred loading (header read on first use)
let source = ImageSource(url: imageURL, loadImmediately: false)
source.loadImageData()source.imageSize // CGSize — pixel dimensions
source.fileFormat // ImageFileFormat — .jpeg, .png, .webp, .heic, .avif, .jpegXL
source.hasAlpha // Bool
source.colorModel // ImageColorModel — .rgb, .grayscale, .cmyk, .lab
source.colorProfile // String — ICC profile name// Full-resolution UIImage
let image = source.decodeFullImage()
// Thumbnail (ImageIO shrink-on-load, avoids full decode when possible)
let thumb = source.makeThumbnail(fittingSize: CGSize(width: 200, height: 200))
// Raw pixel buffer with target size and crop
let buffer = try source.decode(
targetSize: CGSize(width: 1024, height: 1024),
cropRect: CGRect(x: 100, y: 100, width: 500, height: 500),
pixelFormat: .rgba8
)
// JPEG sub-region decode (no full-image decode)
let region = source.decodeRegion(CGRect(x: 0, y: 0, width: 256, height: 256))// Encode to a format (zero-decode fast path when possible)
let jpegData = try source.encode(as: .jpeg, quality: 0.85)
// Write directly to disk
try source.write(to: outputURL, as: .png)
// Transcode between formats (JXL → JPEG uses lossless reconstruction when available)
let data = try source.transcode(to: .jpeg)Conditioning converts any image to JPEG on disk, optionally downscaling oversized images. The resulting JPEG is optimized for efficient partial decoding and thumbnailing.
let conditioned = try source.writeConditionedJPEG(maxDimension: 4096, to: outputURL, quality: 0.85)
// conditioned is a new ImageSource pointing at the JPEG file
// If the source was already a small-enough JPEG, returns `self` (no work done)let buffer = try source.decode(pixelFormat: .rgba8)
buffer.width // Int
buffer.height // Int
buffer.bytesPerRow // Int
buffer.data // UnsafeMutableRawPointer
// Zero-copy CGImage (backed by the buffer's memory)
let cgImage = buffer.makeCGImage()
// Metal texture
let texture = buffer.makeTexture(device: mtlDevice)// Estimate peak memory for a full decode
let bytes = source.estimatedDecodeMemory
// Compare against available memory before decoding
if bytes < os_proc_available_memory() {
let image = source.decodeFullImage()
}ImageIOKit is structured around a single public facade (ImageSource) that wraps Apple's CGImageSource and CGImageDestination:
ImageSource (facade)
├── Decoding (ImageSource+Decoding)
│ ├── Thumbnails via CGImageSourceCreateThumbnailAtIndex
│ ├── Full decode via CGImageSourceCreateImageAtIndex
│ ├── JPEG region decode via JPEGRegionDecoder (TurboJPEG cropping)
│ └── JXL thumbnail via JXLDecoder (libjxl DC-only progressive decode)
├── Encoding (ImageSource+Encoding)
│ ├── Zero-decode encode via CGImageDestinationAddImageFromSource
│ ├── Alpha-strip fallback via CGImage.strippingAlpha()
│ ├── Conditioning (any format → JPEG on disk)
│ ├── Transcoding (format → format)
│ └── JXL → JPEG lossless reconstruction via JXLReconstructor
└── PixelBuffer
├── C-allocated pixel data (rgba8, rgb8, gray8, grayAlpha8)
├── Zero-copy CGImage via CGDataProvider
└── Metal texture creation
While ImageIO handles the vast majority of decode/encode operations, two C libraries are used for capabilities ImageIO does not provide:
| Library | Purpose | Import |
|---|---|---|
| libjpeg-turbo | JPEG sub-region decode (tj3SetCroppingRegion) |
import turbojpeg |
| libjxl | JXL → JPEG lossless reconstruction, DC-only thumbnail decode | import jxl |
ImageIOKit was created by Tim Oliver.
ImageIOKit is available under the MIT license. See LICENSE for details.