AsyncGraphics is a Swift package for working with images and video with concurrency on the GPU. The core type is called Graphic, it's like an image and is backed by a MTLTexture.
Documentation (DocC)
See the Graphic docs for all content and effects.
Image, Video, Camera, View, Screen, Maps
Rectangle, Circle, Polygon, Arc, Star, Line
Levels (Brightness, Contrast, Invert, Opacity), Luma Levels, Color Shift (Hue, Saturation), Luma Color Shift, Sepia, Threshold, Quantize, Lookup, Gradient Lookup, Clamp, Cross (Fade), Chroma Key (Green Screen), Person Segmentation (Background Removal), Color Convert, Range
Blur, Rainbow Blur, Luma Rainbow Blur, Pixelate, Displace, Edge, Sharpen, Kaleidoscope, Morph, Channel Mix
Stack, Resize (Resolution), Mirror, Rotate
Pixels, Channels, Stereoscopic
Crop, Padding, Corner Pin, Sample Line, Slope, Remap, Polar, Flood Fill, Reduce, LUTs, Buffer
.package(url: "https://github.com/heestand-xyz/AsyncGraphics", from: "3.0.0")In AsyncGraphics there are a couple ways to present a graphic.
- AGView to declaratively view AGGraphs
- GraphicView to imperatively view Graphics
- AsyncGraphicView to imperatively view Graphics asynchronously.
- Graphic3DView to view Graphic3Ds
import AsyncGraphics
struct ContentView: View {
var body: some View {
AsyncGraphicView { resolution in
try await .circle(resolution: resolution)
}
}
}or for more control:
import AsyncGraphics
struct ContentView: View {
private static let resolution = CGSize(width: 1000, height: 1000)
@State private var graphic: Graphic?
var body: some View {
ZStack {
if let graphic {
GraphicView(graphic: graphic)
}
}
.task {
graphic = try? await .circle(resolution: Self.resolution)
}
}
}To import and export images there are multiple ways to do this. The simplest way is to load an asset from the asset catalog by name. If the asset does not exist, an error will be thrown.
let graphic: Graphic = try await .image(named: "Kite")To export an UIImage or NSImage just call try await graphic.image or for a SwiftUI image try await graphic.imageForSwiftUI.
First we create an AGView, this is the container for all AGGraphs. In this example we have a AGZStack with 3 AGHStacks. Each graph has a blend mode (AGBlendMode), in this case .screen.
import SwiftUI
import AsyncGraphics
struct ContentView: View {
var body: some View {
AGView {
AGZStack {
AGHStack {
AGSpacer()
AGCircle()
.foregroundColor(.red)
}
AGHStack {
AGSpacer()
AGCircle()
.foregroundColor(.green)
AGSpacer()
}
.blendMode(.screen)
AGHStack {
AGCircle()
.foregroundColor(.blue)
AGSpacer()
}
.blendMode(.screen)
}
}
}
}First we create an AGView, this is the container for all AGGraphs. In this example we create an AGHStack to contain the boxes, then we loop 3 times with an AGForEach, calculate the width and create AGRoundedRectangles. After that we set the frame to get a fixed size and apply a color. After the stack we apply some padding and finally add a background.
import SwiftUI
import AsyncGraphics
struct ContentView: View {
var body: some View {
AGView {
AGHStack(alignment: .top, spacing: 15) {
AGForEach(0..<3) { index in
let width = 50 * CGFloat(index + 1)
AGRoundedRectangle(cornerRadius: 15)
.frame(width: width, height: width)
.foregroundColor(Color(hue: Double(index) / 3,
saturation: 0.5,
brightness: 1.0))
}
}
.padding(15)
.background {
AGRoundedRectangle(cornerRadius: 30)
.opacity(0.1)
}
}
}
}import SwiftUI
import AsyncGraphics
struct ContentView: View {
var body: some View {
AGView {
AGZStack {
AGCamera(.front)
.resizable()
.aspectRatio(contentMode: .fill)
AGCircle()
.blendMode(.multiply)
}
}
}
}You can also do the same with Graphics:
import SwiftUI
import AsyncGraphics
struct ContentView: View {
@State private var graphic: Graphic?
var body: some View {
ZStack {
if let graphic {
GraphicView(graphic: graphic)
}
}
.task {
do {
let resolution = CGSize(width: 1_000, height: 1_000)
let circleGraphic: Graphic = try await .circle(radius: 500,
backgroundColor: .clear,
resolution: resolution)
for await cameraGraphic in try Graphic.camera(.front) {
graphic = try await circleGraphic
.blended(with: cameraGraphic,
blendingMode: .multiply,
placement: .fill)
}
} catch {
print(error)
}
}
}
}Remember to set the Info.plist key
NSCameraUsageDescription"Privacy - Camera Usage Description"
There is the option to write high level metal code in AsyncGraphics. No need to setup a pipeline.
Colors are represented with the PixelColor type.
import PixelColor to create custom colors with hex values.
PixelColor on GitHub.
Most shapes can be tiled (rendered in chunks).
Use the tiled methods to render tiles. Add padding if using a space effect, like blur or displace.
Created by Anton Heestand

