Contents

1amageek/OpenImageIO

A Swift library providing **full API compatibility with Apple's ImageIO framework** for WebAssembly (WASM) and other non-Apple platforms.

Overview

OpenImageIO enables cross-platform Swift code to use familiar ImageIO APIs even in environments where Apple's ImageIO framework is unavailable. Write your image handling code once, and it works seamlessly across iOS, macOS, and WebAssembly.

#if canImport(ImageIO)
import ImageIO
#else
import OpenImageIO
#endif

// This code works in both environments
let source = CGImageSourceCreateWithData(data, nil)
let image = CGImageSourceCreateImageAtIndex(source!, 0, nil)

Features

  • Full API Compatibility - Identical type names, method signatures, and property names as Apple's ImageIO
  • Multiple Format Support - PNG, JPEG, GIF, BMP, TIFF, WebP
  • Image Sources - Read and decode image data with CGImageSource
  • Image Destinations - Encode and write image data with CGImageDestination
  • Rich Metadata Support - EXIF, IPTC, GPS, XMP, TIFF, and format-specific metadata
  • Manufacturer Metadata - Canon, Nikon, Apple, and other camera maker notes
  • Incremental Loading - Progressive image loading support
  • Thumbnail Generation - Create thumbnails with configurable options

Requirements

  • Swift 6.2+
  • For WASM: SwiftWasm toolchain

Installation

Swift Package Manager

Add the following to your Package.swift:

dependencies: [
    .package(url: "https://github.com/1amageek/OpenImageIO.git", from: "1.0.0")
]

Then add OpenImageIO to your target dependencies:

.target(
    name: "YourTarget",
    dependencies: ["OpenImageIO"]
)

Usage

Reading Images

import OpenImageIO

// Create image source from data
let source = CGImageSourceCreateWithData(imageData, nil)

// Get image count (useful for animated GIFs)
let count = CGImageSourceGetCount(source!)

// Get image type
let type = CGImageSourceGetType(source!) // e.g., "public.png"

// Extract image at index
let image = CGImageSourceCreateImageAtIndex(source!, 0, nil)
print("Size: \(image!.width) x \(image!.height)")

Creating Thumbnails

let options: CFDictionary = [
    kCGImageSourceThumbnailMaxPixelSize as String: 200,
    kCGImageSourceCreateThumbnailFromImageAlways as String: true
]

let thumbnail = CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

Reading Image Properties

let properties = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil)

// Access dimensions
let width = properties![kCGImagePropertyPixelWidth as String] as? Int
let height = properties![kCGImagePropertyPixelHeight as String] as? Int

// Access EXIF data
if let exif = properties![kCGImagePropertyExifDictionary as String] as? [String: Any] {
    let exposureTime = exif[kCGImagePropertyExifExposureTime as String]
    let fNumber = exif[kCGImagePropertyExifFNumber as String]
}

// Access GPS data
if let gps = properties![kCGImagePropertyGPSDictionary as String] as? [String: Any] {
    let latitude = gps[kCGImagePropertyGPSLatitude as String]
    let longitude = gps[kCGImagePropertyGPSLongitude as String]
}

Writing Images

let data = CFMutableData()
let destination = CGImageDestinationCreateWithData(
    data,
    "public.png" as CFString,
    1,
    nil
)!

CGImageDestinationAddImage(destination, image, nil)

if CGImageDestinationFinalize(destination) {
    // data now contains the encoded PNG
}

Writing JPEG with Quality

let options: CFDictionary = [
    kCGImageDestinationLossyCompressionQuality as String: 0.8
]

let destination = CGImageDestinationCreateWithData(
    data,
    "public.jpeg" as CFString,
    1,
    nil
)!

CGImageDestinationAddImage(destination, image, options)
CGImageDestinationFinalize(destination)

Working with Metadata

// Create mutable metadata
let metadata = CGImageMetadataCreateMutable()

// Set values
CGImageMetadataSetValueWithPath(
    metadata,
    nil,
    "dc:title" as CFString,
    "My Photo"
)

// Read values
let title = CGImageMetadataCopyStringValueWithPath(
    metadata,
    nil,
    "dc:title" as CFString
)

// Create XMP data
let xmpData = CGImageMetadataCreateXMPData(metadata, nil)

Incremental Loading

let source = CGImageSourceCreateIncremental(nil)

// Feed partial data
CGImageSourceUpdateData(source, partialData, false)

// Check status
let status = CGImageSourceGetStatus(source)
if status == .statusIncomplete {
    // Wait for more data...
}

// Feed complete data
CGImageSourceUpdateData(source, completeData, true)

Supported Formats

| Format | Read | Write | Type Identifier | |--------|------|-------|-----------------| | PNG | ✅ | ✅ | public.png | | JPEG | ✅ | ✅ | public.jpeg | | GIF | ✅ | ✅ | com.compuserve.gif | | BMP | ✅ | ✅ | com.microsoft.bmp | | TIFF | ✅ | ✅ | public.tiff | | WebP | ✅ | ✅ | org.webmproject.webp |

Unsupported Formats

| Format | Status | Reason | |--------|--------|--------| | HEIF/HEIC | ❌ | Requires HEVC (H.265) codec - complex implementation with patent licensing | | AVIF | ❌ | Requires AV1 codec | | RAW | ❌ | Camera-specific formats (CR2, NEF, ARW, etc.) |

Note: HEIF/HEIC support would require implementing an HEVC decoder (thousands of lines of code) or using external libraries like libheif compiled to WebAssembly.

Supported Metadata

  • EXIF - Exposure, aperture, ISO, date/time, camera settings
  • IPTC - Copyright, caption, keywords, location
  • GPS - Latitude, longitude, altitude, timestamps
  • TIFF - Resolution, orientation, software, artist
  • XMP - Dublin Core, Photoshop, IPTC Core, EXIF
  • DNG - Complete Digital Negative metadata support
  • HEIC/HEIF - Property keys defined (format itself not supported, see above)
  • Maker Notes - Canon, Nikon, Apple, Fuji, Olympus, Pentax, Minolta

Building

# Build for current platform
swift build

# Build for WebAssembly (requires SwiftWasm)
swift build --triple wasm32-unknown-wasi

# Run tests
swift test

WASM-Build Smoke Test

OpenImageIO has no browser-side runtime — it is a pure-Swift codec library with no WebGPU or JavaScriptKit dependency. The only per-platform regression we can catch is the WASM toolchain failing to compile the sources. The script at tests/wasm-build.sh does exactly that:

bash tests/wasm-build.sh
# Exits 0 on success, nonzero on compile failure.
# Uses swift-6.3.1-RELEASE_wasm by default; override via WASM_SDK=<name>.

This is the tier-3 "WASM-build smoke" described in the workspace CLAUDE.md. Run it before cutting a release.

Cross-Platform Pattern

The recommended pattern for cross-platform code:

#if canImport(ImageIO)
import ImageIO
import CoreGraphics
#else
import OpenImageIO
#endif

func processImage(data: Data) -> (width: Int, height: Int)? {
    #if canImport(ImageIO)
    let cfData = data as CFData
    #else
    let cfData = CFData(bytes: Array(data))
    #endif

    guard let source = CGImageSourceCreateWithData(cfData, nil),
          let image = CGImageSourceCreateImageAtIndex(source, 0, nil) else {
        return nil
    }

    return (image.width, image.height)
}

API Reference

CGImageSource Functions

| Function | Description | |----------|-------------| | CGImageSourceCreateWithData | Create source from data | | CGImageSourceCreateWithURL | Create source from file URL | | CGImageSourceCreateWithDataProvider | Create source from data provider | | CGImageSourceCreateIncremental | Create incremental source | | CGImageSourceGetCount | Get number of images | | CGImageSourceGetType | Get image format type | | CGImageSourceGetStatus | Get loading status | | CGImageSourceCreateImageAtIndex | Extract image | | CGImageSourceCreateThumbnailAtIndex | Create thumbnail | | CGImageSourceCopyProperties | Get source properties | | CGImageSourceCopyPropertiesAtIndex | Get image properties | | CGImageSourceUpdateData | Update incremental source |

CGImageDestination Functions

| Function | Description | |----------|-------------| | CGImageDestinationCreateWithData | Create destination to data | | CGImageDestinationCreateWithURL | Create destination to file | | CGImageDestinationCreateWithDataConsumer | Create destination to consumer | | CGImageDestinationAddImage | Add image to destination | | CGImageDestinationAddImageFromSource | Add image from source | | CGImageDestinationSetProperties | Set destination properties | | CGImageDestinationFinalize | Finalize and write output |

CGImageMetadata Functions

| Function | Description | |----------|-------------| | CGImageMetadataCreateMutable | Create mutable metadata | | CGImageMetadataCreateMutableCopy | Copy metadata | | CGImageMetadataCopyTags | Get all tags | | CGImageMetadataCopyTagWithPath | Get tag by path | | CGImageMetadataSetValueWithPath | Set value | | CGImageMetadataRemoveTagWithPath | Remove tag | | CGImageMetadataCreateXMPData | Serialize to XMP | | CGImageMetadataCreateFromXMPData | Parse from XMP |

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

This library aims to provide API compatibility with Apple's ImageIO framework, enabling Swift developers to write cross-platform image handling code.

Package Metadata

Repository: 1amageek/OpenImageIO

Stars: 0

Forks: 0

Open issues: 0

Default branch: main

Primary language: swift

License: MIT

README: README.md