🎨 GPU accelerated image / video and camera filter library based on Metal. Support macOS & iOS. 图像、视频、相机滤镜框架
Metal
717
659 commits
updated Sep 29, 2026
| Soul | Combination |
|---|---|
![]() | ![]() |
A texture-first Metal render engine for image and frame pipelines on Apple platforms.
Harbeth processes UIImage / NSImage, CGImage, CIImage, MTLTexture, CVPixelBuffer, and CMSampleBuffer. It provides filters, render graphs, masks, transitions, geometry and optics primitives, output contracts, diagnostics, and preview hosting.
It is the rendering core inside a product, not the product workflow itself: the host decides interaction, media lifecycle, persistence, and product policy; Harbeth turns one image or frame description into a GPU-rendered result with explicit output semantics.
English | 简体中文
| Platform | Minimum |
|---|---|
| iOS / iPadOS | 15.0 |
| macOS | 12.0 |
| tvOS | 15.0 |
| Toolchain | Xcode 16+, Swift 6 |
Build with Xcode 26 or later (Swift 6.2 and Apple platform SDKs 26 or later). Deployment targets remain iOS/iPadOS 15, macOS 12 and tvOS 15. CI is pinned to Xcode 26.3; Swift language mode remains 6.
dependencies: [
.package(url: "https://github.com/yangKJ/Harbeth.git", from: "3.0.0")
]
Add Harbeth to the target that renders images or frames.
pod 'Harbeth', '~> 3.0'
Harbeth exposes two normal integration routes. Runtime, analysis, recipes, and preview hosts support these routes; they are not additional entry points.
| Route | Use it when | Canonical result |
|---|---|---|
HarbethIO | You already have a source and a filter chain | try output() / await transmitOutput() |
ImageNode | You need Harbeth's advanced unified graph, editing, contract, inspection, and delivery surface | makeTexture() / makeFrame() / makeFrameAsync() |
| Source | Direct HarbethIO result | Advanced ImageNode result |
|---|---|---|
UIImage / NSImage (C7Image) | Same image type | Texture, frame, image readback |
CGImage / CIImage | Same source type | Texture, frame, image readback |
MTLTexture | MTLTexture | Texture or metadata-carrying frame |
CVPixelBuffer | CVPixelBuffer | Texture, frame, attachments |
CMSampleBuffer | CMSampleBuffer | Texture, frame, preview-host metadata |
Data / ImageAsset | Use ImageNode for decoded assets | Texture, frame, diagnostics |
HarbethIOlet outputImage = try HarbethIO(
element: inputImage,
filters: [
C7Exposure(exposure: 0.25),
C7Contrast(contrast: 1.08),
C7Saturation(saturation: 0.94)
]
).output()
output() is the primary synchronous API because rendering failures remain observable. transmitOutput(...) is its core asynchronous counterpart.
For non-blocking submission, use the async counterpart:
let outputImage = try await HarbethIO(
element: inputImage,
filters: filters
).transmitOutput()
Callback-based integrations can use transmitOutput(outputColorSpace:complete:). Filtered work is encoded on Harbeth's render operation queue and completes after GPU completion under the default profile. The callback queue is unspecified, so UI updates must return to MainActor. The no-filter fast path may complete inline.
For low-latency asynchronous texture pipelines, keep the established
transmitOutputRealTimeCommit switch as the single public control:
var io = HarbethIO(
element: inputTexture,
filters: filters
)
io.transmitOutputRealTimeCommit = true
let outputTexture = try await io.transmitOutput()
When the switch is true, asynchronous texture-first output may be delivered after its command buffer is scheduled rather than completed. The switch does not change synchronous output() behavior. Image, pixel-buffer and sample-buffer outputs still wait for GPU completion before CPU materialization, even when the switch is enabled.
ImageNodeImageNode is Harbeth's advanced unified entry. Use it when a render description must carry more than a one-shot filter chain: reusable sources, structured edits, output intent, metadata, cache policy, diagnostics, or multiple delivery forms.
let node = ImageNode.image(inputImage)
.applying(C7Exposure(exposure: 0.25))
.applying(C7Contrast(contrast: 1.08))
.transforming(ImageTransformRecipe(rotationDegrees: 90))
.withCachePolicy(.transient)
let previewFrame = try node.makeFrame(profile: .stablePreview)
let backgroundFrame = try await node.makeFrameAsync(profile: .stablePreview)
let exportTexture = try node.makeTexture(profile: .exportQuality)
Its core capabilities are:
CGImage, CIImage, texture, pixel buffer, sample buffer, encoded data, and ImageAsset.EditRecipe, Geometry, Optics, local effects, gradient/shape/path/composite masks, and reusable preview/final modes.RenderProfile, ImageDerivativeSpec, color/alpha/orientation/source-tier metadata, and texture ownership travel with RenderedFrame.RenderRequest, histogram/statistics/color probes, masks, and output attachments.Use makeTexture() for a texture result, makeFrame() when metadata and host delivery matter, makeFrameAsync() when the caller must not block, and makeRenderRequest() when execution must be deferred or inspected. When a node already exists, prefer instance chains such as node.editing(...), node.transforming(...), and node.applying(optics: ...). Recipe families are editing-description primitives inside the ImageNode route, not a third public route.
UIKit and AppKit can host a RenderedFrame directly:
renderView.display(previewFrame)
SwiftUI can use the same Harbeth preview substrate without image readback:
HarbethRenderView(
frame: previewFrame,
resizingMode: .aspectFit
)
RenderView and HarbethRenderView are render-output hosts. Harbeth retains frame metadata, chooses an available backing strategy, handles visibility pause/resume, and exposes execution reports. The host application still owns media capture, playback, recording, timeline, export, and persistence.
Keep image readback explicit and local to the host UI. Use HarbethRenderView whenever SwiftUI previews a texture or RenderedFrame.
C7Blend modes.C7FilterPipelineProtocol.MTLHeap allocation, request budgets, binary archives, derived-resource governance, prewarming, render-plan caching, and stable fingerprints..metal, .metallib, and optional Metal library-provider integration.Harbeth is capability-driven: support for a contract or platform does not imply that every device has the same Metal feature set. Query capability reports and use the documented fallback behavior for advanced features.
Heap-backed allocation is opt-in and uses real MTLHeap resources in 3.0. It keeps the public workflow on HarbethIO / ImageNode, while the runtime owns descriptor compatibility, budgets, memory pressure, leases, and direct-allocation fallback. See the 3.0 Migration Guide before enabling it.
Common pointwise adjustments can execute as one Metal dispatch when their pixel contracts prove the chain safe to fuse. Neighbor sampling, multi-texture kernels, global dependencies, CPU readback, and explicit barriers remain separate passes. RenderRequest can reject work against an explicit resource budget before allocating textures, compare preview/export parity, and return allocator-observed resource reports. These are supporting contracts under the two canonical routes, not additional processing APIs.
Harbeth throws HarbethError from its canonical processing APIs and is silent by default. Route structured events into your own logger when needed:
HarbethLogger.minimumLevel = .warning
HarbethLogger.handler = { event in
appLogger.log("[\(event.category)] \(event.message)")
}
Generate a privacy-safe environment summary for a GitHub Issue:
let supportJSON = try HarbethSupportSnapshot.capture().json()
The snapshot contains the Harbeth version, platform, OS, Metal device name, and monitoring state. It does not include images or user data.
Harbeth keeps high-frequency routes texture-first, caches render plans and pipeline states, and can reuse texture allocations. Performance depends on device, source dimensions, pixel format, filter chain, and output contract; the project does not claim one universal speed multiplier.
Use RenderProfile.interactiveLatency for low-latency presentation, stablePreview for reusable preview output, and exportQuality / readbackQuality when completion or CPU access is part of the contract. See Performance Governance for repeatable measurement rules.
The workspace contains three integration workbenches:
Harbeth-iOS-Demo: UIKit showcase, ImageNode Lab, camera-frame reference wiring, masks, and the complete filter catalog.Harbeth-SwiftUI-Demo: SwiftUI route selection and output-host examples.Harbeth-macOS-Demo: AppKit filter and desktop integration examples.These are workbenches for Harbeth's render capabilities, not packaged camera or video-editing SDKs.
Bug reports should use the repository Issue form and include a minimal HarbethIO or ImageNode reproduction plus a HarbethSupportSnapshot. Use Discussions for integration questions and architecture tradeoffs.
Harbeth is maintained as long-term open-source infrastructure, not as a one-off sample project. The work that makes a GPU engine dependable is often the least visible: following Apple platform changes, reproducing edge cases, profiling real workloads, refining API contracts, and keeping the documentation honest.
If Harbeth has saved you an afternoon of Metal infrastructure work, helped you avoid a production issue, or become a dependable part of your rendering stack, consider returning a small part of that value in the way that fits you best:
Support is never a paywall or an obligation. Harbeth remains available under the MIT License; sponsorship simply makes it easier to give platform changes, regressions, and difficult edge cases the attention they deserve.
For one-time support via Alipay or WeChat:
Maintainer: yangKJ · yangkj310@gmail.com
Harbeth is available under the MIT License.
174 followers · starred Feb 2026
🎨 GPU accelerated image / video and camera filter library based on Metal. Support macOS & iOS. 图像、视频、相机滤镜框架
Metal
717
659 commits
updated Sep 29, 2026
| Soul | Combination |
|---|---|
![]() | ![]() |
A texture-first Metal render engine for image and frame pipelines on Apple platforms.
Harbeth processes UIImage / NSImage, CGImage, CIImage, MTLTexture, CVPixelBuffer, and CMSampleBuffer. It provides filters, render graphs, masks, transitions, geometry and optics primitives, output contracts, diagnostics, and preview hosting.
It is the rendering core inside a product, not the product workflow itself: the host decides interaction, media lifecycle, persistence, and product policy; Harbeth turns one image or frame description into a GPU-rendered result with explicit output semantics.
English | 简体中文
| Platform | Minimum |
|---|---|
| iOS / iPadOS | 15.0 |
| macOS | 12.0 |
| tvOS | 15.0 |
| Toolchain | Xcode 16+, Swift 6 |
Build with Xcode 26 or later (Swift 6.2 and Apple platform SDKs 26 or later). Deployment targets remain iOS/iPadOS 15, macOS 12 and tvOS 15. CI is pinned to Xcode 26.3; Swift language mode remains 6.
dependencies: [
.package(url: "https://github.com/yangKJ/Harbeth.git", from: "3.0.0")
]
Add Harbeth to the target that renders images or frames.
pod 'Harbeth', '~> 3.0'
Harbeth exposes two normal integration routes. Runtime, analysis, recipes, and preview hosts support these routes; they are not additional entry points.
| Route | Use it when | Canonical result |
|---|---|---|
HarbethIO | You already have a source and a filter chain | try output() / await transmitOutput() |
ImageNode | You need Harbeth's advanced unified graph, editing, contract, inspection, and delivery surface | makeTexture() / makeFrame() / makeFrameAsync() |
| Source | Direct HarbethIO result | Advanced ImageNode result |
|---|---|---|
UIImage / NSImage (C7Image) | Same image type | Texture, frame, image readback |
CGImage / CIImage | Same source type | Texture, frame, image readback |
MTLTexture | MTLTexture | Texture or metadata-carrying frame |
CVPixelBuffer | CVPixelBuffer | Texture, frame, attachments |
CMSampleBuffer | CMSampleBuffer | Texture, frame, preview-host metadata |
Data / ImageAsset | Use ImageNode for decoded assets | Texture, frame, diagnostics |
HarbethIOlet outputImage = try HarbethIO(
element: inputImage,
filters: [
C7Exposure(exposure: 0.25),
C7Contrast(contrast: 1.08),
C7Saturation(saturation: 0.94)
]
).output()
output() is the primary synchronous API because rendering failures remain observable. transmitOutput(...) is its core asynchronous counterpart.
For non-blocking submission, use the async counterpart:
let outputImage = try await HarbethIO(
element: inputImage,
filters: filters
).transmitOutput()
Callback-based integrations can use transmitOutput(outputColorSpace:complete:). Filtered work is encoded on Harbeth's render operation queue and completes after GPU completion under the default profile. The callback queue is unspecified, so UI updates must return to MainActor. The no-filter fast path may complete inline.
For low-latency asynchronous texture pipelines, keep the established
transmitOutputRealTimeCommit switch as the single public control:
var io = HarbethIO(
element: inputTexture,
filters: filters
)
io.transmitOutputRealTimeCommit = true
let outputTexture = try await io.transmitOutput()
When the switch is true, asynchronous texture-first output may be delivered after its command buffer is scheduled rather than completed. The switch does not change synchronous output() behavior. Image, pixel-buffer and sample-buffer outputs still wait for GPU completion before CPU materialization, even when the switch is enabled.
ImageNodeImageNode is Harbeth's advanced unified entry. Use it when a render description must carry more than a one-shot filter chain: reusable sources, structured edits, output intent, metadata, cache policy, diagnostics, or multiple delivery forms.
let node = ImageNode.image(inputImage)
.applying(C7Exposure(exposure: 0.25))
.applying(C7Contrast(contrast: 1.08))
.transforming(ImageTransformRecipe(rotationDegrees: 90))
.withCachePolicy(.transient)
let previewFrame = try node.makeFrame(profile: .stablePreview)
let backgroundFrame = try await node.makeFrameAsync(profile: .stablePreview)
let exportTexture = try node.makeTexture(profile: .exportQuality)
Its core capabilities are:
CGImage, CIImage, texture, pixel buffer, sample buffer, encoded data, and ImageAsset.EditRecipe, Geometry, Optics, local effects, gradient/shape/path/composite masks, and reusable preview/final modes.RenderProfile, ImageDerivativeSpec, color/alpha/orientation/source-tier metadata, and texture ownership travel with RenderedFrame.RenderRequest, histogram/statistics/color probes, masks, and output attachments.Use makeTexture() for a texture result, makeFrame() when metadata and host delivery matter, makeFrameAsync() when the caller must not block, and makeRenderRequest() when execution must be deferred or inspected. When a node already exists, prefer instance chains such as node.editing(...), node.transforming(...), and node.applying(optics: ...). Recipe families are editing-description primitives inside the ImageNode route, not a third public route.
UIKit and AppKit can host a RenderedFrame directly:
renderView.display(previewFrame)
SwiftUI can use the same Harbeth preview substrate without image readback:
HarbethRenderView(
frame: previewFrame,
resizingMode: .aspectFit
)
RenderView and HarbethRenderView are render-output hosts. Harbeth retains frame metadata, chooses an available backing strategy, handles visibility pause/resume, and exposes execution reports. The host application still owns media capture, playback, recording, timeline, export, and persistence.
Keep image readback explicit and local to the host UI. Use HarbethRenderView whenever SwiftUI previews a texture or RenderedFrame.
C7Blend modes.C7FilterPipelineProtocol.MTLHeap allocation, request budgets, binary archives, derived-resource governance, prewarming, render-plan caching, and stable fingerprints..metal, .metallib, and optional Metal library-provider integration.Harbeth is capability-driven: support for a contract or platform does not imply that every device has the same Metal feature set. Query capability reports and use the documented fallback behavior for advanced features.
Heap-backed allocation is opt-in and uses real MTLHeap resources in 3.0. It keeps the public workflow on HarbethIO / ImageNode, while the runtime owns descriptor compatibility, budgets, memory pressure, leases, and direct-allocation fallback. See the 3.0 Migration Guide before enabling it.
Common pointwise adjustments can execute as one Metal dispatch when their pixel contracts prove the chain safe to fuse. Neighbor sampling, multi-texture kernels, global dependencies, CPU readback, and explicit barriers remain separate passes. RenderRequest can reject work against an explicit resource budget before allocating textures, compare preview/export parity, and return allocator-observed resource reports. These are supporting contracts under the two canonical routes, not additional processing APIs.
Harbeth throws HarbethError from its canonical processing APIs and is silent by default. Route structured events into your own logger when needed:
HarbethLogger.minimumLevel = .warning
HarbethLogger.handler = { event in
appLogger.log("[\(event.category)] \(event.message)")
}
Generate a privacy-safe environment summary for a GitHub Issue:
let supportJSON = try HarbethSupportSnapshot.capture().json()
The snapshot contains the Harbeth version, platform, OS, Metal device name, and monitoring state. It does not include images or user data.
Harbeth keeps high-frequency routes texture-first, caches render plans and pipeline states, and can reuse texture allocations. Performance depends on device, source dimensions, pixel format, filter chain, and output contract; the project does not claim one universal speed multiplier.
Use RenderProfile.interactiveLatency for low-latency presentation, stablePreview for reusable preview output, and exportQuality / readbackQuality when completion or CPU access is part of the contract. See Performance Governance for repeatable measurement rules.
The workspace contains three integration workbenches:
Harbeth-iOS-Demo: UIKit showcase, ImageNode Lab, camera-frame reference wiring, masks, and the complete filter catalog.Harbeth-SwiftUI-Demo: SwiftUI route selection and output-host examples.Harbeth-macOS-Demo: AppKit filter and desktop integration examples.These are workbenches for Harbeth's render capabilities, not packaged camera or video-editing SDKs.
Bug reports should use the repository Issue form and include a minimal HarbethIO or ImageNode reproduction plus a HarbethSupportSnapshot. Use Discussions for integration questions and architecture tradeoffs.
Harbeth is maintained as long-term open-source infrastructure, not as a one-off sample project. The work that makes a GPU engine dependable is often the least visible: following Apple platform changes, reproducing edge cases, profiling real workloads, refining API contracts, and keeping the documentation honest.
If Harbeth has saved you an afternoon of Metal infrastructure work, helped you avoid a production issue, or become a dependable part of your rendering stack, consider returning a small part of that value in the way that fits you best:
Support is never a paywall or an obligation. Harbeth remains available under the MIT License; sponsorship simply makes it easier to give platform changes, regressions, and difficult edge cases the attention they deserve.
For one-time support via Alipay or WeChat:
Maintainer: yangKJ · yangkj310@gmail.com
Harbeth is available under the MIT License.
174 followers · starred Feb 2026