📷 A composable image editor using Core Image and Metal.
翻訳待ち
デザインSwift · MetalGitHub @FluidGroup自分でインストール所有者未確認
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Build a photo editor with the included editor UI, compose your own editing interface, or use the rendering engine on its own. Brightroom keeps edits as parameters, so you can change adjustments, revisit crops, and render again from the original image.
ImageEditor.ImageEditorCanvas, which edits the same document as the built-in editor.EditingDocument keeps the source image, the edit as a value composed from typed features, and its undo history. Undo, redo, and rendering are operations on that value.SourceImage from a UIImage, a CGImage, encoded data, a file, RAW data, or a Photos asset. Download remote images yourself and pass the data.Choose the product that matches what you want to build:
| Product | Purpose | Platforms |
|---|---|---|
BrightroomUI | The built-in image editor and a reusable editing canvas for SwiftUI | iOS 17+ |
BrightroomEngine | Source images, editing documents with undo history, and rendering | iOS 17+ |
BrightroomParametric | Typed features, masks, and Core Image rendering, with no third-party dependencies and no SwiftUI | iOS 17+, macOS 14+ |
The Swift package requires Swift 6.3 or later and uses Swift 6 language mode. Use an Xcode toolchain that includes Swift 6.3 or later. The built-in UI uses UIKit internally; UIKit apps can present it through UIHostingController.
Add https://github.com/FluidGroup/Brightroom.git in Xcode's package dependencies, or declare it in your Package.swift:
dependencies: [
.package(url: "https://github.com/FluidGroup/Brightroom.git", from: "6.0.0")
]
For an image editor, add BrightroomUI, BrightroomEngine, and BrightroomParametric to your app target. In a Swift package target, use:
.target(
name: "MyApp",
dependencies: [
.product(name: "BrightroomUI", package: "Brightroom"),
.product(name: "BrightroomEngine", package: "Brightroom"),
.product(name: "BrightroomParametric", package: "Brightroom"),
]
)
For processing without the editor, add BrightroomParametric alone.
ImageEditor edits an ImageEditorDocument that your app owns. The document holds the source image, the edit (an ImageEditorFeature), and the edit's undo history. Create it before showing the editor, and keep it in your own state:
import BrightroomEngine
import BrightroomParametric
import BrightroomUI
import SwiftUI
import UIKit
/// Edits one image and delivers the exported image or a rendering error.
struct PhotoEditor: View {
@Environment(\.dismiss) private var dismiss
@State private var document: ImageEditorDocument
@State private var isExporting = false
private let onComplete: (Result<UIImage, Error>) -> Void
init(
image: UIImage,
onComplete: @escaping (Result<UIImage, Error>) -> Void
) {
_document = State(initialValue: ImageEditorDocument(source: SourceImage(image: image)))
self.onComplete = onComplete
}
var body: some View {
ImageEditor(
document: $document,
onDone: export,
onCancel: { dismiss() }
)
.disabled(isExporting)
.overlay {
if isExporting {
ProgressView()
}
}
}
private func export() {
guard !isExporting else { return }
isExporting = true
let document = document
Task {
defer { isExporting = false }
do {
let rendered = try await document.render()
onComplete(.success(try rendered.uiImage))
} catch {
onComplete(.failure(error))
}
}
}
}
The editor prepares the image itself. Each completed operation, such as a filter tap, a slider drag, a brush stroke, or an applied crop, is written to the binding once. On Cancel, the editor writes back the document it opened with, history included, discarding everything written since (your own writes to the binding too), and then calls onCancel; pass nil for onCancel to hide the Cancel button. The editor never registers anything with an UndoManager: undo and redo are document.undo() and document.redo(), so you can drive them from your own buttons.
For large images, prefer SourceImage(contentsOf:) or SourceImage(data:), which decode only when the image is rendered or prepared. SourceImage(asset:) loads a Photos asset. To start from a crop that frames a face, create the document with await ImageEditorDocument(source:initialCrop:) and .faceDetection(aspectRatio:); .aspectRatio(_:) starts from a centered crop of a ratio.
This is the beginning of the README. Read the rest at the original. Read the full README ↗