Skip to content

heestand-xyz/AsyncGraphics

Repository files navigation

AsyncGraphics

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

Documentation (DocC)

See the Graphic docs for all content and effects.

Articles

Resources

Image, Video, Camera, View, Screen, Maps

Shapes

Rectangle, Circle, Polygon, Arc, Star, Line

Visuals

Color, Gradient, Noise

Particles

Particles

Color Effects

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

Space Effects

Blur, Rainbow Blur, Luma Rainbow Blur, Pixelate, Displace, Edge, Sharpen, Kaleidoscope, Morph, Channel Mix

Layout

Stack, Resize (Resolution), Mirror, Rotate

Info

Pixels, Channels, Stereoscopic

Convert

Crop, Padding, Corner Pin, Sample Line, Slope, Remap, Polar, Flood Fill, Reduce, LUTs, Buffer

Detection

Face Detection

Metal

Metal

Install

.package(url: "https://github.com/heestand-xyz/AsyncGraphics", from: "3.0.0")

Views

In AsyncGraphics there are a couple ways to present a graphic.

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)
        }
    }
}

Examples

Images

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.

Blending

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)
            }
        }
    }
}

Layout

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)
            }
        }
    }
}

Camera

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"

Metal

There is the option to write high level metal code in AsyncGraphics. No need to setup a pipeline.

Colors

Colors are represented with the PixelColor type.

import PixelColor to create custom colors with hex values.

PixelColor on GitHub.

Tiling

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.

Author

Created by Anton Heestand

License

MIT

About

Edit images and video with Swift concurrency, powered by Metal.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •