• Stars
    star
    808
  • Rank 56,429 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Live Graphics in Swift & Metal

Check out AsyncGraphics.
Inspired by PixelKit & RenderKit.

PixelKit

Live Graphics for iOS, macOS and tvOS
Runs on RenderKit, powered by Metal

PixelKit combines custom shaders, metal performance shaders, core image filters and vision to create tools for real-time rendering.

Examples: Camera Effects - Green Screen
Info: Coordinate Space - Blend Operators - Effect Convenience Funcs - High Bit Mode

CameraPIX DepthCameraPIX ImagePIX VideoPIX ScreenCapturePIX StreamInPIX SlopePIX
ColorPIX CirclePIX RectanglePIX PolygonPIX ArcPIX LinePIX GradientPIX StackPIX
NoisePIX TextPIX MetalPIX TwirlPIX FeedbackPIX DelayPIX SharpenPIX StreamOutPIX
LevelsPIX BlurPIX EdgePIX ThresholdPIX QuantizePIX TransformPIX KaleidoscopePIX
ChannelMixPIX ChromaKeyPIX CornerPinPIX ColorShiftPIX FlipFlopPIX RangePIX StarPIX
SepiaPIX ConvertPIX ReducePIX ClampPIX FreezePIX FlarePIX AirPlayPIX RecordPIX
BlendPIX CrossPIX LookupPIX DisplacePIX RemapPIX ReorderPIX ResolutionPIX CropPIX
BlendsPIX LumaLevelsPIX LumaBlurPIX LumaTransformPIX TimeMachinePIX ArrayPIX

Install

Swift Package

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

Setup

SwiftUI

import SwiftUI
import PixelKit

struct ContentView: View {
    
    @StateObject var circlePix = CirclePIX()
    @StateObject var blurPix = BlurPIX()
    
    var body: some View {
        PixelView(pix: blurPix)
            .onAppear {
                blurPix.input = circlePix
                blurPix.radius = 0.25
            }
    }
}

UIKit

import UIKit
import PixelKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let circlePix = CirclePIX()

        let blurPix = BlurPIX()
        blurPix.input = circlePix
        blurPix.radius = 0.25

        let finalPix: PIX = blurPix
        finalPix.view.frame = view.bounds
        view.addSubview(finalPix.view)
    }
}

Resolution

In PixelKit all PIXs have a resolution. Some PIXs have defined resolutions (default to .auto) and some PIXs have derived resolutions.

The .auto resolution will fill up the view and get the correct resolution based on the view size. If a view is 100x100 points, the resolution will be 200x200 pixels on macOS and 300x300 pixels on iPhone.

Import the resolution package to work with resolutions:

import Resolution

You can multiply and divide resolutions with a CGFloat or Int.

There are predefined resolutions like ._1080p & ._4K.

Rendered Image

.renderedImage // UIImage or NSImage
.renderedTexture // MTLTexture

Example: Camera Effects

import SwiftUI
import PixelKit

class ViewModel: ObservableObject {
    
    let camera: CameraPIX
    let levels: LevelsPIX
    let colorShift: ColorShiftPIX
    let blur: BlurPIX
    let circle: CirclePIX
    
    let finalPix: PIX
    
    init() {
        
        camera = CameraPIX()
        camera.cameraResolution = ._1080p

        levels = LevelsPIX()
        levels.input = camera
        levels.brightness = 1.5
        levels.gamma = 0.5

        colorShift = ColorShiftPIX()
        colorShift.input = levels
        colorShift.saturation = 0.5

        blur = BlurPIX()
        blur.input = colorShift
        blur.radius = 0.25

        circle = CirclePIX(at: .square(1080))
        circle.radius = 0.45
        circle.backgroundColor = .clear

        finalPix = blur & (camera * circle)
    }
}

struct ContentView: View {
    
    @StateObject var viewModel = ViewModel()
    
    var body: some View {
        PixelView(pix: viewModel.finalPix)
    }
}

This can also be done with Effect Convenience Funcs:

let pix = CameraPIX().pixBrightness(1.5).pixGamma(0.5).pixSaturation(0.5).pixBlur(0.25)

Remeber to add NSCameraUsageDescription to your Info.plist

Example: Green Screen

import RenderKit import PixelKit

let cityImage = ImagePIX()
cityImage.image = UIImage(named: "city")

let supermanVideo = VideoPIX()
supermanVideo.load(fileNamed: "superman", withExtension: "mov")

let supermanKeyed = ChromaKeyPIX()
supermanKeyed.input = supermanVideo
supermanKeyed.keyColor = .green

let blendPix = BlendPIX()
blendPix.blendingMode = .over
blendPix.inputA = cityImage
blendPix.inputB = supermanKeyed

let finalPix: PIX = blendPix
finalPix.view.frame = view.bounds
view.addSubview(finalPix.view)

This can also be done with Blend Operators and Effect Convenience Funcs:

let pix = cityImage & supermanVideo.pixChromaKey(.green)

Example: Depth Camera

import RenderKit import PixelKit

let cameraPix = CameraPIX()
cameraPix.camera = .front

let depthCameraPix = DepthCameraPIX.setup(with: cameraPix)

let levelsPix = LevelsPIX()
levelsPix.input = depthCameraPix
levelsPix.inverted = true

let lumaBlurPix = cameraPix.pixLumaBlur(pix: levelsPix, radius: 0.1)

let finalPix: PIX = lumaBlurPix
finalPix.view.frame = view.bounds
view.addSubview(finalPix.view)

The DepthCameraPIX was added in PixelKit v0.8.4 and requires an iPhone X or newer.

Note to use the setup(with:filter:) method of DepthCameraPIX.
It will take care of orientation, color and enable depth on the CameraPIX.

To gain access to depth values ouside of the 0.0 and 1.0 bounds,
enable 16 bit mode like this: PixelKit.main.render.bits = ._16

Example: Multi Camera

let cameraPix = CameraPIX()
cameraPix.camera = .back

let multiCameraPix = MultiCameraPIX.setup(with: cameraPix, camera: .front)

let movedMultiCameraPix = multiCameraPix.pixScale(by: 0.25).pixTranslate(x: 0.375 * (9 / 16), y: 0.375)

let finalPix: PIX = camearPix & movedMultiCameraPix
finalPix.view.frame = view.bounds
view.addSubview(finalPix.view)

Note MultiCameraPIX requires iOS 13.

Coordinate Space

The PixelKit coordinate space is normailzed to the vertical axis (1.0 in height) with the origin (0.0, 0.0) in the center.
Note that compared to native UIKit and SwiftUI views the vertical axis is flipped and origin is moved, this is more convinent when working with graphics in PixelKit. A full rotation is defined by 1.0

Center: CGPoint(x: 0, y: 0)
Bottom Left: CGPoint(x: -0.5 * aspectRatio, y: -0.5)
Top Right: CGPoint(x: 0.5 * aspectRatio, y: 0.5)

Tip: Resolution has an .aspect property:
let aspectRatio: CGFloat = Resolution._1080p.aspect

Blend Operators

A quick and convenient way to blend PIXs
These are the supported BlendingMode operators:

& !& + - * ** !** % ~ °
.over .under .add .subtract .multiply .power .gamma .difference .average cosine
<> >< ++ -- <-> >-< +-+
.minimum .maximum .addWithAlpha .subtractWithAlpha inside outside exclusiveOr
let blendPix = (CameraPIX() !** NoisePIX(at: .fullHD(.portrait))) * CirclePIX(at: .fullHD(.portrait))

The default global blend operator fill mode is .fit, change it like this:
PIX.blendOperators.globalPlacement = .fill

Effect Convenience Funcs

  • .pixScaleResolution(to: ._1080p * 0.5) -> ResolutionPIX
  • .pixScaleResolution(by: 0.5) -> ResolutionPIX
  • .pixBrightness(0.5) -> LevelsPIX
  • .pixDarkness(0.5) -> LevelsPIX
  • .pixContrast(0.5) -> LevelsPIX
  • .pixGamma(0.5) -> LevelsPIX
  • .pixInvert() -> LevelsPIX
  • .pixOpacity(0.5) -> LevelsPIX
  • .pixBlur(0.5) -> BlurPIX
  • .pixEdge() -> EdgePIX
  • .pixThreshold(at: 0.5) -> ThresholdPIX
  • .pixQuantize(by: 0.5) -> QuantizePIX
  • .pixPosition(at: CGPoint(x: 0.5, y: 0.5)) -> TransformPIX
  • .pixRotatate(by: 0.5) -> TransformPIX
  • .pixRotatate(byRadians: .pi) -> TransformPIX
  • .pixRotatate(byDegrees: 180) -> TransformPIX
  • .pixScale(by: 0.5) -> TransformPIX
  • .pixKaleidoscope() -> KaleidoscopePIX
  • .pixTwirl(0.5) -> TwirlPIX
  • .pixSwap(.red, .blue) -> ChannelMixPIX
  • .pixChromaKey(.green) -> ChromaKeyPIX
  • .pixHue(0.5) -> ColorShiftPIX
  • .pixSaturation(0.5) -> ColorShiftPIX
  • .pixCrop(CGRect(x: 0.25, y 0.25, width: 0.5, height: 0.5)) -> CropPIX
  • .pixFlipX() -> FlipFlopPIX
  • .pixFlipY() -> FlipFlopPIX
  • .pixFlopLeft() -> FlipFlopPIX
  • .pixFlopRight() -> FlipFlopPIX
  • .pixRange(inLow: 0.0, inHigh: 0.5, outLow: 0.5, outHigh: 1.0) -> RangePIX
  • .pixRange(inLow: .clear, inHigh: .gray, outLow: .gray, outHigh: .white) -> RangePIX
  • .pixSharpen() -> SharpenPIX
  • .pixSlope() - > SlopePIX
  • .pixVignetting(radius: 0.5, inset: 0.25, gamma: 0.5) -> LumaLevelsPIX
  • .pixLookup(pix: pixB, axis: .x) -> LookupPIX
  • .pixLumaBlur(pix: pixB, radius: 0.5) -> LumaBlurPIX
  • .pixLumaLevels(pix: pixB, brightness: 2.0) -> LumaLevelsPIX
  • .pixDisplace(pix: pixB, distance: 0.5) -> DisplacePIX
  • .pixRemap(pix: pixB) -> RemapPIX

Keep in mind that these funcs will create new PIXs.
Be careful of overloading GPU memory, some funcs create several PIXs.

High Bit Mode

Some effects like DisplacePIX and SlopePIX can benefit from a higher bit depth.
The default is 8 bits. Change it like this: PixelKit.main.render.bits = ._16

Enable high bit mode before you create any PIXs.

Note resources do not support higher bits yet.
There is currently there is some gamma offset with resources.

MetalPIXs

let metalPix = MetalPIX(at: ._1080p, code:
    """
    pix = float4(u, v, 0.0, 1.0);
    """
)
let metalEffectPix = MetalEffectPIX(code:
    """
    float gamma = 0.25;
    pix = pow(input, 1.0 / gamma);
    """
)
metalEffectPix.input = CameraPIX()
let metalMergerEffectPix = MetalMergerEffectPIX(code:
    """
    pix = pow(inputA, 1.0 / inputB);
    """
)
metalMergerEffectPix.inputA = CameraPIX()
metalMergerEffectPix.inputB = ImagePIX("img_name")
let metalMultiEffectPix = MetalMultiEffectPIX(code:
    """
    float4 inPixA = inTexs.sample(s, uv, 0);
    float4 inPixB = inTexs.sample(s, uv, 1);
    float4 inPixC = inTexs.sample(s, uv, 2);
    pix = inPixA + inPixB + inPixC;
    """
)
metalMultiEffectPix.inputs = [ImagePIX("img_a"), ImagePIX("img_b"), ImagePIX("img_c")]

Uniforms:

var lumUniform = MetalUniform(name: "lum")
let metalPix = MetalPIX(at: ._1080p, code:
    """
    pix = float4(in.lum, in.lum, in.lum, 1.0);
    """,
    uniforms: [lumUniform]
)
lumUniform.value = 0.5

Notes:

  • To gain camera access, on macOS, check Camera in the App Sandbox in your Xcode project settings under Capabilities.

inspired by TouchDesigner created by Anton Heestand XYZ

More Repositories

1

AsyncGraphics

Edit images and video with async / await in Swift, powered by Metal.
Swift
230
star
2

UI3

3D UI layout lib for SwiftUI
Swift
78
star
3

SwiftFX

SwiftUI Effects
Swift
62
star
4

SwiftMetal

Write Metal in Swift
Swift
56
star
5

VertexKit

a Framework for iOS & macOS
Swift
50
star
6

VoxelKit

Volumetric realtime graphics
Swift
46
star
7

RenderKit

Realtime Render Engine Protocol
Swift
45
star
8

PolyKit

Rounded Polygons in SwiftUI
Swift
44
star
9

PixelUI

Create Live Graphics in SwiftUI (iOS, tvOS & macOS)
Swift
35
star
10

AirKit

AirPlay SwiftUI Views
Swift
23
star
11

cook_bar

A 'Health Bar' over each op indicating cook time. (now with GPU info)
19
star
12

Trails

Trails of values over time in SwiftUI
Swift
18
star
13

GeometryWriter

The opposite of a GeometryReader in SwiftUI
Swift
17
star
14

SwiftUIOSC

OSC Property Wrapper
Swift
14
star
15

Tabs

Tabs in SwiftUI
Swift
13
star
16

HDR-Camera

HDR Camera iOS App
Swift
10
star
17

TextureMap

Map textures in different formats like UIImage, NSImage, CGImage, CIImage and MTLTexture
Swift
9
star
18

Noise

*Random Smooth Cloudy* Noise for SwiftUI
Swift
8
star
19

Game-of-Life

Made in Swift & Metal with PixelKit
Swift
8
star
20

ImageFX

Async Image Effects
Swift
7
star
21

LiveValues

LiveValues for iOS, tvOS & macOS
Swift
7
star
22

RoundedPath

Round Paths in SwiftUI
Swift
7
star
23

MetalShaders

Shared Metal Shaders
Metal
7
star
24

VJ

hexagons and circles
Swift
6
star
25

KanjiGrid

An app for exploring Kanji
Swift
6
star
26

BottomSheet

Made in SwiftUI
Swift
5
star
27

PixelKit-SwiftUI

PixelKit with SwiftUI
Swift
5
star
28

pixtools

Pixel Command Line Tools
Swift
5
star
29

LUMA-FACE

LUMA FACE
Swift
5
star
30

MultiViews

SwiftUI Views for iOS, tvOS & macOS
Swift
5
star
31

pixlab

Pixel Command Line Lab
Swift
4
star
32

VoxelView

Metal 3D Texture Voxel Viewer
Swift
4
star
33

Shapes

SwiftUI Shapes
Swift
4
star
34

Hello-Pixels

The Hello World of Pixels
Swift
4
star
35

EuclidDemo

RealityKit Bool Demo with Euclid
Swift
4
star
36

BindingOperators

SwiftUI Binding Operators
Swift
4
star
37

Resolution

4K, 1080p and iPhone Resolutions
Swift
3
star
38

DisplayLink

iOS, tvOS, watchOS & macOS
Swift
3
star
39

Source-Calculator

Xcode Extension
Swift
3
star
40

tox_diff

This is a TouchDesigner .tox diff tool.
3
star
41

PixelTransitions

Transitions for Views in iOS
Swift
3
star
42

CodableGraphics

Codabillity for AsyncGraphics
Swift
3
star
43

manabi

学び
Swift
2
star
44

Sora

Swift
2
star
45

CoreGraphicsExtensions

Common Operator Overloading
Swift
2
star
46

Live-Graphics

Swift Package Collection
2
star
47

PixelMerger

Merge Videos with custom Resolution & FPS
Swift
2
star
48

WebsiteResultBuilder

Write HTML & CSS in Swift
Swift
2
star
49

ref_scan

op reference search scan
Python
2
star
50

Pixels-HDR

HDR Exposure stacking with Pixels
Swift
2
star
51

dome_calibration_pattern

2
star
52

geo__bezier_curve

TouchDesigner - Bezier Curve
2
star
53

PixelColor

Codable Color Struct
Swift
2
star
54

AssetManager

Import, export and share files on iOS and macOS
Swift
2
star
55

KeyColor

Extract Key Colors of an Image
Swift
2
star
56

LiveColorPIX

PixelKit Demo
Swift
2
star
57

heestand-xyz

Heestand XYZ
1
star
58

Transparency

Transparent Images
Swift
1
star
59

Xcode-Progress

Live Build Progress from Xcode
C
1
star
60

3d360

scripts to manage slicing and merging of raw images to generate a 3d equirectangular map for vr
Python
1
star
61

VideoFrames

Convert between Video and Image Frames
Swift
1
star
62

oneline

compress code to one line
Python
1
star
63

GEO3D

a 3D Protocol
Swift
1
star
64

Frequent-Finder

a "Finder" that sorts files and folders based on frequent visits
Swift
1
star