Skip to content

Repository files navigation

Harbeth

Soul Combination

CI GitHub Release CocoaPods License Platforms Swift

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 | 简体中文

Requirements

Platform Minimum
iOS / iPadOS 15.0
macOS 12.0
tvOS 15.0
Toolchain Xcode 16+, Swift 6

Installation

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/yangKJ/Harbeth.git", from: "3.0.0")
]

Add Harbeth to the target that renders images or frames.

CocoaPods

pod 'Harbeth', '~> 3.0'

Choose One of Two Routes

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 and result map

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

1. Direct processing with HarbethIO

let 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.

2. Structured processing with ImageNode

ImageNode 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:

  • Unified sources: image, CGImage, CIImage, texture, pixel buffer, sample buffer, encoded data, and ImageAsset.
  • Composable processing: filters, explicit kernel contracts, plugins, cache policy, and sampler policy.
  • Structured editing: EditRecipe, Geometry, Optics, local effects, gradient/shape/path/composite masks, and reusable preview/final modes.
  • Multi-source composition: transitions and ordered layer composites with masks, transforms, blend contracts, and output contracts.
  • Stable delivery semantics: RenderProfile, ImageDerivativeSpec, color/alpha/orientation/source-tier metadata, and texture ownership travel with RenderedFrame.
  • Inspection and replay: image graph, diagnostics, debug snapshot, deferred 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.

Texture-First Preview

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.

Engine Capabilities

  • Image, texture, pixel-buffer, and sample-buffer input/output paths.
  • Compute, render, blit, MPS, filter pipelines, and an explicit Metal command-encoding escape hatch; see the source-aligned Filter Catalog for all 183 public execution types and 30 C7Blend modes.
  • Color adjustment, blur, blend, edge/detail, geometry, optics, LUT/Cube, utility, generator, and quality filters.
  • Public Combination filters implemented through C7FilterPipelineProtocol.
  • Mask, local-effect, layer-composite, transition, and edit-recipe primitives.
  • Texture pooling, real MTLHeap allocation, request budgets, binary archives, derived-resource governance, prewarming, render-plan caching, and stable fingerprints.
  • Alpha, working/output color profiles, YUV, HDR metadata, output quantization, output-size, orientation, and readback contracts.
  • GPU waveform/vectorscope, histogram, statistics, probes, graph snapshots, preview/export parity, and performance metrics.
  • Custom .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.

Errors, Logs, and Bug Reports

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.

Performance

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.

Demo

The workspace contains three integration workbenches:

These are workbenches for Harbeth's render capabilities, not packaged camera or video-editing SDKs.

Documentation

Contributing

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.

Support Long-Term Maintenance

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:

  • Star or share the project to help other Apple-platform developers discover it.
  • Use GitHub Sponsors for ongoing support.
  • Use Buy Me a Coffee, Alipay, or WeChat for a one-time show of support.

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.

Buy me a coffee GitHub Sponsors

For one-time support via Alipay or WeChat:

Alipay support QR code WeChat support QR code

Maintainer: yangKJ · yangkj310@gmail.com

License

Harbeth is available under the MIT License.

About

🎨 GPU accelerated image / video and camera filter library based on Metal. Support macOS & iOS. 图像、视频、相机滤镜框架

Topics

Resources

Stars

710 stars

Watchers

12 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages