├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── Example
└── MetalCanvasExamples.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ └── Package.resolved
├── Package.resolved
├── Package.swift
├── Sources
├── MetalCanvas
│ ├── MCCanvas.swift
│ ├── MCColor
│ │ └── MCColor.swift
│ ├── MCCore.swift
│ ├── MCFilter
│ │ ├── ColorProcessing
│ │ │ ├── ColorProcessing.swift
│ │ │ └── Lut3DFilter
│ │ │ │ └── Lut3DFilter.swift
│ │ ├── ColorSpace
│ │ │ ├── ColorSpace.swift
│ │ │ ├── MCFilterColorSpaceNameSpace.swift
│ │ │ └── YCbCrToRGB
│ │ │ │ ├── RGBToY.swift
│ │ │ │ ├── RGBToYCbCr.swift
│ │ │ │ ├── YCbCrToRGB.swift
│ │ │ │ └── YCbCrToRGBYCbCr.swift
│ │ ├── Geometrics
│ │ │ ├── Geometrics.swift
│ │ │ └── Transform.swift
│ │ ├── ImageBlending
│ │ │ └── MCFilterImageBlending.swift
│ │ └── MCFilter.swift
│ ├── MCFunction.swift
│ ├── MCGeom
│ │ ├── MCGeomNameSpace.swift
│ │ └── MCMatrix4.swift
│ ├── MCImageRenderView.swift
│ ├── MCPrimitive
│ │ ├── Image
│ │ │ └── Image.swift
│ │ ├── MCPrimitive.swift
│ │ ├── MCPrimitiveTypeProtocol.swift
│ │ ├── Point
│ │ │ ├── MCPoint+Extended.swift
│ │ │ └── Point.swift
│ │ ├── Points.swift
│ │ └── Triangle
│ │ │ ├── Rectangle.swift
│ │ │ └── Triangle.swift
│ ├── MCTexture
│ │ └── MCTexture.swift
│ ├── MCVision
│ │ ├── Depth
│ │ │ ├── DepthMapTexture.swift
│ │ │ └── HumanSegmentationTexture.swift
│ │ ├── Detection
│ │ │ ├── Face.swift
│ │ │ └── FaceItem.swift
│ │ ├── Inference.swift
│ │ ├── MCVision.swift
│ │ └── Protocol
│ │ │ ├── VisionLayerProtocol.swift
│ │ │ └── VisionResultProtocol.swift
│ ├── Typealias.swift
│ └── Utils
│ │ ├── MCPixelFormatType.swift
│ │ ├── MCRenderPipelineState.swift
│ │ ├── MCShaderPreset.swift
│ │ └── MCTools.swift
└── MetalCanvasShaders
│ ├── MetalCanvasShaders.m
│ ├── MetalShaderType.metal
│ ├── Shader
│ ├── ColorProcessing
│ │ ├── Lut1DFilter.metal
│ │ └── Lut3DFilter.metal
│ ├── ColorSpace
│ │ ├── RGBToY.metal
│ │ ├── RGBToYCbCr.metal
│ │ └── YCbCrToRGB.metal
│ ├── Geometrics
│ │ └── Transform.metal
│ ├── ImageBlending
│ │ ├── AlphaBlending.metal
│ │ └── DepthBlending.metal
│ ├── Primitive
│ │ ├── Image.metal
│ │ ├── Point.metal
│ │ └── Triangle.metal
│ └── Shader.metal
│ └── include
│ ├── MCShaderTypes.h
│ └── MetalCanvasShaders.h
└── Tests
├── LinuxMain.swift
└── MetalCanvasTests
├── MetalCanvasTests.swift
└── XCTestManifests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/MetalCanvasExamples.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Example/MetalCanvasExamples.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/MetalCanvasExamples.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "GraphicsLibs.Swift",
6 | "repositoryURL": "https://github.com/Hideyuki-Machida/GraphicsLibs.Swift",
7 | "state": {
8 | "branch": "master",
9 | "revision": "f8217e8bb3af77add4ad59ace8e52c71f9708a94",
10 | "version": null
11 | }
12 | },
13 | {
14 | "package": "ProcessLogger.Swift",
15 | "repositoryURL": "https://github.com/Hideyuki-Machida/ProcessLogger.Swift",
16 | "state": {
17 | "branch": "master",
18 | "revision": "624f37efdfca9f91a16f1e827f677ecf163153d0",
19 | "version": null
20 | }
21 | }
22 | ]
23 | },
24 | "version": 1
25 | }
26 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "GraphicsLibs.Swift",
6 | "repositoryURL": "https://github.com/Hideyuki-Machida/GraphicsLibs.Swift",
7 | "state": {
8 | "branch": "master",
9 | "revision": "89c002dae842675d13cfe752e36bc771f282b88e",
10 | "version": null
11 | }
12 | },
13 | {
14 | "package": "ProcessLogger.Swift",
15 | "repositoryURL": "https://github.com/Hideyuki-Machida/ProcessLogger.Swift",
16 | "state": {
17 | "branch": "master",
18 | "revision": "78c0af8bf66805d03415961802a631ece12ef3b1",
19 | "version": null
20 | }
21 | }
22 | ]
23 | },
24 | "version": 1
25 | }
26 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "MetalCanvas",
8 | platforms: [
9 | .iOS(.v11), .macOS(.v10_15)
10 | ],
11 | products: [
12 | .library(
13 | name: "MetalCanvas",
14 | targets: ["MetalCanvasShaders", "MetalCanvas"]),
15 | ],
16 | dependencies: [
17 | .package(url: "https://github.com/Hideyuki-Machida/ProcessLogger.Swift", .branch("master")),
18 | .package(url: "https://github.com/Hideyuki-Machida/GraphicsLibs.Swift", .branch("master"))
19 | ],
20 | targets: [
21 | .target(
22 | name: "MetalCanvasShaders",
23 | dependencies: ["ProcessLogger.Swift", "GraphicsLibs.Swift"]
24 | ),
25 | .target(
26 | name: "MetalCanvas",
27 | dependencies: ["MetalCanvasShaders"]
28 | )
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCCanvas.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCCanvas.swift
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/23.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import CoreVideo
12 | import GraphicsLibs_Swift
13 | import GLKit
14 |
15 | public class MCCanvas {
16 | public enum ErrorType: Error {
17 | case setupError
18 | case drawError
19 | case endError
20 | }
21 |
22 | public enum OrthoType {
23 | case perspective
24 | case center
25 | case topLeft
26 | case bottomLeft
27 |
28 | func getMatrix(size: CGSize) -> GLKMatrix4 {
29 | switch self {
30 | case .perspective: return MCGeom.Matrix4x4().glkMatrix
31 | case .center: return GLKMatrix4MakeOrtho(-Float(size.width / 2), Float(size.width / 2), Float(size.height / 2), -Float(size.height / 2), -1, 1)
32 | case .topLeft: return GLKMatrix4MakeOrtho(0, Float(size.width), Float(size.height), 0, -1, 1)
33 | case .bottomLeft: return GLKMatrix4MakeOrtho(0, Float(size.width), 0, Float(size.height), -1, 1)
34 | }
35 | }
36 |
37 | /*
38 | func getMatrix(size: MCSize) -> MCGeom.Matrix4x4 {
39 | switch self {
40 | case .perspective: return MCGeom.Matrix4x4()
41 | case .center: return MCGeom.Matrix4x4.Ortho(left: -Float(size.w / 2), right: Float(size.w / 2), bottom: Float(size.h / 2), top: -Float(size.h / 2), nearZ: -1, farZ: 1)
42 | case .topLeft: return MCGeom.Matrix4x4.Ortho(left: 0, right: Float(size.w), bottom: Float(size.h), top: 0, nearZ: -1, farZ: 1)
43 | case .bottomLeft: return MCGeom.Matrix4x4.Ortho(left: 0, right: Float(size.w), bottom: 0, top: Float(size.h), nearZ: -1, farZ: 1)
44 | }
45 | }
46 | */
47 | }
48 |
49 | private var projection: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
50 | public private(set) var drawInfo: MCPrimitive.DrawInfo
51 | public var mcTexture: MCTexture
52 | public var texture: MTLTexture? {
53 | return drawInfo.renderPassDescriptor.colorAttachments[0].texture
54 | }
55 |
56 | public init(destination: inout MCTexture, orthoType: OrthoType, loadAction: MTLLoadAction = MTLLoadAction.load) throws {
57 | self.mcTexture = destination
58 | /*
59 | let renderSize: MCSize = MCSize(w: destination.size.w, h: destination.size.h)
60 | self.projection = orthoType.getMatrix(size: renderSize)
61 | */
62 | self.mcTexture = destination
63 | let renderSize: MCSize = MCSize(w: destination.size.w, h: destination.size.h)
64 | self.projection.glkMatrix = orthoType.getMatrix(size: renderSize.toCGSize())
65 |
66 | let renderPassDescriptor: MTLRenderPassDescriptor = MTLRenderPassDescriptor()
67 | renderPassDescriptor.colorAttachments[0].loadAction = loadAction
68 | renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreAction.store
69 | renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
70 | renderPassDescriptor.colorAttachments[0].texture = destination.texture
71 |
72 | let projectionMatrixBuffer: MTLBuffer = try MCCore.makeBuffer(data: self.projection.raw)
73 | self.drawInfo = MCPrimitive.DrawInfo(
74 | renderPassDescriptor: renderPassDescriptor,
75 | renderSize: renderSize,
76 | orthoType: orthoType,
77 | projectionMatrixBuffer: projectionMatrixBuffer
78 | )
79 | }
80 |
81 | public convenience init(orthoType: OrthoType, renderSize: CGSize, loadAction: MTLLoadAction = MTLLoadAction.load) throws {
82 | guard let pixelBuffer: CVPixelBuffer = CVPixelBuffer.create(size: renderSize) else { throw ErrorType.setupError }
83 | var texture: MCTexture = try MCTexture(pixelBuffer: pixelBuffer, planeIndex: 0)
84 | try self.init(destination: &texture, orthoType: orthoType, loadAction: loadAction)
85 | }
86 |
87 | public convenience init(pixelBuffer: inout CVPixelBuffer, orthoType: OrthoType, renderSize: CGSize, loadAction: MTLLoadAction = MTLLoadAction.load) throws {
88 | var texture: MCTexture = try MCTexture(pixelBuffer: pixelBuffer, planeIndex: 0)
89 | try self.init(destination: &texture, orthoType: orthoType, loadAction: loadAction)
90 | }
91 | }
92 |
93 | extension MCCanvas {
94 | public func update(destination: inout MCTexture, orthoType: OrthoType, loadAction: MTLLoadAction) throws {
95 | let renderSize: MCSize = MCSize(w: destination.size.w, h: destination.size.h)
96 |
97 | self.drawInfo.renderPassDescriptor.colorAttachments[0].loadAction = loadAction
98 | self.drawInfo.renderPassDescriptor.colorAttachments[0].texture = destination.texture
99 |
100 | self.drawInfo.orthoType = orthoType
101 | self.projection.glkMatrix = orthoType.getMatrix(size: self.drawInfo.renderSize.toCGSize())
102 | self.drawInfo.projectionMatrixBuffer = try MCCore.makeBuffer(data: self.projection.raw)
103 | self.drawInfo.renderSize = renderSize
104 | }
105 |
106 | public func update(destination: inout MCTexture) throws {
107 | self.drawInfo.renderPassDescriptor.colorAttachments[0].texture = destination.texture
108 | }
109 | }
110 |
111 | extension MCCanvas {
112 | public func draw(commandBuffer: MTLCommandBuffer, objects: [MCPrimitiveTypeProtocol]) throws {
113 | for object in objects {
114 | try object.draw(commandBuffer: commandBuffer, drawInfo: self.drawInfo)
115 | }
116 | }
117 | }
118 |
119 | extension MCCanvas {
120 | public func fill(commandBuffer: MTLCommandBuffer, color: MCColor) throws {
121 | let object: MCPrimitiveTypeProtocol
122 | switch self.drawInfo.orthoType {
123 | case .perspective:
124 | object = try MCPrimitive.Rectangle(
125 | position: SIMD2(x: -1.0, y: -1.0),
126 | w: 2.0,
127 | h: 2.0,
128 | color: color
129 | )
130 | case .center:
131 | object = try MCPrimitive.Rectangle(
132 | position: SIMD2(x: -Float(self.drawInfo.renderSize.w / 2.0), y: -Float(self.drawInfo.renderSize.h / 2.0)),
133 | w: Float(self.drawInfo.renderSize.w),
134 | h: Float(self.drawInfo.renderSize.h),
135 | color: color
136 | )
137 | case .topLeft, .bottomLeft:
138 | object = try MCPrimitive.Rectangle(
139 | position: SIMD2(x: 0.0, y: 0.0),
140 | w: Float(self.drawInfo.renderSize.w),
141 | h: Float(self.drawInfo.renderSize.h),
142 | color: color
143 | )
144 | }
145 | try object.draw(commandBuffer: commandBuffer, drawInfo: self.drawInfo)
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCColor/MCColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCColor.swift
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/25.
6 | // Copyright © 2018 町田 秀行. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public typealias RGB = (r: Int, g: Int, b: Int)
12 | public typealias RGBA = (r: Int, g: Int, b: Int, a: Int)
13 | public typealias HSV = (h: Int, s: Int, v: Int)
14 | public typealias HSVA = (h: Int, s: Int, v: Int, a: Int)
15 |
16 | public struct MCColor {
17 | let color: SIMD4
18 |
19 | public init(red: Float, green: Float, blue: Float, alpha: Float) {
20 | self.color = [red, green, blue, alpha]
21 | }
22 |
23 | public init(hex: String, alpha: Float = 1.0) {
24 | let hex: String = hex.replacingOccurrences(of: "#", with: "")
25 | let scanner: Scanner = Scanner(string: hex)
26 | scanner.scanLocation = 0
27 | var rgbValue: UInt64 = 0
28 | scanner.scanHexInt64(&rgbValue)
29 | let red: UInt64 = (rgbValue & 0xFF0000) >> 16
30 | let green: UInt64 = (rgbValue & 0xFF00) >> 8
31 | let blue: UInt64 = rgbValue & 0xFF
32 |
33 | self.color = SIMD4(x: Float(red) / 0xFF, y: Float(green) / 0xFF, z: Float(blue) / 0xFF, w: alpha)
34 | }
35 |
36 | /*
37 | public var toHexString: String {
38 | //self.getRed(&r, green: &g, blue: &b, alpha: &a)
39 |
40 | return String(
41 | format: "%02X%02X%02X",
42 | Int(r * 0xff),
43 | Int(g * 0xff),
44 | Int(b * 0xff)
45 | )
46 | }
47 | */
48 |
49 | // MARK: - RGB
50 | /*
51 | public init(r: Int = 0, g: Int = 0, b: Int = 0, a: Int = 255) {
52 | self.init( red: Float(max(min(r, 255), 0)) / 255, green: Float(max(min(g, 255), 0)) / 255, blue: Float(max(min(b, 255), 0)) / 255, alpha: Float(max(min(a, 255), 0)) / 255)
53 | }
54 |
55 | public convenience init(_ r: Int, _ g: Int, _ b: Int, _ a: Int) {
56 | self.init(r: r, g: g, b: b, a: a)
57 | }
58 |
59 | public convenience init(rgba: RGBA) {
60 | self.init(rgba.r, rgba.g, rgba.b, rgba.a)
61 | }
62 |
63 | public convenience init(rgb: RGB) {
64 | self.init(rgb.r, rgb.g, rgb.b, 255)
65 | }
66 |
67 | public var toRGBA: RGBA {
68 | var r: CGFloat = 0
69 | var g: CGFloat = 0
70 | var b: CGFloat = 0
71 | var a: CGFloat = 0
72 |
73 | self.getRed(&r, green: &g, blue: &b, alpha: &a)
74 |
75 | return RGBA(r: Int(r * 255), g: Int(g * 255), b: Int(b * 255), a: Int(a * 255))
76 | }
77 |
78 | public var toRGB: RGB {
79 | var r: CGFloat = 0
80 | var g: CGFloat = 0
81 | var b: CGFloat = 0
82 | var a: CGFloat = 0
83 |
84 | self.getRed(&r, green: &g, blue: &b, alpha: &a)
85 |
86 | return RGB(r: Int(r * 255), g: Int(g * 255), b: Int(b * 255))
87 | }
88 |
89 | public var toRGBAList: [Int] {
90 | var r: CGFloat = 0
91 | var g: CGFloat = 0
92 | var b: CGFloat = 0
93 | var a: CGFloat = 0
94 |
95 | self.getRed(&r, green: &g, blue: &b, alpha: &a)
96 |
97 | return [Int(r * 255), Int(g * 255), Int(b * 255), Int(a * 255)]
98 | }
99 | */
100 |
101 | // MARK: - HSV
102 | /*
103 | public convenience init(h: Int = 0, s: Int = 0, v: Int = 0, a: Int = 255) {
104 | self.init(hue: CGFloat(max(min(h, 255), 0)) / 255, saturation: CGFloat(max(min(s, 255), 0)) / 255, brightness: CGFloat(max(min(v, 255), 0)) / 255, alpha: CGFloat(max(min(a, 255), 0)) / 255)
105 | }
106 |
107 | public convenience init(hsva: HSVA) {
108 | self.init(h: hsva.h, s: hsva.s, v: hsva.v, a: hsva.a)
109 | }
110 |
111 | public convenience init(hsv: HSV) {
112 | self.init(h: hsv.h, s: hsv.s, v: hsv.v, a: 255)
113 | }
114 |
115 | public var toHSVA: HSVA {
116 | var h: CGFloat = 0
117 | var s: CGFloat = 0
118 | var v: CGFloat = 0
119 | var a: CGFloat = 0
120 |
121 | self.getHue(&h, saturation: &s, brightness: &v, alpha: &a)
122 |
123 | return HSVA(h: Int(h * 255), s: Int(s * 255), v: Int(v * 255), a: Int(a * 255))
124 | }
125 |
126 | public var toHSV: HSV {
127 | var h: CGFloat = 0
128 | var s: CGFloat = 0
129 | var v: CGFloat = 0
130 | var a: CGFloat = 0
131 |
132 | self.getHue(&h, saturation: &s, brightness: &v, alpha: &a)
133 |
134 | return HSV(h: Int(h * 255), s: Int(s * 255), v: Int(v * 255))
135 | }
136 | */
137 | }
138 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCCore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCCore.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2018/12/29.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import Foundation
11 | import Metal
12 | import MetalKit
13 | import MetalCanvasShaders
14 | import ProcessLogger_Swift
15 |
16 | public final class MCCore {
17 | public enum MCCoreErrorType: Error {
18 | case setup
19 | case createMetalRenderPassDescriptorError
20 | case createMetalBuffer
21 | }
22 |
23 | public fileprivate(set) static var isMetalCanvas: Bool = false
24 |
25 | public fileprivate(set) static var device: MTLDevice!
26 | public fileprivate(set) static var commandQueue: MTLCommandQueue!
27 | public fileprivate(set) static var ciContext: CIContext!
28 | public fileprivate(set) static var library: MTLLibrary!
29 | public fileprivate(set) static var textureLoader: MTKTextureLoader!
30 | public fileprivate(set) static var textureCache: CVMetalTextureCache?
31 |
32 | public static func setup(contextOptions: [CIContextOption: Any]) throws {
33 | let errorMessage: String = "MetalCanvas: SetupError"
34 |
35 | ///////////////////////////////////////////////////////////////////////////////
36 | // Metalの各ツール設定
37 | guard
38 | let device: MTLDevice = MTLCreateSystemDefaultDevice(),
39 | let commandQueue: MTLCommandQueue = device.makeCommandQueue()
40 | else {
41 | ProcessLogger.errorLog(errorMessage)
42 | self.isMetalCanvas = false
43 | throw MCCoreErrorType.setup
44 | }
45 |
46 | self.device = device
47 | self.commandQueue = commandQueue
48 | self.textureLoader = MTKTextureLoader(device: device)
49 | self.textureCache = MCCore.createTextureCache(device: MCCore.device)
50 | ///////////////////////////////////////////////////////////////////////////////
51 |
52 | ///////////////////////////////////////////////////////////////////////////////
53 | // default.metallib 取得
54 |
55 | let bundle: Bundle = MetalCanvasShaders.getBundle()
56 | let metallibURL: URL = bundle.url(forResource: "default", withExtension: "metallib")!
57 | do {
58 | self.library = try device.makeLibrary(filepath: metallibURL.path)
59 | } catch {
60 | ProcessLogger.errorLog(errorMessage)
61 | self.isMetalCanvas = false
62 | throw MCCoreErrorType.setup
63 | }
64 | ///////////////////////////////////////////////////////////////////////////////
65 |
66 | ///////////////////////////////////////////////////////////////////////////////
67 | // CIContext設定
68 | if !contextOptions.isEmpty {
69 | // コンテクストのオプションが指定されている
70 | self.ciContext = CIContext(mtlDevice: device, options: contextOptions)
71 | } else {
72 | // コンテクストのオプションが指定されていない
73 | self.ciContext = CIContext(
74 | mtlDevice: device, options: [
75 | CIContextOption.workingColorSpace: CGColorSpace.displayP3,
76 | CIContextOption.useSoftwareRenderer: NSNumber(value: false),
77 | ]
78 | )
79 | }
80 |
81 | self.isMetalCanvas = true
82 | ///////////////////////////////////////////////////////////////////////////////
83 | }
84 | }
85 |
86 | extension MCCore {
87 | public static func createTextureCache() -> CVMetalTextureCache? {
88 | return self.createTextureCache(device: self.device)
89 | }
90 |
91 | private static func createTextureCache(device: MTLDevice) -> CVMetalTextureCache? {
92 | var textureCache: CVMetalTextureCache?
93 | CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
94 | return textureCache
95 | }
96 | }
97 |
98 | extension MCCore {
99 | public static func makeBuffer(data: [T]) throws -> MTLBuffer {
100 | let length: Int = MemoryLayout.size * data.count
101 | guard let buffer: MTLBuffer = MCCore.device.makeBuffer(bytes: data, length: length) else { throw MCCoreErrorType.createMetalBuffer }
102 | return buffer
103 | }
104 | }
105 |
106 | extension MCCore {
107 | public static func texture(sampleBuffer: inout CMSampleBuffer, textureCache: inout CVMetalTextureCache, mtlPixelFormat: MTLPixelFormat) -> MTLTexture? {
108 | guard var pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil }
109 | return MCCore.texture(pixelBuffer: &pixelBuffer, textureCache: &textureCache, mtlPixelFormat: mtlPixelFormat)
110 | }
111 |
112 | public static func texture(pixelBuffer: inout CVPixelBuffer, textureCache: inout CVMetalTextureCache, mtlPixelFormat: MTLPixelFormat) -> MTLTexture? {
113 | let width: Int = CVPixelBufferGetWidth(pixelBuffer)
114 | let height: Int = CVPixelBufferGetHeight(pixelBuffer)
115 | var imageTexture: CVMetalTexture?
116 | let result: CVReturn = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, nil, mtlPixelFormat, width, height, 0, &imageTexture)
117 | guard result == kCVReturnSuccess else { return nil }
118 | guard let imgTexture: CVMetalTexture = imageTexture else { return nil }
119 | if let texture: MTLTexture = CVMetalTextureGetTexture(imgTexture) {
120 | return texture
121 | }
122 | return nil
123 | }
124 |
125 | public static func texture(sampleBuffer: inout CMSampleBuffer, mtlPixelFormat: MTLPixelFormat, planeIndex: Int = 0) -> MTLTexture? {
126 | guard var pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil }
127 | return MCCore.texture(pixelBuffer: &pixelBuffer, mtlPixelFormat: mtlPixelFormat, planeIndex: planeIndex)
128 | }
129 |
130 | public static func texture(pixelBuffer: inout CVPixelBuffer, mtlPixelFormat: MTLPixelFormat, planeIndex: Int = 0) -> MTLTexture? {
131 | guard let textureCache: CVMetalTextureCache = MCCore.textureCache ?? MCCore.createTextureCache(device: MCCore.device) else { return nil }
132 | return MCCore.texture(pixelBuffer: &pixelBuffer, textureCache: textureCache, mtlPixelFormat: mtlPixelFormat, planeIndex: planeIndex)
133 | }
134 |
135 | public static func texture(pixelBuffer: inout CVPixelBuffer, textureCache: CVMetalTextureCache, mtlPixelFormat: MTLPixelFormat, planeIndex: Int = 0) -> MTLTexture? {
136 | let width: Int = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
137 | let height: Int = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
138 | var imageTexture: CVMetalTexture?
139 | let result: CVReturn = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, pixelBuffer, nil, mtlPixelFormat, width, height, planeIndex, &imageTexture)
140 | guard result == kCVReturnSuccess else { return nil }
141 | guard let imgTexture: CVMetalTexture = imageTexture else { return nil }
142 | if let texture: MTLTexture = CVMetalTextureGetTexture(imgTexture) {
143 | return texture
144 | }
145 | return nil
146 | }
147 |
148 | public static func texture(cgImage: inout CGImage, colorPixelFormat: MTLPixelFormat) -> MTLTexture? {
149 | return try? self.textureLoader.newTexture(cgImage: cgImage, options: nil)
150 | }
151 |
152 | public static func texture(URL: URL, isSRGB: Bool = false) throws -> MTLTexture {
153 | let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [
154 | MTKTextureLoader.Option.SRGB: isSRGB,
155 | MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
156 | MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.private.rawValue),
157 | ]
158 | return try MCCore.textureLoader.newTexture(URL: URL, options: textureLoaderOptions)
159 | }
160 |
161 | public static func flush() {
162 | MCCore.textureCache = MCCore.createTextureCache(device: MCCore.device)
163 | MCCore.ciContext.clearCaches()
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorProcessing/ColorProcessing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorProcessing.swift
3 | // MetalCanvas
4 | //
5 | // Created by machida.hideyuki on 2019/11/29.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MCFilter { public struct ColorProcessing { private init() {} /* このstructはnamespace用途なのでインスタンス化防止 */ } }
12 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorProcessing/Lut3DFilter/Lut3DFilter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LutFilter.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/02/24.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MetalKit
11 | import MetalCanvasShaders
12 |
13 | extension MCFilter.ColorProcessing {
14 | public struct Lut3DFilter {
15 | // MARK: - vars
16 |
17 | fileprivate let pipelineState: MCRenderPipelineState
18 | fileprivate let vertexInBuffer: MTLBuffer
19 | fileprivate var intensityBuffer: MTLBuffer
20 |
21 | fileprivate var renderPassDescriptor: MTLRenderPassDescriptor = MTLRenderPassDescriptor()
22 | private var lutImageTexture: MCTexture
23 |
24 | public var intensity: Float = 1.0 {
25 | willSet {
26 | do {
27 | self.intensityBuffer = try MCCore.makeBuffer(data: [newValue])
28 | } catch {}
29 | }
30 | }
31 |
32 | // MARK: - func
33 |
34 | public init(lutImageTexture: MCTexture) throws {
35 | self.lutImageTexture = lutImageTexture
36 |
37 | self.pipelineState = try MCRenderPipelineState(
38 | vertex: MCFilter.ColorProcessing.Lut3DFilter.shaderFunc.vertex,
39 | fragment: MCFilter.ColorProcessing.Lut3DFilter.shaderFunc.fragment,
40 | label: "MCFilter.ColorProcessing.Lut3DFilter"
41 | )
42 | self.vertexInBuffer = try MCCore.makeBuffer(data: MCShaderPreset.normalizedVertex)
43 | self.intensityBuffer = try MCCore.makeBuffer(data: [self.intensity])
44 |
45 | //self.renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadAction.load
46 | //self.renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
47 | }
48 |
49 | public func process(commandBuffer: MTLCommandBuffer, imageTexture: MCTexture, destinationTexture: inout MCTexture) throws {
50 | self.renderPassDescriptor.colorAttachments[0].texture = destinationTexture.texture
51 |
52 | guard let renderCommandEncoder: MTLRenderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { throw MCFilter.ErrorType.drawError }
53 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
54 | renderCommandEncoder.setVertexBuffer(self.vertexInBuffer, offset: 0, index: Int(MCVertexIndex.rawValue))
55 | renderCommandEncoder.setFragmentTexture(imageTexture.texture, index: 0)
56 | renderCommandEncoder.setFragmentTexture(self.lutImageTexture.texture, index: 1)
57 | renderCommandEncoder.setFragmentBuffer(self.intensityBuffer, offset: 0, index: Int(MCIntensity.rawValue))
58 | renderCommandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
59 | renderCommandEncoder.endEncoding()
60 | }
61 | }
62 | }
63 |
64 | extension MCFilter.ColorProcessing.Lut3DFilter {
65 | // MARK: - MTLFunction
66 |
67 | private static let shaderFunc: ShaderFunc = MCFilter.ColorProcessing.Lut3DFilter.ShaderFunc()
68 |
69 | fileprivate struct ShaderFunc {
70 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_Lut3DFilter")!
71 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_Lut3DFilter")!
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/ColorSpace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorSpace.swift
3 | // MetalCanvas
4 | //
5 | // Created by machida.hideyuki on 2019/11/29.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MCFilter { public struct ColorSpace { private init() {} /* このstructはnamespace用途なのでインスタンス化防止 */ } }
12 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/MCFilterColorSpaceNameSpace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCFilterColorSpaceNameSpace.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MCFilter {
12 | //public struct ColorSpace {}
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/YCbCrToRGB/RGBToY.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGBToY.swift
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/10/04.
6 | //
7 |
8 | import Foundation
9 | import Metal
10 |
11 | extension MCFilter.ColorSpace {
12 | public struct RGBToY {
13 | // MARK: - vars
14 |
15 | fileprivate let pipelineState: MTLComputePipelineState
16 | fileprivate let threadsPerThreadgroup = MTLSize(width: 16, height: 16, depth: 1)
17 |
18 | // MARK: - func
19 |
20 | public init() throws {
21 | self.pipelineState = try MCCore.device.makeComputePipelineState(function: MCFilter.ColorSpace.RGBToY.shaderFunc.kernel)
22 | }
23 |
24 | public func process(commandBuffer: MTLCommandBuffer, sorceRGB: MCTexture, destinationY: inout MCTexture) throws {
25 |
26 | let threadgroupCount = MTLSize(
27 | width: Int(sorceRGB.size.w) / self.threadsPerThreadgroup.width,
28 | height: Int(sorceRGB.size.h) / self.threadsPerThreadgroup.height,
29 | depth: 1
30 | )
31 |
32 | let encoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
33 | encoder.setComputePipelineState(self.pipelineState)
34 | encoder.setTexture(sorceRGB.texture, index: 0)
35 | encoder.setTexture(destinationY.texture, index: 1)
36 | encoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadsPerThreadgroup)
37 | encoder.endEncoding()
38 | }
39 | }
40 | }
41 |
42 | extension MCFilter.ColorSpace.RGBToY {
43 | // MARK: - MTLFunction
44 |
45 | private static let shaderFunc: ShaderFunc = MCFilter.ColorSpace.RGBToY.ShaderFunc()
46 |
47 | fileprivate struct ShaderFunc {
48 | fileprivate let kernel: MTLFunction = MCCore.library.makeFunction(name: "kernel_RGBToY")!
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/YCbCrToRGB/RGBToYCbCr.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGBToYCbCr.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/07/22.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 |
8 | import Foundation
9 | import Metal
10 |
11 | extension MCFilter.ColorSpace {
12 | public struct RGBToYCbCr {
13 | // MARK: - vars
14 |
15 | fileprivate let pipelineState: MTLComputePipelineState
16 | fileprivate let threadsPerThreadgroup = MTLSize(width: 16, height: 16, depth: 1)
17 |
18 | // MARK: - func
19 |
20 | public init() throws {
21 | self.pipelineState = try MCCore.device.makeComputePipelineState(function: MCFilter.ColorSpace.RGBToYCbCr.shaderFunc.kernel)
22 | //self.pipelineState.label = "MCFilter.ColorSpace RGBToYCbCr"
23 | }
24 |
25 | public func process(commandBuffer: MTLCommandBuffer, sorceRGB: MCTexture, destinationY: inout MCTexture, destinationCb: inout MCTexture, destinationCr: inout MCTexture) throws {
26 |
27 | let threadgroupCount = MTLSize(
28 | width: Int(sorceRGB.size.w) / self.threadsPerThreadgroup.width,
29 | height: Int(sorceRGB.size.h) / self.threadsPerThreadgroup.height,
30 | depth: 1
31 | )
32 |
33 | let encoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
34 | encoder.setComputePipelineState(self.pipelineState)
35 | encoder.setTexture(sorceRGB.texture, index: 0)
36 | encoder.setTexture(destinationY.texture, index: 1)
37 | encoder.setTexture(destinationCb.texture, index: 2)
38 | encoder.setTexture(destinationCr.texture, index: 3)
39 | encoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadsPerThreadgroup)
40 | encoder.endEncoding()
41 | }
42 | }
43 | }
44 |
45 | extension MCFilter.ColorSpace.RGBToYCbCr {
46 | // MARK: - MTLFunction
47 |
48 | private static let shaderFunc: ShaderFunc = MCFilter.ColorSpace.RGBToYCbCr.ShaderFunc()
49 |
50 | fileprivate struct ShaderFunc {
51 | fileprivate let kernel: MTLFunction = MCCore.library.makeFunction(name: "kernel_RGBToYCbCr")!
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/YCbCrToRGB/YCbCrToRGB.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCFilterYCbCrToRGB.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 |
12 | extension MCFilter.ColorSpace {
13 | public struct YCbCrToRGB {
14 | // MARK: - vars
15 |
16 | fileprivate let pipelineState: MTLComputePipelineState
17 | fileprivate let threadsPerThreadgroup = MTLSize(width: 16, height: 16, depth: 1)
18 |
19 | // MARK: - func
20 |
21 | public init() throws {
22 | self.pipelineState = try MCCore.device.makeComputePipelineState(function: MCFilter.ColorSpace.YCbCrToRGB.shaderFunc.kernel)
23 | }
24 |
25 | public func process(commandBuffer: MTLCommandBuffer, sorceY: inout MCTexture, sorceCbCr: inout MCTexture, destinationRGB: inout MCTexture) throws {
26 |
27 | let threadgroupCount = MTLSize(
28 | width: Int(sorceY.size.w) / self.threadsPerThreadgroup.width,
29 | height: Int(sorceY.size.h) / self.threadsPerThreadgroup.height,
30 | depth: 1
31 | )
32 |
33 | let encoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
34 | encoder.setComputePipelineState(self.pipelineState)
35 | encoder.setTexture(sorceY.texture, index: 0)
36 | encoder.setTexture(sorceCbCr.texture, index: 1)
37 | encoder.setTexture(destinationRGB.texture, index: 2)
38 | encoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadsPerThreadgroup)
39 | encoder.endEncoding()
40 | }
41 | }
42 | }
43 |
44 | extension MCFilter.ColorSpace.YCbCrToRGB {
45 | // MARK: - MTLFunction
46 |
47 | private static let shaderFunc: ShaderFunc = MCFilter.ColorSpace.YCbCrToRGB.ShaderFunc()
48 |
49 | fileprivate struct ShaderFunc {
50 | fileprivate let kernel: MTLFunction = MCCore.library.makeFunction(name: "kernel_YCbCrToRGB")!
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ColorSpace/YCbCrToRGB/YCbCrToRGBYCbCr.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGBToYCbCr.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/07/22.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 |
8 | import Foundation
9 | import Metal
10 |
11 | extension MCFilter.ColorSpace {
12 | public struct YCbCrToRGBYCbCr {
13 | // MARK: - vars
14 |
15 | fileprivate let pipelineState: MTLComputePipelineState
16 | fileprivate let threadsPerThreadgroup = MTLSize(width: 16, height: 16, depth: 1)
17 |
18 | // MARK: - func
19 |
20 | public init() throws {
21 | self.pipelineState = try MCCore.device.makeComputePipelineState(function: MCFilter.ColorSpace.YCbCrToRGBYCbCr.shaderFunc.kernel)
22 | }
23 |
24 | public func process(commandBuffer: MTLCommandBuffer, sorceY: inout MCTexture, sorceCbCr: inout MCTexture, destinationRGB: inout MCTexture, destinationY: inout MCTexture, destinationCb: inout MCTexture, destinationCr: inout MCTexture) throws {
25 |
26 | let threadgroupCount = MTLSize(
27 | width: Int(sorceY.size.w) / self.threadsPerThreadgroup.width,
28 | height: Int(sorceY.size.h) / self.threadsPerThreadgroup.height,
29 | depth: 1
30 | )
31 |
32 | let encoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
33 | encoder.setComputePipelineState(self.pipelineState)
34 | encoder.setTexture(sorceY.texture, index: 0)
35 | encoder.setTexture(sorceCbCr.texture, index: 1)
36 | encoder.setTexture(destinationRGB.texture, index: 2)
37 | encoder.setTexture(destinationY.texture, index: 3)
38 | encoder.setTexture(destinationCb.texture, index: 4)
39 | encoder.setTexture(destinationCr.texture, index: 5)
40 | encoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup: threadsPerThreadgroup)
41 | encoder.endEncoding()
42 | }
43 | }
44 | }
45 |
46 | extension MCFilter.ColorSpace.YCbCrToRGBYCbCr {
47 | // MARK: - MTLFunction
48 |
49 | private static let shaderFunc: ShaderFunc = ShaderFunc()
50 |
51 | fileprivate struct ShaderFunc {
52 | fileprivate let kernel: MTLFunction = MCCore.library.makeFunction(name: "kernel_YCbCrToRGBYCbCr")!
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/Geometrics/Geometrics.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/10/04.
6 | //
7 |
8 | import Foundation
9 |
10 | extension MCFilter { public struct Geometrics { private init() {} /* このstructはnamespace用途なのでインスタンス化防止 */ } }
11 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/Geometrics/Transform.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Transform.swift
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/10/04.
6 | //
7 |
8 | import Foundation
9 | import GLKit
10 | import Metal
11 |
12 | extension MCFilter.Geometrics {
13 | public class Transform {
14 | // MARK: - vars
15 | ////////////////////////////////////////////////////////////////////
16 | // output
17 | public let outputPixelBuffer: CVPixelBuffer
18 | public let outputTexture: MTLTexture
19 | ////////////////////////////////////////////////////////////////////
20 | // pipeline
21 | private let pipelineState: MCRenderPipelineState
22 | private let vertexInBuffer: MTLBuffer
23 | private var renderPassDescriptor: MTLRenderPassDescriptor = MTLRenderPassDescriptor()
24 |
25 | // MatrixBuffer
26 | private let projectionMatrixBuffer: MTLBuffer
27 |
28 | public init(size: CGSize, orthoType: MCFilter.Geometrics.Transform.OrthoType) throws {
29 | self.pipelineState = try MCRenderPipelineState(
30 | vertex: MCFilter.Geometrics.Transform.shaderFunc.vertex,
31 | fragment: MCFilter.Geometrics.Transform.shaderFunc.fragment,
32 | label: "TransformFilter"
33 | )
34 |
35 | // projectionMatrix生成
36 | var projection: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
37 | projection.glkMatrix = orthoType.getMatrix(size: size)
38 | self.projectionMatrixBuffer = try MCCore.makeBuffer(data: projection.raw)
39 |
40 | // outputPixelBuffer & outputTexture生成
41 | var outputPixelBuffer: CVPixelBuffer = CVPixelBuffer.create(size: size)!
42 | self.outputPixelBuffer = outputPixelBuffer
43 | self.outputTexture = MCCore.texture(pixelBuffer: &outputPixelBuffer, mtlPixelFormat: MTLPixelFormat.bgra8Unorm)!
44 |
45 | self.vertexInBuffer = try MCCore.makeBuffer(data: MCShaderPreset.normalizedVertex)
46 | self.renderPassDescriptor.colorAttachments[0].texture = self.outputTexture
47 | }
48 |
49 | deinit {
50 | self.dispose()
51 | }
52 |
53 | public func dispose() {}
54 |
55 | public func process(commandBuffer: MTLCommandBuffer, transform: MCGeom.Matrix4x4 = MCGeom.Matrix4x4(), source: MTLTexture, anchorPoint: MCFilter.Geometrics.Transform.Anchor = .topLeft) throws {
56 | let imageMatrix: MCGeom.Matrix4x4 = anchorPoint.getMatrix(size: CGSize(w: CGFloat(source.width), h: CGFloat(source.height)))
57 | let objMatrix: MCGeom.Matrix4x4 = transform * imageMatrix
58 | let objMatrixBuffer: MTLBuffer = try MCCore.makeBuffer(data: objMatrix.raw)
59 |
60 | guard let renderCommandEncoder: MTLRenderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: self.renderPassDescriptor) else { throw MCFilter.ErrorType.drawError }
61 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
62 | renderCommandEncoder.setVertexBuffer(self.vertexInBuffer, offset: 0, index: 0)
63 | renderCommandEncoder.setVertexBuffer(self.projectionMatrixBuffer, offset: 0, index: 1)
64 | renderCommandEncoder.setVertexBuffer(objMatrixBuffer, offset: 0, index: 2)
65 | renderCommandEncoder.setFragmentTexture(source, index: 0)
66 | renderCommandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
67 | renderCommandEncoder.endEncoding()
68 | }
69 | }
70 |
71 | }
72 |
73 | extension MCFilter.Geometrics.Transform {
74 | // MARK: - MTLFunction
75 |
76 | private static let shaderFunc: ShaderFunc = MCFilter.Geometrics.Transform.ShaderFunc()
77 |
78 | fileprivate struct ShaderFunc {
79 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_Transform")!
80 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_Transform")!
81 | }
82 | }
83 |
84 | public extension MCFilter.Geometrics.Transform {
85 | enum OrthoType {
86 | case perspective
87 | case center
88 | case topLeft
89 | case bottomLeft
90 |
91 | func getMatrix(size: CGSize) -> GLKMatrix4 {
92 | switch self {
93 | case .perspective: return MCGeom.Matrix4x4().glkMatrix
94 | case .center: return GLKMatrix4MakeOrtho(-Float(size.width / 2), Float(size.width / 2), Float(size.height / 2), -Float(size.height / 2), -1, 1)
95 | case .topLeft: return GLKMatrix4MakeOrtho(0, Float(size.width), Float(size.height), 0, -1, 1)
96 | case .bottomLeft: return GLKMatrix4MakeOrtho(0, Float(size.width), 0, Float(size.height), -1, 1)
97 | }
98 | }
99 | }
100 |
101 | enum Anchor {
102 | case topLeft
103 | case bottomLeft
104 | case center
105 |
106 | func getMatrix(size: CGSize) -> MCGeom.Matrix4x4 {
107 | var matrix: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
108 | matrix.scale(x: Float(size.width) / 2.0, y: (Float(size.height) / 2.0) * -1, z: 0.0)
109 |
110 | switch self {
111 | case .topLeft: matrix.translate(x: 1, y: -1, z: 0.0)
112 | case .bottomLeft: matrix.translate(x: 1, y: 1, z: 0.0)
113 | case .center: matrix.translate(x: 0, y: 0, z: 0.0)
114 | }
115 | return matrix
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/ImageBlending/MCFilterImageBlending.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCFilterImageBlending.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/02.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import CoreGraphics
12 | import MetalCanvasShaders
13 |
14 | extension MCFilter {
15 | public struct ImageBlending {
16 | // MARK: - vars
17 |
18 | private let pipeline: MTLComputePipelineState
19 |
20 | // MARK: - func
21 |
22 | public init(mode: MCFilter.ImageBlending.ImageBlendingMode) throws {
23 | let function: MTLFunction = mode.kernel
24 | self.pipeline = try MCCore.device.makeComputePipelineState(function: function)
25 | }
26 |
27 | public func process(commandBuffer: inout MTLCommandBuffer, originalTexture: inout MCTexture, overTexture: inout MCTexture, destinationTexture: inout MCTexture, renderSize: CGSize) throws {
28 | guard let encoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder() else { throw ErrorType.drawError }
29 | encoder.setComputePipelineState(self.pipeline)
30 | encoder.setTexture(originalTexture.texture, index: Int(OriginalTextureIndex.rawValue))
31 | encoder.setTexture(overTexture.texture, index: Int(OverTextureIndex.rawValue))
32 | encoder.setTexture(destinationTexture.texture, index: Int(DestinationTextureIndex.rawValue))
33 | encoder.endEncoding()
34 | }
35 | }
36 | }
37 |
38 | extension MCFilter.ImageBlending {
39 | private static let shaderFunc: ShaderFunc = MCFilter.ImageBlending.ShaderFunc()
40 |
41 | fileprivate struct ShaderFunc {
42 | fileprivate let kernelAlphaBlending: MTLFunction = MCCore.library.makeFunction(name: "kernel_imageBlending_alphaBlending")!
43 | fileprivate let kernelDepthBlending: MTLFunction = MCCore.library.makeFunction(name: "kernel_imageBlending_depthBlending")!
44 | }
45 |
46 | public enum ImageBlendingMode {
47 | case alphaBlending
48 | case depthBlending
49 |
50 | public var kernel: MTLFunction {
51 | switch self {
52 | case .alphaBlending: return MCFilter.ImageBlending.shaderFunc.kernelAlphaBlending
53 | case .depthBlending: return MCFilter.ImageBlending.shaderFunc.kernelDepthBlending
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFilter/MCFilter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCFilter.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct MCFilter {
12 | private init() {} // このstructはnamespace用途なのでインスタンス化防止
13 |
14 | public enum ErrorType: Error {
15 | case setupError
16 | case drawError
17 | case endError
18 | }
19 | }
20 |
21 | public protocol MCFilterProtocol {}
22 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCFunction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCFunction.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/02.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 |
12 | public enum MCFunction {
13 | public enum ColorSpace {
14 | case YCbCrToRGB
15 |
16 | public var kernel: MTLFunction {
17 | switch self {
18 | case .YCbCrToRGB: return MCCore.library.makeFunction(name: "kernel_YCbCrToRGB")!
19 | }
20 | }
21 | public var vertex: MTLFunction {
22 | switch self {
23 | case .YCbCrToRGB: return MCCore.library.makeFunction(name: "vertex_YCbCrToRGB")!
24 | }
25 | }
26 |
27 | public var fragment: MTLFunction {
28 | switch self {
29 | case .YCbCrToRGB: return MCCore.library.makeFunction(name: "fragment_YCbCrToRGB")!
30 | }
31 | }
32 |
33 | }
34 |
35 | public enum Primitive {
36 | case point
37 | case points
38 | case image
39 | case triangle
40 |
41 | public var vertex: MTLFunction {
42 | switch self {
43 | case .point: return MCCore.library.makeFunction(name: "vertex_primitive_point")!
44 | case .points: return MCCore.library.makeFunction(name: "vertex_primitive_points")!
45 | case .image: return MCCore.library.makeFunction(name: "vertex_primitive_image")!
46 | case .triangle: return MCCore.library.makeFunction(name: "vertex_primitive_triangle")!
47 | }
48 | }
49 |
50 | public var fragment: MTLFunction {
51 | switch self {
52 | case .point: return MCCore.library.makeFunction(name: "fragment_primitive_point")!
53 | case .points: return MCCore.library.makeFunction(name: "fragment_primitive_points")!
54 | case .image: return MCCore.library.makeFunction(name: "fragment_primitive_image")!
55 | case .triangle: return MCCore.library.makeFunction(name: "fragment_primitive_triangle")!
56 | }
57 | }
58 | }
59 |
60 | public enum ImageBlending {
61 | case alphaBlending
62 | case depthBlending
63 |
64 | public var kernel: MTLFunction {
65 | switch self {
66 | case .alphaBlending: return MCCore.library.makeFunction(name: "kernel_imageBlending_alphaBlending")!
67 | case .depthBlending: return MCCore.library.makeFunction(name: "kernel_imageBlending_depthBlending")!
68 | }
69 | }
70 | }
71 |
72 | public enum ColorProcessing {
73 | case lut1D
74 | case lut3D
75 |
76 | public var vertex: MTLFunction {
77 | switch self {
78 | case .lut1D: return MCCore.library.makeFunction(name: "vertex_Lut1DFilter")!
79 | case .lut3D: return MCCore.library.makeFunction(name: "vertex_Lut3DFilter")!
80 | }
81 | }
82 |
83 | public var fragment: MTLFunction {
84 | switch self {
85 | case .lut1D: return MCCore.library.makeFunction(name: "fragment_Lut1DFilter")!
86 | case .lut3D: return MCCore.library.makeFunction(name: "fragment_Lut3DFilter")!
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCGeom/MCGeomNameSpace.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GeomNameSpace.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/04.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct MCGeom { private init() {} /* このstructはnamespace用途なのでインスタンス化防止 */ }
12 |
13 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCGeom/MCMatrix4.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCMatrix4.swift
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/24.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import simd
11 | import GLKit
12 |
13 | extension MCGeom {
14 | public struct Matrix4x4 {
15 | public var glkMatrix: GLKMatrix4 = GLKMatrix4Identity
16 |
17 | public init() {}
18 | public init(angleRad: Float, aspectRatio: Float, nearZ: Float, farZ: Float) {
19 | self.glkMatrix = GLKMatrix4MakePerspective(
20 | angleRad,
21 | aspectRatio,
22 | nearZ,
23 | farZ
24 | )
25 | }
26 | }
27 | }
28 |
29 | extension MCGeom.Matrix4x4 {
30 | public init(scaleX: Float, scaleY: Float, scaleZ: Float) {
31 | self.glkMatrix = GLKMatrix4Scale(self.glkMatrix, scaleX, scaleY, scaleZ)
32 | }
33 | }
34 |
35 | extension MCGeom.Matrix4x4 {
36 | public mutating func scale(x: Float, y: Float, z: Float) {
37 | glkMatrix = GLKMatrix4Scale(glkMatrix, x, y, z)
38 | }
39 |
40 | public mutating func rotateAroundX(xAngleRad: Float, yAngleRad: Float, zAngleRad: Float) {
41 | glkMatrix = GLKMatrix4Rotate(glkMatrix, xAngleRad, 1, 0, 0)
42 | glkMatrix = GLKMatrix4Rotate(glkMatrix, yAngleRad, 0, 1, 0)
43 | glkMatrix = GLKMatrix4Rotate(glkMatrix, zAngleRad, 0, 0, 1)
44 | }
45 |
46 | public mutating func translate(x: Float, y: Float, z: Float) {
47 | glkMatrix = GLKMatrix4Translate(glkMatrix, x, y, z)
48 | }
49 |
50 | public mutating func multiplyLeft(matrix: inout MCGeom.Matrix4x4) {
51 | glkMatrix = GLKMatrix4Multiply(matrix.glkMatrix, glkMatrix)
52 | }
53 |
54 | public mutating func transpose() {
55 | glkMatrix = GLKMatrix4Transpose(glkMatrix)
56 | }
57 |
58 | public func numberOfElements() -> Int {
59 | return 16
60 | }
61 | }
62 |
63 | extension MCGeom.Matrix4x4 {
64 | public static func + (left: MCGeom.Matrix4x4, right: MCGeom.Matrix4x4) -> MCGeom.Matrix4x4 {
65 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
66 | mat.glkMatrix = GLKMatrix4Add(left.glkMatrix, right.glkMatrix)
67 | return mat
68 | }
69 |
70 | public static func * (left: MCGeom.Matrix4x4, right: MCGeom.Matrix4x4) -> MCGeom.Matrix4x4 {
71 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
72 | mat.glkMatrix = GLKMatrix4Multiply(left.glkMatrix, right.glkMatrix)
73 | return mat
74 | }
75 | }
76 |
77 | extension MCGeom.Matrix4x4 {
78 | public var raw: [Float] {
79 | return [glkMatrix.m00, glkMatrix.m01, glkMatrix.m02, glkMatrix.m03,
80 | glkMatrix.m10, glkMatrix.m11, glkMatrix.m12, glkMatrix.m13,
81 | glkMatrix.m20, glkMatrix.m21, glkMatrix.m22, glkMatrix.m23,
82 | glkMatrix.m30, glkMatrix.m31, glkMatrix.m32, glkMatrix.m33]
83 | }
84 | }
85 |
86 | /*
87 | extension MCGeom {
88 | public struct Matrix4x4 {
89 | private var m: simd_float4x4 = simd_float4x4(columns: (
90 | simd_float4.init(x: 1.0, y: 0.0, z: 0.0, w: 0.0),
91 | simd_float4.init(x: 0.0, y: 1.0, z: 0.0, w: 0.0),
92 | simd_float4.init(x: 0.0, y: 0.0, z: 1.0, w: 0.0),
93 | simd_float4.init(x: 0.0, y: 0.0, z: 0.0, w: 1.0))
94 | )
95 |
96 | public init() {}
97 | public init(_ float4x4: simd_float4x4) {
98 | self.m = float4x4
99 | }
100 | public init(angleRad: Float, aspectRatio: Float, nearZ: Float, farZ: Float) {
101 | let cotan: Float = 1.0 / tanf(angleRad / 2.0)
102 | self.m.columns = (
103 | SIMD4(cotan / aspectRatio, 0.0, 0.0, 0.0),
104 | SIMD4(0.0, cotan, 0.0, 0.0),
105 | SIMD4(0.0, 0.0, (farZ + nearZ) / (nearZ - farZ), -1.0),
106 | SIMD4(0.0, 0.0, (2.0 * farZ * nearZ) / (nearZ - farZ), 0.0)
107 | )
108 | }
109 | }
110 |
111 | }
112 |
113 | extension MCGeom.Matrix4x4 {
114 | public init(scaleX: Float, scaleY: Float, scaleZ: Float) {
115 | let m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
116 | self.m.columns = (
117 | SIMD4(m.0.x * scaleX, m.0.y * scaleX, m.0.z * scaleX, m.0.w * scaleX),
118 | SIMD4(m.1.x * scaleY, m.1.y * scaleY, m.1.z * scaleY, m.1.w * scaleY),
119 | SIMD4(m.2.x * scaleZ, m.2.y * scaleZ, m.2.z * scaleZ, m.2.w * scaleZ),
120 | SIMD4(m.3.x, m.3.y, m.3.z, m.3.w)
121 | )
122 | }
123 | }
124 |
125 | extension MCGeom.Matrix4x4 {
126 | public mutating func scale(x scaleX: Float, y scaleY: Float, z scaleZ: Float) {
127 | let m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
128 | self.m.columns = (
129 | SIMD4(m.0.x * scaleX, m.0.y * scaleX, m.0.z * scaleX, m.0.w * scaleX),
130 | SIMD4(m.1.x * scaleY, m.1.y * scaleY, m.1.z * scaleY, m.1.w * scaleY),
131 | SIMD4(m.2.x * scaleZ, m.2.y * scaleZ, m.2.z * scaleZ, m.2.w * scaleZ),
132 | SIMD4(m.3.x, m.3.y, m.3.z, m.3.w)
133 | )
134 | }
135 |
136 | /*
137 | public mutating func rotation(radians: Float, x: Float, y: Float, z: Float) {
138 | GLK_INLINE GLKMatrix4 GLKMatrix4MakeRotation(float radians, float x, float y, float z)
139 | {
140 | GLKVector3 v = GLKVector3Normalize(GLKVector3Make(x, y, z));
141 | float cos = cosf(radians);
142 | float cosp = 1.0f - cos;
143 | float sin = sinf(radians);
144 |
145 | GLKMatrix4 m = { cos + cosp * v.v[0] * v.v[0],
146 | cosp * v.v[0] * v.v[1] + v.v[2] * sin,
147 | cosp * v.v[0] * v.v[2] - v.v[1] * sin,
148 | 0.0f,
149 | cosp * v.v[0] * v.v[1] - v.v[2] * sin,
150 | cos + cosp * v.v[1] * v.v[1],
151 | cosp * v.v[1] * v.v[2] + v.v[0] * sin,
152 | 0.0f,
153 | cosp * v.v[0] * v.v[2] + v.v[1] * sin,
154 | cosp * v.v[1] * v.v[2] - v.v[0] * sin,
155 | cos + cosp * v.v[2] * v.v[2],
156 | 0.0f,
157 | 0.0f,
158 | 0.0f,
159 | 0.0f,
160 | 1.0f };
161 |
162 | return m;
163 | }
164 |
165 | }
166 | */
167 |
168 | public mutating func rotateX(radians: Float) {
169 | let cos: Float = cosf(radians);
170 | let sin: Float = sinf(radians);
171 | let mat: (SIMD4, SIMD4, SIMD4, SIMD4) = (
172 | SIMD4(1.0, 0.0, 0.0, 0.0),
173 | SIMD4(0.0, cos, sin, 0.0),
174 | SIMD4(0.0, -sin, cos, 0.0),
175 | SIMD4(0.0, 0.0, 0.0, 1.0)
176 | )
177 | let m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
178 | }
179 |
180 | public mutating func rotateY(radians: Float) {
181 | let cos: Float = cosf(radians);
182 | let sin: Float = sinf(radians);
183 | self.m.columns = (
184 | SIMD4(cos, 0.0, -sin, 0.0),
185 | SIMD4(0.0, 1.0, 0.0, 0.0),
186 | SIMD4(sin, 0.0, cos, 0.0),
187 | SIMD4(0.0, 0.0, 0.0, 1.0)
188 | )
189 | }
190 |
191 | public mutating func rotateZ(radians: Float) {
192 | let cos: Float = cosf(radians);
193 | let sin: Float = sinf(radians);
194 | self.m.columns = (
195 | SIMD4(cos, sin, 0.0, 0.0),
196 | SIMD4(-sin, cos, 0.0, 0.0),
197 | SIMD4(0.0, 0.0, 1.0, 0.0),
198 | SIMD4(0.0, 0.0, 0.0, 1.0)
199 | )
200 | }
201 |
202 | public mutating func translate(x tx: Float, y ty: Float, z tz: Float) {
203 | let m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
204 | self.m.columns = (
205 | SIMD4(m.0.x, m.0.y, m.0.z, m.0.w),
206 | SIMD4(m.1.x, m.1.y, m.1.z, m.1.w),
207 | SIMD4(m.2.x, m.2.y, m.2.z, m.2.w),
208 | SIMD4(
209 | m.0.x * tx + m.1.x * ty + m.2.x * tz + m.3.x,
210 | m.0.y * tx + m.1.y * ty + m.2.y * tz + m.3.y,
211 | m.0.z * tx + m.1.z * ty + m.2.z * tz + m.3.z,
212 | m.0.w * tx + m.1.w * ty + m.2.w * tz + m.3.w
213 | )
214 | )
215 | }
216 |
217 |
218 | public mutating func multiply(_ matrixLeft: MCGeom.Matrix4x4, _ matrixRight: MCGeom.Matrix4x4) {
219 | var m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
220 | let matrixLeft = matrixLeft.m.columns
221 | let matrixRight = matrixRight.m.columns
222 |
223 |
224 | //self.m.columns.0.x = matrixLeft.m.0.x * matrixRight.m.0.x + matrixLeft.m.1.x * matrixRight.m.1 + matrixLeft.m.2.x * matrixRight.m.0.z + matrixLeft.m.3.x * matrixRight.m.0.w
225 | /*
226 | m.columns.0.x = matrixLeft.0.x * matrixRight.0.x + matrixLeft.1.x * matrixRight.0.y + matrixLeft.2.x * matrixRight.0.z + matrixLeft.3.x * matrixRight.0.w
227 | m.columns.1.x = matrixLeft.0.x * matrixRight.1.x + matrixLeft.1.x * matrixRight.1.y + matrixLeft.2.x * matrixRight.1.z + matrixLeft.3.x * matrixRight.1.w
228 | m.columns.2.x = matrixLeft.0.x * matrixRight.2.x + matrixLeft.1.x * matrixRight.2.y + matrixLeft.2.x * matrixRight.2.z + matrixLeft.3.x * matrixRight.2.w
229 | m.columns.3.x = matrixLeft.0.x * matrixRight.3.x + matrixLeft.1.x * matrixRight.3.y + matrixLeft.2.x * matrixRight.3.z + matrixLeft.3.x * matrixRight.3.w
230 |
231 | m.columns.0.y = matrixLeft.0.y * matrixRight.0.x + matrixLeft.1.y * matrixRight.0.y + matrixLeft.2.y * matrixRight.0.z + matrixLeft.3.y * matrixRight.0.w;
232 | m.columns.1.y = matrixLeft.0.y * matrixRight.1.x + matrixLeft.1.y * matrixRight.1.y + matrixLeft.2.y * matrixRight.1.z + matrixLeft.3.y * matrixRight.1.w;
233 | m.columns.2.y = matrixLeft.0.y * matrixRight.2.x + matrixLeft.1.y * matrixRight.2.y + matrixLeft.2.y * matrixRight.2.z + matrixLeft.3.y * matrixRight.2.w;
234 | m.columns.3.y = matrixLeft.0.y * matrixRight.3.x + matrixLeft.1.y * matrixRight.3.y + matrixLeft.2.y * matrixRight.3.z + matrixLeft.3.y * matrixRight.3.w;
235 |
236 | m.columns.0.z = matrixLeft.0.z * matrixRight.0.x + matrixLeft.1.z * matrixRight.0.y + matrixLeft.2.z * matrixRight.0.z + matrixLeft.3.z * matrixRight.0.w;
237 | m.columns.1.z = matrixLeft.0.z * matrixRight.1.x + matrixLeft.1.z * matrixRight.1.y + matrixLeft.2.z * matrixRight.1.z + matrixLeft.3.z * matrixRight.1.w;
238 | m.columns.2.z = matrixLeft.0.z * matrixRight.2.x + matrixLeft.1.z * matrixRight.2.y + matrixLeft.2.z * matrixRight.2.z + matrixLeft.3.z * matrixRight.2.w;
239 | m.columns.3.z = matrixLeft.0.z * matrixRight.3.x + matrixLeft.1.z * matrixRight.3.y + matrixLeft.2.z * matrixRight.3.z + matrixLeft.3.z * matrixRight.3.w;
240 |
241 | m.columns.0.w = matrixLeft.0.w * matrixRight.0.x + matrixLeft.1.w * matrixRight.0.y + matrixLeft.2.w * matrixRight.0.z + matrixLeft.3.w * matrixRight.0.w;
242 | m.columns.1.w = matrixLeft.0.w * matrixRight.1.x + matrixLeft.1.w * matrixRight.1.y + matrixLeft.2.w * matrixRight.1.z + matrixLeft.3.w * matrixRight.1.w;
243 | m.columns.2.w = matrixLeft.0.w * matrixRight.2.x + matrixLeft.1.w * matrixRight.2.y + matrixLeft.2.w * matrixRight.2.z + matrixLeft.3.w * matrixRight.2.w;
244 | m.columns.3.w = matrixLeft.0.w * matrixRight.3.x + matrixLeft.1.w * matrixRight.3.y + matrixLeft.2.w * matrixRight.3.z + matrixLeft.3.w * matrixRight.3.w;
245 | */
246 |
247 | }
248 |
249 | /*
250 | public mutating func transpose() {
251 | glkMatrix = GLKMatrix4Transpose(glkMatrix)
252 | }
253 | */
254 | public func numberOfElements() -> Int {
255 | return 16
256 | }
257 | }
258 |
259 |
260 | extension MCGeom.Matrix4x4 {
261 | /*
262 | public static func + (left: MCGeom.Matrix4x4, right: MCGeom.Matrix4x4) -> MCGeom.Matrix4x4 {
263 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
264 | mat.glkMatrix = GLKMatrix4Add(left.glkMatrix, right.glkMatrix)
265 | return mat
266 | }
267 | */
268 |
269 |
270 | public static func * (left: MCGeom.Matrix4x4, right: MCGeom.Matrix4x4) -> MCGeom.Matrix4x4 {
271 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
272 | let matrixLeft = left.m.columns
273 | let matrixRight = right.m.columns
274 |
275 | mat.m.columns.0.x = matrixLeft.0.x * matrixRight.0.x + matrixLeft.1.x * matrixRight.0.y + matrixLeft.2.x * matrixRight.0.z + matrixLeft.3.x * matrixRight.0.w
276 | mat.m.columns.1.x = matrixLeft.0.x * matrixRight.1.x + matrixLeft.1.x * matrixRight.1.y + matrixLeft.2.x * matrixRight.1.z + matrixLeft.3.x * matrixRight.1.w
277 | mat.m.columns.2.x = matrixLeft.0.x * matrixRight.2.x + matrixLeft.1.x * matrixRight.2.y + matrixLeft.2.x * matrixRight.2.z + matrixLeft.3.x * matrixRight.2.w
278 | mat.m.columns.3.x = matrixLeft.0.x * matrixRight.3.x + matrixLeft.1.x * matrixRight.3.y + matrixLeft.2.x * matrixRight.3.z + matrixLeft.3.x * matrixRight.3.w
279 |
280 | mat.m.columns.0.y = matrixLeft.0.y * matrixRight.0.x + matrixLeft.1.y * matrixRight.0.y + matrixLeft.2.y * matrixRight.0.z + matrixLeft.3.y * matrixRight.0.w
281 | mat.m.columns.1.y = matrixLeft.0.y * matrixRight.1.x + matrixLeft.1.y * matrixRight.1.y + matrixLeft.2.y * matrixRight.1.z + matrixLeft.3.y * matrixRight.1.w
282 | mat.m.columns.2.y = matrixLeft.0.y * matrixRight.2.x + matrixLeft.1.y * matrixRight.2.y + matrixLeft.2.y * matrixRight.2.z + matrixLeft.3.y * matrixRight.2.w
283 | mat.m.columns.3.y = matrixLeft.0.y * matrixRight.3.x + matrixLeft.1.y * matrixRight.3.y + matrixLeft.2.y * matrixRight.3.z + matrixLeft.3.y * matrixRight.3.w
284 |
285 | mat.m.columns.0.z = matrixLeft.0.z * matrixRight.0.x + matrixLeft.1.z * matrixRight.0.y + matrixLeft.2.z * matrixRight.0.z + matrixLeft.3.z * matrixRight.0.w
286 | mat.m.columns.1.z = matrixLeft.0.z * matrixRight.1.x + matrixLeft.1.z * matrixRight.1.y + matrixLeft.2.z * matrixRight.1.z + matrixLeft.3.z * matrixRight.1.w
287 | mat.m.columns.2.z = matrixLeft.0.z * matrixRight.2.x + matrixLeft.1.z * matrixRight.2.y + matrixLeft.2.z * matrixRight.2.z + matrixLeft.3.z * matrixRight.2.w
288 | mat.m.columns.3.z = matrixLeft.0.z * matrixRight.3.x + matrixLeft.1.z * matrixRight.3.y + matrixLeft.2.z * matrixRight.3.z + matrixLeft.3.z * matrixRight.3.w
289 |
290 | mat.m.columns.0.w = matrixLeft.0.w * matrixRight.0.x + matrixLeft.1.w * matrixRight.0.y + matrixLeft.2.w * matrixRight.0.z + matrixLeft.3.w * matrixRight.0.w
291 | mat.m.columns.1.w = matrixLeft.0.w * matrixRight.1.x + matrixLeft.1.w * matrixRight.1.y + matrixLeft.2.w * matrixRight.1.z + matrixLeft.3.w * matrixRight.1.w
292 | mat.m.columns.2.w = matrixLeft.0.w * matrixRight.2.x + matrixLeft.1.w * matrixRight.2.y + matrixLeft.2.w * matrixRight.2.z + matrixLeft.3.w * matrixRight.2.w
293 | mat.m.columns.3.w = matrixLeft.0.w * matrixRight.3.x + matrixLeft.1.w * matrixRight.3.y + matrixLeft.2.w * matrixRight.3.z + matrixLeft.3.w * matrixRight.3.w
294 |
295 | return mat
296 | }
297 |
298 | }
299 |
300 | /*
301 | extension MCGeom.Matrix4x4 {
302 | public var raw: simd_float4x4 {
303 | return self.m
304 | }
305 | }
306 | */
307 | extension MCGeom.Matrix4x4 {
308 | public var raw: [Float] {
309 | let m: (SIMD4, SIMD4, SIMD4, SIMD4) = self.m.columns
310 | return [m.0.x, m.0.y, m.0.z, m.0.w,
311 | m.1.x, m.1.y, m.1.z, m.1.w,
312 | m.2.x, m.2.y, m.2.z, m.2.w,
313 | m.3.x, m.3.y, m.3.z, m.3.w
314 | ]
315 | }
316 | }
317 |
318 |
319 | extension MCGeom.Matrix4x4 {
320 | public static func Ortho(left: Float, right: Float, bottom: Float, top: Float, nearZ: Float, farZ: Float) -> MCGeom.Matrix4x4 {
321 | var m: simd_float4x4 = simd_float4x4()
322 |
323 | let ral: Float = right + left
324 | let rsl: Float = right - left
325 | let tab: Float = top + bottom
326 | let tsb: Float = top - bottom
327 | let fan: Float = farZ + nearZ
328 | let fsn: Float = farZ - nearZ
329 |
330 | m.columns = (
331 | SIMD4(2.0 / rsl, 0.0, 0.0, 0.0),
332 | SIMD4(0.0, 2.0 / tsb, 0.0, 0.0),
333 | SIMD4(0.0, 0.0, -2.0 / fsn, 0.0),
334 | SIMD4(-ral / rsl, -tab / tsb, -fan / fsn, 1.0)
335 | )
336 |
337 | return MCGeom.Matrix4x4.init(m)
338 | }
339 | }
340 | */
341 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCImageRenderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCImageRenderView.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2018/12/29.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import Metal
11 | import MetalKit
12 | import MetalPerformanceShaders
13 | import ProcessLogger_Swift
14 |
15 | open class MCImageRenderView: MTKView {
16 | #if targetEnvironment(simulator)
17 | private let hasMPS: Bool = false
18 | #else
19 | private let hasMPS: Bool = MPSSupportsMTLDevice(MCCore.device)
20 | #endif
21 |
22 | public var drawRect: CGRect?
23 | public var trimRect: CGRect?
24 | public var onDraw: ((_: MTKView)->Void)?
25 |
26 | private var filter: MPSImageLanczosScale?
27 |
28 | public override init(frame frameRect: CGRect, device: MTLDevice?) {
29 | super.init(frame: frameRect, device: device)
30 | self._init()
31 | }
32 |
33 | public required init(coder: NSCoder) {
34 | super.init(coder: coder)
35 | self._init()
36 | }
37 |
38 | private func _init() {
39 | self.delegate = self
40 |
41 | self.framebufferOnly = false
42 | self.enableSetNeedsDisplay = false
43 | self.autoResizeDrawable = true
44 | }
45 |
46 | open func setup() throws {
47 | guard MCCore.isMetalCanvas else { throw MCCore.MCCoreErrorType.setup }
48 | self.device = MCCore.device
49 | #if targetEnvironment(simulator)
50 | #else
51 | self.filter = MPSImageLanczosScale(device: MCCore.device)
52 | #endif
53 | }
54 |
55 | deinit {
56 | self.releaseDrawables()
57 | ProcessLogger.deinitLog(self)
58 | }
59 | }
60 |
61 | extension MCImageRenderView {
62 | public func update(texture: MTLTexture, renderSize: CGSize, queue: DispatchQueue?) {
63 | guard let commandBuffer: MTLCommandBuffer = MCCore.commandQueue.makeCommandBuffer() else { return }
64 | if let queue = queue {
65 | queue.async { [weak self] in
66 | autoreleasepool { [weak self] in
67 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
68 | }
69 | }
70 | } else {
71 | autoreleasepool { [weak self] in
72 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
73 | }
74 | }
75 | }
76 |
77 | public func update(commandBuffer: MTLCommandBuffer, texture: MTLTexture, renderSize: CGSize, queue: DispatchQueue?) {
78 | if let queue = queue {
79 | queue.async { [weak self] in
80 | autoreleasepool { [weak self] in
81 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
82 | }
83 | }
84 | } else {
85 | autoreleasepool { [weak self] in
86 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
87 | }
88 | }
89 | }
90 |
91 | fileprivate func updatePixelBuffer(commandBuffer: MTLCommandBuffer, texture: MTLTexture, renderSize: CGSize) {
92 | ////////////////////////////////////////////////////////////
93 | //
94 | guard let drawable: CAMetalDrawable = self.currentDrawable else {
95 | commandBuffer.commit()
96 | return
97 | }
98 | var commandBuffer: MTLCommandBuffer = commandBuffer
99 | ////////////////////////////////////////////////////////////
100 |
101 | ////////////////////////////////////////////////////////////
102 | // drawableSizeを最適化
103 | self.drawableSize = renderSize
104 | ////////////////////////////////////////////////////////////
105 |
106 | if self.hasMPS {
107 | // MPSが使える端末
108 |
109 | ////////////////////////////////////////////////////////////
110 | // previewScale encode
111 | let scale: Double = Double(drawable.texture.width) / Double(texture.width)
112 | var transform: MPSScaleTransform = MPSScaleTransform(scaleX: scale, scaleY: scale, translateX: 0, translateY: 0)
113 | withUnsafePointer(to: &transform) { [weak self] (transformPtr: UnsafePointer) -> Void in
114 | self?.filter?.scaleTransform = transformPtr
115 | self?.filter?.encode(commandBuffer: commandBuffer, sourceTexture: texture, destinationTexture: drawable.texture)
116 | }
117 | ////////////////////////////////////////////////////////////
118 |
119 | ////////////////////////////////////////////////////////////
120 | // commit
121 | commandBuffer.present(drawable)
122 | commandBuffer.commit()
123 | self.draw()
124 | ////////////////////////////////////////////////////////////
125 | } else {
126 | // MPSが使えない端末
127 |
128 | do {
129 | defer { commandBuffer.commit() }
130 | ////////////////////////////////////////////////////////////
131 | // previewScale encode
132 | let sourceTexture: MCTexture = try MCTexture(texture: texture)
133 | var drawableTexture: MCTexture = try MCTexture(texture: drawable.texture)
134 | let scale: Float = Float(drawableTexture.size.w) / Float(sourceTexture.size.w)
135 | let canvas: MCCanvas = try MCCanvas(destination: &drawableTexture, orthoType: MCCanvas.OrthoType.topLeft)
136 | let imageMat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4(scaleX: scale, scaleY: scale, scaleZ: 1.0)
137 |
138 | try canvas.draw(commandBuffer: commandBuffer, objects: [
139 | try MCPrimitive.Image(
140 | texture: sourceTexture,
141 | position: SIMD3(x: Float(drawableTexture.size.w) / 2.0, y: Float(drawableTexture.size.h) / 2.0, z: 0),
142 | transform: imageMat,
143 | anchorPoint: .center
144 | ),
145 | ])
146 | ////////////////////////////////////////////////////////////
147 |
148 | ////////////////////////////////////////////////////////////
149 | // commit
150 | commandBuffer.present(drawable)
151 | self.draw()
152 | ////////////////////////////////////////////////////////////
153 |
154 | } catch {
155 | ProcessLogger.log("updatePixelBuffer error")
156 | }
157 | }
158 | }
159 | }
160 |
161 | extension MCImageRenderView {
162 | public func update(texture: MCTexture, renderSize: CGSize, queue: DispatchQueue?) {
163 | guard let commandBuffer: MTLCommandBuffer = MCCore.commandQueue.makeCommandBuffer() else { return }
164 | if let queue = queue {
165 | queue.async { [weak self] in
166 | autoreleasepool { [weak self] in
167 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
168 | }
169 | }
170 | } else {
171 | autoreleasepool { [weak self] in
172 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
173 | }
174 | }
175 | }
176 |
177 | public func update(commandBuffer: MTLCommandBuffer, texture: MCTexture, renderSize: CGSize, queue: DispatchQueue?) {
178 | if let queue = queue {
179 | queue.async { [weak self] in
180 | autoreleasepool { [weak self] in
181 | self?.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
182 | }
183 | }
184 | } else {
185 | self.updatePixelBuffer(commandBuffer: commandBuffer, texture: texture, renderSize: renderSize)
186 | }
187 | }
188 |
189 | private func updatePixelBuffer(commandBuffer: MTLCommandBuffer, texture: MCTexture, renderSize: CGSize) {
190 | ////////////////////////////////////////////////////////////
191 | //
192 | guard let drawable: CAMetalDrawable = self.currentDrawable else { return }
193 | ////////////////////////////////////////////////////////////
194 |
195 | if self.hasMPS {
196 | ////////////////////////////////////////////////////////////
197 | // previewScale encode
198 | let scale: Double = Double(drawable.texture.width) / Double(texture.size.w)
199 | var transform: MPSScaleTransform = MPSScaleTransform(scaleX: scale, scaleY: scale, translateX: 0, translateY: 0)
200 | withUnsafePointer(to: &transform) { [weak self] (transformPtr: UnsafePointer) -> Void in
201 | self?.filter?.scaleTransform = transformPtr
202 | self?.filter?.encode(commandBuffer: commandBuffer, sourceTexture: texture.texture, destinationTexture: drawable.texture)
203 | }
204 | ////////////////////////////////////////////////////////////
205 |
206 | ////////////////////////////////////////////////////////////
207 | // commit
208 | commandBuffer.present(drawable)
209 | commandBuffer.commit()
210 | self.draw()
211 | ////////////////////////////////////////////////////////////
212 | } else {
213 | do {
214 | ////////////////////////////////////////////////////////////
215 | // previewScale encode
216 | var drawableTexture: MCTexture = try MCTexture(texture: drawable.texture)
217 | let scale: Float = Float(drawableTexture.size.w) / Float(texture.size.w)
218 | let canvas: MCCanvas = try MCCanvas(destination: &drawableTexture, orthoType: MCCanvas.OrthoType.topLeft)
219 | let imageMat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4(scaleX: scale, scaleY: scale, scaleZ: 1.0)
220 |
221 | try canvas.draw(commandBuffer: commandBuffer, objects: [
222 | try MCPrimitive.Image(
223 | texture: texture,
224 | position: SIMD3(x: Float(drawableTexture.size.w) / 2.0, y: Float(drawableTexture.size.h) / 2.0, z: 0),
225 | transform: imageMat,
226 | anchorPoint: .center
227 | ),
228 | ])
229 | ////////////////////////////////////////////////////////////
230 |
231 | ////////////////////////////////////////////////////////////
232 | // commit
233 | commandBuffer.present(drawable)
234 | commandBuffer.commit()
235 | self.draw()
236 | ////////////////////////////////////////////////////////////
237 |
238 | } catch {
239 | commandBuffer.commit()
240 | }
241 | }
242 | }
243 | }
244 |
245 | extension MCImageRenderView {
246 | public func updatePixelBuffer(commandBuffer: MTLCommandBuffer, source: MTLTexture, destination: MTLTexture, renderSize: CGSize) {
247 | if self.hasMPS {
248 | ////////////////////////////////////////////////////////////
249 | // previewScale encode
250 | let scale: Double = Double(destination.width) / Double(source.width)
251 | var transform: MPSScaleTransform = MPSScaleTransform(scaleX: scale, scaleY: scale, translateX: 0, translateY: 0)
252 | withUnsafePointer(to: &transform) { [weak self] (transformPtr: UnsafePointer) -> Void in
253 | self?.filter?.scaleTransform = transformPtr
254 | self?.filter?.encode(commandBuffer: commandBuffer, sourceTexture: source, destinationTexture: destination)
255 | }
256 | ////////////////////////////////////////////////////////////
257 | } else {
258 | do {
259 | ////////////////////////////////////////////////////////////
260 | // previewScale encode
261 | let texture: MCTexture = try MCTexture(texture: source)
262 | var destinationTexture: MCTexture = try MCTexture(texture: destination)
263 | let scale: Float = Float(destinationTexture.size.w) / Float(texture.size.w)
264 | let canvas: MCCanvas = try MCCanvas(destination: &destinationTexture, orthoType: MCCanvas.OrthoType.topLeft)
265 | let imageMat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4(scaleX: scale, scaleY: scale, scaleZ: 1.0)
266 |
267 | try canvas.draw(commandBuffer: commandBuffer, objects: [
268 | try MCPrimitive.Image(
269 | texture: texture,
270 | position: SIMD3(x: Float(destinationTexture.size.w) / 2.0, y: Float(destinationTexture.size.h) / 2.0, z: 0),
271 | transform: imageMat,
272 | anchorPoint: .center
273 | ),
274 | ])
275 | ////////////////////////////////////////////////////////////
276 | } catch {
277 | ProcessLogger.log("updatePixelBuffer error")
278 | }
279 | }
280 | }
281 | }
282 |
283 | extension MCImageRenderView {
284 | public func drawUpdate(drawTexture: MTLTexture) {
285 | guard
286 | let commandBuffer: MTLCommandBuffer = MCCore.commandQueue.makeCommandBuffer()
287 | else { return }
288 | self.drawUpdate(commandBuffer: commandBuffer, drawTexture: drawTexture)
289 | }
290 |
291 | public func drawUpdate(commandBuffer: MTLCommandBuffer, drawTexture: MTLTexture) {
292 | defer {
293 | commandBuffer.commit()
294 | //commandBuffer.waitUntilCompleted()
295 | }
296 | ////////////////////////////////////////////////////////////
297 | // drawableSizeを最適化
298 | if self.currentDrawable!.texture.width != drawTexture.width || self.currentDrawable!.texture.height != drawTexture.height {
299 | self.drawableSize = CGSize(width: CGFloat(drawTexture.width), height: CGFloat(drawTexture.height))
300 | }
301 |
302 | ////////////////////////////////////////////////////////////
303 | guard
304 | let drawable: CAMetalDrawable = self.currentDrawable,
305 | drawable.texture.width == drawTexture.width, drawable.texture.height == drawTexture.height
306 | else { return }
307 |
308 | ///////////////////////////////////////////////////////////////////////////////////////////
309 | // ブリットエンコード
310 | let blitEncoder: MTLBlitCommandEncoder? = commandBuffer.makeBlitCommandEncoder()
311 | blitEncoder?.copy(from: drawTexture,
312 | sourceSlice: 0,
313 | sourceLevel: 0,
314 | sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
315 | sourceSize: MTLSizeMake(drawable.texture.width, drawable.texture.height, drawable.texture.depth),
316 | to: drawable.texture,
317 | destinationSlice: 0,
318 | destinationLevel: 0,
319 | destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
320 | blitEncoder?.endEncoding()
321 | ///////////////////////////////////////////////////////////////////////////////////////////
322 |
323 | commandBuffer.present(drawable)
324 | }
325 | }
326 |
327 | extension MCImageRenderView: MTKViewDelegate {
328 | open func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
329 | open func draw(in view: MTKView) {
330 | self.onDraw?(view)
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Image/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPrimitive {
14 | public struct Image: MCPrimitiveTypeProtocol {
15 | // MARK: - vars
16 |
17 | private let pipelineState: MCRenderPipelineState
18 | private let vertexInBuffer: MTLBuffer
19 |
20 | private var texture: MCTexture
21 | private let imageMat: MCGeom.Matrix4x4
22 | private let posMat: MCGeom.Matrix4x4
23 | private let objMat: MCGeom.Matrix4x4
24 | private let objMatBuffer: MTLBuffer
25 |
26 | // MARK: - func
27 |
28 | public init(texture: MCTexture, position: SIMD3, transform: MCGeom.Matrix4x4 = MCGeom.Matrix4x4(), anchorPoint: MCPrimitive.Anchor = .topLeft) throws {
29 | self.pipelineState = try MCRenderPipelineState(
30 | vertex: MCPrimitive.Image.shaderFunc.vertex,
31 | fragment: MCPrimitive.Image.shaderFunc.fragment,
32 | label: "MCPrimitive Image"
33 | )
34 |
35 | self.texture = texture
36 | self.vertexInBuffer = try MCCore.makeBuffer(data: MCShaderPreset.normalizedVertex)
37 |
38 | // imageMatrix4x4
39 | var imageMat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
40 | imageMat.scale(x: Float(texture.size.w) / 2.0, y: (Float(texture.size.h) / 2.0) * -1, z: 0.0)
41 |
42 | switch anchorPoint {
43 | case .topLeft: imageMat.translate(x: 1, y: -1, z: 0.0)
44 | case .bottomLeft: imageMat.translate(x: 1, y: 1, z: 0.0)
45 | case .center: imageMat.translate(x: 0, y: 0, z: 0.0)
46 | }
47 |
48 | self.imageMat = imageMat
49 |
50 | // positionMatrix4x4
51 | var posMat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4()
52 | posMat.translate(x: position.x, y: position.y, z: 0.0)
53 | self.posMat = posMat
54 |
55 | // objectMatrix4x4
56 | self.objMat = self.posMat * (transform * self.imageMat)
57 |
58 | // objMatrix4x4MTLBuffer
59 | self.objMatBuffer = try MCCore.makeBuffer(data: self.objMat.raw)
60 | }
61 |
62 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
63 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw MCPrimitive.ErrorType.drawError }
64 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
65 | renderCommandEncoder.setVertexBuffer(self.vertexInBuffer, offset: 0, index: Int(MCVertexIndex.rawValue))
66 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
67 | renderCommandEncoder.setVertexBuffer(self.objMatBuffer, offset: 0, index: 30)
68 | renderCommandEncoder.setFragmentTexture(self.texture.texture, index: 0)
69 | renderCommandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
70 | renderCommandEncoder.endEncoding()
71 | }
72 | }
73 | }
74 |
75 | extension MCPrimitive.Image {
76 | // MARK: - MTLFunction
77 |
78 | private static let shaderFunc: ShaderFunc = MCPrimitive.Image.ShaderFunc()
79 |
80 | fileprivate struct ShaderFunc {
81 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_primitive_image")!
82 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_primitive_image")!
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/MCPrimitive.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCPrimitive.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 |
12 | public struct MCPrimitive {
13 | private init() {} /* このstructはnamespace用途なのでインスタンス化防止 */
14 |
15 | public enum ErrorType: Error {
16 | case setupError
17 | case drawError
18 | case endError
19 | }
20 |
21 | public enum Anchor {
22 | case topLeft
23 | case bottomLeft
24 | case center
25 | }
26 |
27 | public struct DrawInfo {
28 | var renderPassDescriptor: MTLRenderPassDescriptor
29 | var renderSize: MCSize
30 | var orthoType: MCCanvas.OrthoType
31 | var projectionMatrixBuffer: MTLBuffer
32 |
33 | public init(renderPassDescriptor: MTLRenderPassDescriptor, renderSize: MCSize, orthoType: MCCanvas.OrthoType, projectionMatrixBuffer: MTLBuffer) {
34 | self.renderPassDescriptor = renderPassDescriptor
35 | self.renderSize = renderSize
36 | self.orthoType = orthoType
37 | self.projectionMatrixBuffer = projectionMatrixBuffer
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/MCPrimitiveTypeProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTLCPrimitiveTypeProtocol.swift
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/25.
6 | // Copyright © 2018 町田 秀行. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 |
12 | public protocol MCPrimitiveTypeProtocol {
13 | func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Point/MCPoint+Extended.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTLCPoint+Extended.swift
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/25.
6 | // Copyright © 2018 町田 秀行. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPoint: MCPrimitiveTypeProtocol {
14 | fileprivate var renderPipelineState: MTLRenderPipelineState {
15 | let vertexFunction: MTLFunction = MCFunction.Primitive.point.vertex
16 | let fragmentFunction: MTLFunction = MCFunction.Primitive.point.fragment
17 |
18 | let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
19 | renderPipelineDescriptor.vertexFunction = vertexFunction
20 | renderPipelineDescriptor.fragmentFunction = fragmentFunction
21 | renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
22 | return try! MCCore.device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
23 | }
24 |
25 | public enum ErrorType: Error {
26 | case setupError
27 | case drawError
28 | case endError
29 | }
30 |
31 | public init(ppsition: SIMD3, color: MCColor, size: Float) {
32 | self.init()
33 | //self.position = ppsition
34 | //self.color = color.color
35 | //self.color = [color.color[0], color.color[1], color.color[2], color.color[3]]
36 | //self.size = size
37 | }
38 |
39 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
40 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw ErrorType.drawError }
41 | renderCommandEncoder.setRenderPipelineState(self.renderPipelineState)
42 | renderCommandEncoder.setRenderPipelineState(renderPipelineState)
43 | renderCommandEncoder.setVertexBytes([self], length: MemoryLayout.size, index: Int(MCVertexIndex.rawValue))
44 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
45 | renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.point, vertexStart: 0, vertexCount: 1)
46 | renderCommandEncoder.endEncoding()
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Point/Point.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Point.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2018/12/25.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPrimitive {
14 | public struct Point: MCPrimitiveTypeProtocol {
15 | // MARK: - vars
16 |
17 | fileprivate let pipelineState: MCRenderPipelineState
18 | fileprivate let pointIn: MCPointIn
19 |
20 | // MARK: - func
21 |
22 | public init(position: SIMD3, color: MCColor, size: Float) throws {
23 | self.pipelineState = try MCRenderPipelineState(
24 | vertex: MCPrimitive.Point.shaderFunc.vertex,
25 | fragment: MCPrimitive.Point.shaderFunc.fragment,
26 | label: "MCPrimitive Point"
27 | )
28 |
29 | self.pointIn = MCPointIn(position: position,
30 | color: SIMD4(color.color[0], color.color[1], color.color[2], color.color[3]),
31 | size: size)
32 | }
33 |
34 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
35 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw ErrorType.drawError }
36 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
37 | renderCommandEncoder.setVertexBytes([self.pointIn], length: MemoryLayout.size, index: Int(MCVertexIndex.rawValue))
38 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
39 | renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.point, vertexStart: 0, vertexCount: 1)
40 | renderCommandEncoder.endEncoding()
41 | }
42 | }
43 | }
44 |
45 | extension MCPrimitive.Point {
46 | // MARK: - MTLFunction
47 |
48 | private static let shaderFunc: ShaderFunc = MCPrimitive.Point.ShaderFunc()
49 |
50 | fileprivate struct ShaderFunc {
51 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_primitive_point")!
52 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_primitive_point")!
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Points.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCPoints.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPrimitive {
14 | public struct Points: MCPrimitiveTypeProtocol {
15 | fileprivate var renderPipelineState: MTLRenderPipelineState
16 |
17 | public enum ErrorType: Error {
18 | case setupError
19 | case drawError
20 | case endError
21 | }
22 |
23 | //fileprivate var vertex: [Float]
24 | //fileprivate var color: [Float]
25 | fileprivate var size: [Float]
26 |
27 | public init(positions: [SIMD3], color: MCColor, size: Float) throws {
28 | var vertex: [Float] = []
29 | for p in positions { vertex.append(p.x); vertex.append(p.y); vertex.append(p.z) }
30 | try self.init(positions: vertex, color: color, size: size)
31 | }
32 |
33 | public init(positions: [Float], color: MCColor, size: Float) throws {
34 | let vertexFunction: MTLFunction = MCFunction.Primitive.points.vertex
35 | let fragmentFunction: MTLFunction = MCFunction.Primitive.points.fragment
36 |
37 | let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
38 | renderPipelineDescriptor.vertexFunction = vertexFunction
39 | renderPipelineDescriptor.fragmentFunction = fragmentFunction
40 | renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
41 | self.renderPipelineState = try MCCore.device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
42 |
43 | //self.vertex = positions
44 | //self.color = color.color
45 | self.size = [size]
46 | }
47 |
48 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
49 | /*
50 | let vertexBuffer: MTLBuffer = try MCCore.makeBuffer(data: self.vertex)
51 | let colorBuffer: MTLBuffer = try MCCore.makeBuffer(data: self.color)
52 | let sizeBuffer: MTLBuffer = try MCCore.makeBuffer(data: self.size)
53 |
54 | //print(commandBuffer)
55 | //print(drawInfo.renderPassDescriptor)
56 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw ErrorType.drawError }
57 | renderCommandEncoder.setRenderPipelineState(self.renderPipelineState)
58 | renderCommandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: Int(MCVertexIndex.rawValue))
59 | renderCommandEncoder.setVertexBuffer(colorBuffer, offset: 0, index: Int(MCColorIndex.rawValue))
60 | renderCommandEncoder.setVertexBuffer(sizeBuffer, offset: 0, index: Int(MCSizeIndex.rawValue))
61 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
62 | renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.point, vertexStart: 0, vertexCount: self.vertex.count / 3)
63 | renderCommandEncoder.endEncoding()
64 | */
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Triangle/Rectangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Rectangle.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/05/15.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPrimitive {
14 | public struct Rectangle: MCPrimitiveTypeProtocol {
15 | // MARK: - vars
16 |
17 | fileprivate let pipelineState: MCRenderPipelineState
18 | fileprivate let vertexInBuffer: MTLBuffer
19 |
20 | // MARK: - func
21 |
22 | public init(position: MCPoint, w: Float, h: Float, color: MCColor) throws {
23 | let vertex: [MCPrimitiveVertexIn] = [
24 | MCPrimitiveVertexIn(position: SIMD4(x: position.x, y: position.y, z: 0.0, w: 1.0), color: color.color), // LT
25 | MCPrimitiveVertexIn(position: SIMD4(x: position.x, y: position.y + h, z: 0.0, w: 1.0), color: color.color), // LB
26 | MCPrimitiveVertexIn(position: SIMD4(x: position.x + w, y: position.y, z: 0.0, w: 1.0), color: color.color), // RT
27 | MCPrimitiveVertexIn(position: SIMD4(x: position.x + w, y: position.y + h, z: 0.0, w: 1.0), color: color.color), // RB
28 | ]
29 |
30 | self.pipelineState = try MCRenderPipelineState(
31 | vertex: MCPrimitive.Rectangle.shaderFunc.vertex,
32 | fragment: MCPrimitive.Rectangle.shaderFunc.fragment,
33 | label: "MCPrimitive Rectangle"
34 | )
35 |
36 | self.vertexInBuffer = try MCCore.makeBuffer(data: vertex)
37 | }
38 |
39 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
40 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw MCPrimitive.ErrorType.drawError }
41 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
42 | renderCommandEncoder.setVertexBuffer(self.vertexInBuffer, offset: 0, index: Int(MCVertexIndex.rawValue))
43 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
44 | renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.triangleStrip, vertexStart: 0, vertexCount: 4)
45 | renderCommandEncoder.endEncoding()
46 | }
47 | }
48 | }
49 |
50 | extension MCPrimitive.Rectangle {
51 | // MARK: - MTLFunction
52 |
53 | private static let shaderFunc: ShaderFunc = MCPrimitive.Rectangle.ShaderFunc()
54 |
55 | fileprivate struct ShaderFunc {
56 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_primitive_triangle")!
57 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_primitive_triangle")!
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCPrimitive/Triangle/Triangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Triangle.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/05/15.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 | import MetalCanvasShaders
12 |
13 | extension MCPrimitive {
14 | public struct Triangle: MCPrimitiveTypeProtocol {
15 | // MARK: - vars
16 |
17 | fileprivate let pipelineState: MCRenderPipelineState
18 | fileprivate let vertexInBuffer: MTLBuffer
19 | fileprivate let vertexCount: Int
20 |
21 | // MARK: - func
22 |
23 | public init(positions: [MCPoint], color: MCColor) throws {
24 | var data: [SIMD4] = []
25 | for pos in positions {
26 | data.append(SIMD4(x: pos.x, y: pos.y, z: 0.0, w: 1.0))
27 | }
28 |
29 | self.pipelineState = try MCRenderPipelineState(
30 | vertex: MCPrimitive.Triangle.shaderFunc.vertex,
31 | fragment: MCPrimitive.Triangle.shaderFunc.fragment,
32 | label: "MCPrimitive Triangle"
33 | )
34 |
35 | self.vertexInBuffer = try MCCore.makeBuffer(data: data)
36 | self.vertexCount = data.count
37 | }
38 |
39 | public func draw(commandBuffer: MTLCommandBuffer, drawInfo: MCPrimitive.DrawInfo) throws {
40 | guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: drawInfo.renderPassDescriptor) else { throw MCPrimitive.ErrorType.drawError }
41 | renderCommandEncoder.setRenderPipelineState(self.pipelineState.renderPipelineState)
42 | renderCommandEncoder.setVertexBuffer(self.vertexInBuffer, offset: 0, index: Int(MCVertexIndex.rawValue))
43 | renderCommandEncoder.setVertexBuffer(drawInfo.projectionMatrixBuffer, offset: 0, index: Int(MCProjectionMatrixIndex.rawValue))
44 | renderCommandEncoder.drawPrimitives(type: MTLPrimitiveType.triangleStrip, vertexStart: 0, vertexCount: self.vertexCount)
45 | renderCommandEncoder.endEncoding()
46 | }
47 | }
48 | }
49 |
50 | extension MCPrimitive.Triangle {
51 | // MARK: - MTLFunction
52 |
53 | private static let shaderFunc: ShaderFunc = MCPrimitive.Triangle.ShaderFunc()
54 |
55 | fileprivate struct ShaderFunc {
56 | fileprivate let vertex: MTLFunction = MCCore.library.makeFunction(name: "vertex_primitive_triangle")!
57 | fileprivate let fragment: MTLFunction = MCCore.library.makeFunction(name: "fragment_primitive_triangle")!
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCTexture/MCTexture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCTexture.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MetalKit
11 |
12 | public struct MCTexture {
13 | public enum ErrorType: Error {
14 | case createError
15 | }
16 |
17 | public var userInfo: [String: Any] = [:]
18 |
19 | public fileprivate(set) var size: MCSize
20 | public var colorPixelFormat: MTLPixelFormat { return texture.pixelFormat }
21 | public fileprivate(set) var pixelBuffer: CVPixelBuffer?
22 | public private(set) var texture: MTLTexture
23 | public init(renderSize: CGSize) throws {
24 | let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.bgra8Unorm, width: Int(renderSize.width), height: Int(renderSize.height), mipmapped: true)
25 | textureDescriptor.usage = [.shaderRead, .shaderWrite, .renderTarget, .pixelFormatView]
26 | guard let texture: MTLTexture = MCCore.device.makeTexture(descriptor: textureDescriptor) else { throw ErrorType.createError }
27 | try self.init(texture: texture)
28 | }
29 |
30 | /*
31 | @available(iOS 11, *)
32 | public init(image: UIImage, isSRGB: Bool = false) throws {
33 | let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [
34 | MTKTextureLoader.Option.SRGB: isSRGB,
35 | MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
36 | MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.private.rawValue),
37 | ]
38 | guard let cgImage = image.cgImage else { throw ErrorType.createError }
39 | let texture: MTLTexture = try MCCore.textureLoader.newTexture(cgImage: cgImage, options: textureLoaderOptions)
40 | try self.init(texture: texture)
41 | }
42 | */
43 | public init(image: CGImage, isSRGB: Bool = false) throws {
44 | let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [
45 | MTKTextureLoader.Option.SRGB: isSRGB,
46 | MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
47 | MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.private.rawValue),
48 | ]
49 | let texture: MTLTexture = try MCCore.textureLoader.newTexture(cgImage: image, options: textureLoaderOptions)
50 | try self.init(texture: texture)
51 | }
52 |
53 | public init(URL: URL, isSRGB: Bool = false) throws {
54 | let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [
55 | MTKTextureLoader.Option.SRGB: isSRGB,
56 | MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
57 | MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.private.rawValue),
58 | ]
59 | let texture: MTLTexture = try MCCore.textureLoader.newTexture(URL: URL, options: textureLoaderOptions)
60 | try self.init(texture: texture)
61 | }
62 |
63 | public init(URL: URL, commandBuffer: MTLCommandBuffer) throws {
64 | guard
65 | let inputImage: CIImage = CIImage(contentsOf: URL),
66 | let colorSpace: CGColorSpace = inputImage.colorSpace
67 | else { throw ErrorType.createError }
68 | let renderSize: CGSize = inputImage.extent.size
69 |
70 | let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
71 | pixelFormat: MTLPixelFormat.bgra8Unorm,
72 | width: Int(renderSize.width), height: Int(renderSize.height),
73 | mipmapped: true
74 | )
75 | textureDescriptor.usage = [.shaderRead, .shaderWrite]
76 | guard let texture: MTLTexture = MCCore.device.makeTexture(descriptor: textureDescriptor) else { throw ErrorType.createError }
77 | MCCore.ciContext.render(inputImage, to: texture, commandBuffer: commandBuffer, bounds: inputImage.extent, colorSpace: colorSpace)
78 | try self.init(texture: texture)
79 | }
80 |
81 | public init(pixelBuffer: CVPixelBuffer, planeIndex: Int) throws {
82 | try self.init(pixelBuffer: pixelBuffer, mtlPixelFormat: MTLPixelFormat.bgra8Unorm, planeIndex: planeIndex)
83 | }
84 |
85 | public init(pixelBuffer: CVPixelBuffer, mtlPixelFormat: MTLPixelFormat, planeIndex: Int) throws {
86 | var pixelBuffer: CVPixelBuffer = pixelBuffer
87 | guard let texture: MTLTexture = MCCore.texture(pixelBuffer: &pixelBuffer, mtlPixelFormat: mtlPixelFormat, planeIndex: planeIndex) else { throw ErrorType.createError }
88 | try self.init(texture: texture)
89 | self.pixelBuffer = pixelBuffer
90 | }
91 |
92 | public init(pixelBuffer: CVPixelBuffer, textureCache: CVMetalTextureCache, mtlPixelFormat: MTLPixelFormat, planeIndex: Int) throws {
93 | var pixelBuffer: CVPixelBuffer = pixelBuffer
94 | guard let texture: MTLTexture = MCCore.texture(pixelBuffer: &pixelBuffer, textureCache: textureCache, mtlPixelFormat: mtlPixelFormat, planeIndex: planeIndex) else { throw ErrorType.createError }
95 | try self.init(texture: texture)
96 | self.pixelBuffer = pixelBuffer
97 | }
98 |
99 | public init(texture: MTLTexture) throws {
100 | self.texture = texture
101 | self.size = MCSize(Float(texture.width), Float(texture.height))
102 | }
103 | }
104 |
105 | #if os(iOS)
106 | public extension MCTexture {
107 | public init(image: UIImage, isSRGB: Bool = false) throws {
108 | let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [
109 | MTKTextureLoader.Option.SRGB: isSRGB,
110 | MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
111 | MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.private.rawValue),
112 | ]
113 | guard let cgImage = image.cgImage else { throw ErrorType.createError }
114 | let texture: MTLTexture = try MCCore.textureLoader.newTexture(cgImage: cgImage, options: textureLoaderOptions)
115 | try self.init(texture: texture)
116 | }
117 | }
118 | #endif
119 |
120 | public extension MCTexture {
121 | func copy() throws -> MCTexture {
122 | guard let texture: MTLTexture = self.texture.makeTextureView(pixelFormat: self.colorPixelFormat) else { throw ErrorType.createError }
123 | return try MCTexture(texture: texture)
124 | }
125 |
126 | mutating func update(commandBuffer: MTLCommandBuffer, URL: URL) throws {
127 | guard let image: CIImage = CIImage(contentsOf: URL) else { throw ErrorType.createError }
128 | let colorSpace: CGColorSpace = image.colorSpace ?? CGColorSpaceCreateDeviceRGB()
129 | MCCore.ciContext.render(image, to: self.texture, commandBuffer: commandBuffer, bounds: image.extent, colorSpace: colorSpace)
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Depth/DepthMapTexture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DepthMapToTexture.swift
3 | // iOS_AVModule
4 | //
5 | // Created by hideyuki machida on 2019/01/12.
6 | // Copyright © 2019 町田 秀行. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AVFoundation
11 | import CoreImage
12 | import GraphicsLibs_Swift
13 |
14 | extension MCVision.Depth {
15 | public struct DepthMapTexture {
16 | public fileprivate(set) var texture: MCTexture?
17 | fileprivate var textureCache: CVMetalTextureCache? = MCCore.createTextureCache()
18 | fileprivate(set) var canvas: MCCanvas?
19 | fileprivate(set) var image: MCPrimitive.Image?
20 |
21 | public init () {}
22 |
23 | public mutating func update(to depthData: AVDepthData, renderSize: MCSize) throws {
24 | var depthPixelBuffer: CVPixelBuffer = depthData.depthDataMap
25 | let depthTexture: MCTexture = try MCTexture.init(pixelBuffer: depthPixelBuffer, planeIndex: 0)
26 | self.texture = depthTexture
27 | }
28 |
29 | public mutating func update(to depthData: AVDepthData, commandBuffer: inout MTLCommandBuffer, orientation: AVCaptureVideoOrientation, position: AVCaptureDevice.Position, renderSize: MCSize) throws {
30 | guard let textureCache: CVMetalTextureCache = self.textureCache else { throw MCVision.ErrorType.rendering }
31 | //////////////////////////////////////////////////////////
32 | // depthTexture 生成
33 | let depthPixelBuffer: CVPixelBuffer = depthData.depthDataMap
34 | //depthPixelBuffer.normalize()
35 | let depthWidth: Int = CVPixelBufferGetWidth(depthPixelBuffer)
36 | let depthHeight: Int = CVPixelBufferGetHeight(depthPixelBuffer)
37 | guard var newPixelBuffer: CVPixelBuffer = CVPixelBuffer.create(size: CGSize(w: CGFloat(depthWidth), h: CGFloat(depthHeight))) else { throw MCVision.ErrorType.rendering }
38 | let depthTexture: MCTexture = try MCTexture.init(pixelBuffer: newPixelBuffer, textureCache: textureCache, mtlPixelFormat: MTLPixelFormat.bgra8Unorm, planeIndex: 0)
39 | let depthImage: CIImage = CIImage(cvPixelBuffer: depthPixelBuffer, options: [:])
40 | let depthCloppdImage: CIImage = depthImage.clampedToExtent().cropped(to: depthImage.extent)
41 | MCCore.ciContext.render(depthCloppdImage, to: depthTexture.texture, commandBuffer: commandBuffer, bounds: depthCloppdImage.extent, colorSpace: depthImage.colorSpace!)
42 | //////////////////////////////////////////////////////////
43 |
44 | //////////////////////////////////////////////////////////
45 | // outTexture canvas 生成
46 | var outTexture: MCTexture
47 | if self.texture == nil {
48 | guard var newImageBuffer: CVImageBuffer = CVImageBuffer.create(size: CGSize(w: CGFloat(renderSize.w), h: CGFloat(renderSize.h))) else { return }
49 | outTexture = try MCTexture.init(pixelBuffer: newImageBuffer, textureCache: textureCache, mtlPixelFormat: MTLPixelFormat.bgra8Unorm, planeIndex: 0)
50 | self.canvas = try MCCanvas.init(destination: &outTexture, orthoType: .topLeft)
51 | } else {
52 | outTexture = self.texture!
53 | try self.canvas?.update(destination: &outTexture)
54 | }
55 | //////////////////////////////////////////////////////////
56 |
57 | //////////////////////////////////////////////////////////
58 | // Orientation変換
59 | var image: MCPrimitive.Image
60 | if self.image == nil {
61 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4.init()
62 | let angle: CGFloat = 90 * CGFloat.pi / 180
63 | mat.scale(x: renderSize.w / Float(depthTexture.size.h), y: renderSize.h / Float(depthTexture.size.w), z: 1.0)
64 | //mat.rotateZ(radians: Float(angle))
65 | mat.translate(x: 0, y: -Float(depthTexture.size.h), z: 0.0)
66 |
67 | image = try MCPrimitive.Image.init(texture: depthTexture, position: SIMD3(x: 0.0, y: 0.0, z: 0.0), transform: mat, anchorPoint: MCPrimitive.Anchor.topLeft)
68 |
69 | self.image = image
70 | } else {
71 | image = self.image!
72 | }
73 | //image.texture = depthTexture
74 | try self.canvas?.draw(commandBuffer: commandBuffer, objects: [
75 | image,
76 | ])
77 | //////////////////////////////////////////////////////////
78 |
79 | //////////////////////////////////////////////////////////
80 | // set
81 | self.texture = outTexture
82 | //////////////////////////////////////////////////////////
83 | }
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Depth/HumanSegmentationTexture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DepthMapToHumanSegmentationTexture.swift
3 | // iOS_AVModule
4 | //
5 | // Created by hideyuki machida on 2019/01/16.
6 | // Copyright © 2019 町田 秀行. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AVFoundation
11 | import CoreImage
12 | import GraphicsLibs_Swift
13 |
14 | extension MCVision.Depth {
15 | public struct HumanSegmentationTexture {
16 | private var blurRadius: Float = 2.0
17 | private var gamma: Float = 1.5
18 |
19 | public fileprivate(set) var texture: MCTexture?
20 | fileprivate(set) var canvas: MCCanvas?
21 | fileprivate(set) var image: MCPrimitive.Image?
22 |
23 | public init () {}
24 |
25 | public mutating func update(to depthData: AVDepthData, metadataFaceObjects: [AVMetadataFaceObject], commandBuffer: inout MTLCommandBuffer, renderSize: MCSize) throws {
26 | let depthPixelBuffer: CVPixelBuffer = depthData.depthDataMap
27 | //depthPixelBuffer.normalize()
28 | //guard let depthData: AVDepthData = depthData else { throw Renderer.ErrorType.rendering }
29 | guard let faceObject: AVMetadataFaceObject = metadataFaceObjects.first else { throw MCVision.ErrorType.rendering }
30 | let depthWidth: Int = CVPixelBufferGetWidth(depthPixelBuffer)
31 | let depthHeight: Int = CVPixelBufferGetHeight(depthPixelBuffer)
32 | guard var newPixelBuffer: CVPixelBuffer = CVPixelBuffer.create(size: CGSize.init(width: depthWidth, height: depthHeight)) else { throw MCVision.ErrorType.rendering }
33 | let alphaMatteTexture: MCTexture = try MCTexture.init(pixelBuffer: newPixelBuffer, planeIndex: 0)
34 |
35 | //depthPixelBuffer.normalize()
36 | let faceCenter: CGPoint = CGPoint(x: faceObject.bounds.midX, y: faceObject.bounds.midY)
37 | let scale: CGFloat = CGFloat(CVPixelBufferGetWidth(depthPixelBuffer)) / CGFloat(renderSize.w)
38 |
39 | //let pixelX: Int = Int((faceCenter.y * scale).rounded())
40 | //let pixelY: Int = Int((faceCenter.x * scale).rounded())
41 | let pixelX: Int = Int(floor(faceCenter.y * scale))
42 | let pixelY: Int = Int(floor(faceCenter.x * scale))
43 |
44 | CVPixelBufferLockBaseAddress(depthPixelBuffer, .readOnly)
45 | guard let rawPointer: UnsafeMutableRawPointer = CVPixelBufferGetBaseAddress(depthPixelBuffer) else { CVPixelBufferUnlockBaseAddress(depthPixelBuffer, .readOnly); return }
46 | let rawPointer002: Int = CVPixelBufferGetBytesPerRow(depthPixelBuffer)
47 | let rowData: UnsafeMutableRawPointer = rawPointer + pixelY * rawPointer002
48 | let memoryBound: UnsafeMutablePointer = rowData.assumingMemoryBound(to: Float32.self)
49 | let faceCenterDepth: Float32 = memoryBound[pixelX]
50 | CVPixelBufferUnlockBaseAddress(depthPixelBuffer, .readOnly)
51 |
52 | let depthCutOff = faceCenterDepth + 0.25
53 | //let depthCutOff = faceCenterDepth + 0.1
54 |
55 | CVPixelBufferLockBaseAddress(depthPixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
56 | for yMap in 0 ..< depthHeight {
57 | let rowData = CVPixelBufferGetBaseAddress(depthPixelBuffer)! + yMap * CVPixelBufferGetBytesPerRow(depthPixelBuffer)
58 | let data = UnsafeMutableBufferPointer(start: rowData.assumingMemoryBound(to: Float32.self), count: depthWidth)
59 | for index in 0 ..< depthWidth {
60 | if data[index] > 0 && data[index] <= depthCutOff {
61 | data[index] = 1.0
62 | } else {
63 | data[index] = 0.0
64 | }
65 | }
66 | }
67 | CVPixelBufferUnlockBaseAddress(depthPixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
68 |
69 | // Create the mask from that pixel buffer.
70 | let depthImage: CIImage = CIImage(cvPixelBuffer: depthPixelBuffer, options: [:])
71 | let depthCloppdImage: CIImage = depthImage.clampedToExtent().cropped(to: depthImage.extent)
72 | MCCore.ciContext.render(depthCloppdImage, to: alphaMatteTexture.texture, commandBuffer: commandBuffer, bounds: depthCloppdImage.extent, colorSpace: depthImage.colorSpace!)
73 |
74 | //////////////////////////////////////////////////////////
75 | // outTexture canvas 生成
76 | var outTexture: MCTexture
77 | if self.texture == nil {
78 | guard var newImageBuffer: CVImageBuffer = CVImageBuffer.create(size: CGSize(w: CGFloat(renderSize.w), h: CGFloat(renderSize.h))) else { return }
79 | outTexture = try MCTexture.init(pixelBuffer: newImageBuffer, mtlPixelFormat: MTLPixelFormat.bgra8Unorm, planeIndex: 0)
80 | self.canvas = try MCCanvas.init(destination: &outTexture, orthoType: .topLeft)
81 | } else {
82 | outTexture = self.texture!
83 | try self.canvas?.update(destination: &outTexture)
84 | }
85 | //////////////////////////////////////////////////////////
86 |
87 | //////////////////////////////////////////////////////////
88 | // Orientation変換
89 | var image: MCPrimitive.Image
90 | if self.image == nil {
91 | var mat: MCGeom.Matrix4x4 = MCGeom.Matrix4x4.init()
92 | let angle: CGFloat = 90 * CGFloat.pi / 180
93 | mat.scale(x: renderSize.w / Float(alphaMatteTexture.size.h), y: renderSize.h / Float(alphaMatteTexture.size.w), z: 1.0)
94 | //mat.rotateZ(radians: Float(angle))
95 | mat.translate(x: 0, y: -Float(alphaMatteTexture.size.h), z: 0.0)
96 |
97 | image = try MCPrimitive.Image.init(texture: alphaMatteTexture, position: SIMD3(x: 0.0, y: 0.0, z: 0.0), transform: mat, anchorPoint: MCPrimitive.Anchor.topLeft)
98 |
99 | self.image = image
100 | } else {
101 | image = self.image!
102 | }
103 | //image.texture = alphaMatteTexture
104 | try self.canvas?.draw(commandBuffer: commandBuffer, objects: [
105 | image,
106 | ])
107 | //////////////////////////////////////////////////////////
108 |
109 | //////////////////////////////////////////////////////////
110 | // set
111 | //self.texture = outTexture
112 | self.texture = outTexture
113 | //////////////////////////////////////////////////////////
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Detection/Face.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FaceDetection.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2018/12/31.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 | /*
9 | import Foundation
10 | import Vision
11 |
12 | extension MCVision.Detection {
13 | public class Face {
14 |
15 | //private let queue: DispatchQueue = DispatchQueue(label: "MetalCanvas.FaceDetection.queue", attributes: .concurrent)
16 | private let queue: DispatchQueue = DispatchQueue(label: "MetalCanvas.FaceDetection.queue")
17 |
18 | public struct Face {
19 | public var boundingBox: CGRect = CGRect.init()
20 | public var allPoints: [CGPoint] = []
21 | public init() {}
22 | }
23 |
24 |
25 |
26 |
27 | public enum ErrorType: Error {
28 | case trackingError
29 | }
30 |
31 | private var detectionRequests: [VNDetectFaceLandmarksRequest]?
32 | private var trackingRequests: [VNTrackObjectRequest]?
33 | lazy var sequenceRequestHandler = VNSequenceRequestHandler()
34 |
35 | var faces: [Face] = []
36 | var faceItems: [MCVision.Detection.Face.Item] = []
37 |
38 | public init() {
39 | self.prepareVisionRequest()
40 | }
41 |
42 | var isRequest: Bool = false
43 | fileprivate func prepareVisionRequest002(pixelBuffer: inout CVPixelBuffer, renderSize: MCSize) throws {
44 | self.isRequest = true
45 | var pixelBuffer = pixelBuffer
46 | let faceDetectionRequest: VNDetectFaceLandmarksRequest = VNDetectFaceLandmarksRequest(completionHandler: { [weak self] (request, error) in
47 | guard let self = self else { return }
48 | if error != nil {
49 | print("FaceDetection error: \(String(describing: error)).")
50 | }
51 |
52 | guard let faceDetectionRequest = request as? VNDetectFaceLandmarksRequest,
53 | let results = faceDetectionRequest.results as? [VNFaceObservation] else {
54 | return
55 | }
56 |
57 | print("results.count")
58 | print(results.count)
59 | self.faceItems = []
60 | for observation in results {
61 | let faceItem: MCVision.Detection.Face.Item = MCVision.Detection.Face.Item.init(id: 0, observation: observation, landmarks: observation.landmarks, renderSize: renderSize)
62 | //let faceItem: FaceDetection.FaceItem = FaceDetection.FaceItem.init(observation: observation, renderSize: renderSize)
63 | do {
64 | try faceItem.tracking(pixelBuffer: &pixelBuffer)
65 | self.faceItems.append(faceItem)
66 | } catch {
67 |
68 | }
69 | }
70 | //var isRequest: Bool = false
71 | })
72 |
73 | let imageRequestHandler: VNImageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
74 | try imageRequestHandler.perform([faceDetectionRequest])
75 | }
76 |
77 | fileprivate func prepareVisionRequest003(pixelBuffer: inout CVPixelBuffer, renderSize: MCSize) throws {
78 | self.isRequest = true
79 | var pixelBuffer = pixelBuffer
80 | let faceDetectionRequest: VNDetectFaceRectanglesRequest = VNDetectFaceRectanglesRequest(completionHandler: { [weak self] (request, error) in
81 | guard let self = self else { return }
82 | if error != nil {
83 | print("FaceDetection error: \(String(describing: error)).")
84 | }
85 |
86 | guard let faceDetectionRequest = request as? VNDetectFaceRectanglesRequest,
87 | let results = faceDetectionRequest.results as? [VNFaceObservation] else {
88 | return
89 | }
90 |
91 | print("results.count")
92 | print(results.count)
93 | self.faceItems = []
94 | for observation in results {
95 | let faceItem: MCVision.Detection.Face.Item = MCVision.Detection.Face.Item.init(id: 0, observation: observation, landmarks: nil, renderSize: renderSize)
96 | do {
97 | try faceItem.tracking(pixelBuffer: &pixelBuffer)
98 | self.faceItems.append(faceItem)
99 | } catch {
100 |
101 | }
102 | }
103 | //var isRequest: Bool = false
104 | })
105 |
106 | let imageRequestHandler: VNImageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
107 | try imageRequestHandler.perform([faceDetectionRequest])
108 | }
109 |
110 | fileprivate func prepareVisionRequest() {
111 | self.trackingRequests = []
112 | var requests = [VNTrackObjectRequest]()
113 | let faceDetectionRequest = VNDetectFaceLandmarksRequest(completionHandler: { [weak self] (request, error) in
114 | guard let self = self else { return }
115 | if error != nil {
116 | print("FaceDetection error: \(String(describing: error)).")
117 | }
118 |
119 | guard let faceDetectionRequest = request as? VNDetectFaceLandmarksRequest,
120 | let results = faceDetectionRequest.results as? [VNFaceObservation] else {
121 | return
122 | }
123 | DispatchQueue.main.async {
124 | // Add the observations to the tracking list
125 | for observation: VNFaceObservation in results {
126 | let faceTrackingRequest: VNTrackObjectRequest = VNTrackObjectRequest(detectedObjectObservation: observation)
127 | requests.append(faceTrackingRequest)
128 | }
129 | self.trackingRequests = requests
130 | }
131 | })
132 |
133 | self.detectionRequests = [faceDetectionRequest]
134 | self.sequenceRequestHandler = VNSequenceRequestHandler()
135 | }
136 |
137 | var count: Int = 0
138 | public func detection(pixelBuffer: inout CVPixelBuffer, renderSize: MCSize, onDetection: @escaping ((_ landmarksResults: [MCVision.Detection.Face.Item])->Void)) throws -> [MCVision.Detection.Face.Item] {
139 | onDetection(self.faceItems)
140 | var pixelBuffer = pixelBuffer
141 |
142 | if self.faceItems.count >= 1 {
143 | print("@1:", self.faceItems.count)
144 | var faceItems: [MCVision.Detection.Face.Item] = []
145 | for item in self.faceItems {
146 | do {
147 | try item.tracking(pixelBuffer: &pixelBuffer)
148 | if item.isDetection {
149 | faceItems.append(item)
150 | }
151 | } catch {
152 |
153 | }
154 | }
155 | print("@2:", faceItems.count)
156 | self.faceItems = faceItems
157 | } else {
158 | self.queue.async {
159 | do {
160 | try self.prepareVisionRequest002(pixelBuffer: &pixelBuffer, renderSize: renderSize)
161 | } catch {
162 | print("DetectionError001")
163 | }
164 |
165 | }
166 | }
167 | return self.faceItems
168 | }
169 | }
170 | }
171 | */
172 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Detection/FaceItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FaceItem.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/14.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 | /*
9 | import Foundation
10 | import Vision
11 |
12 | extension MCVision.Detection.Face {
13 | public class Item {
14 | public let id: Int
15 | private let queue: DispatchQueue
16 | public var boundingBox: CGRect = CGRect.init()
17 | public var allPoints: [CGPoint] = []
18 | public var landmarks: VNFaceLandmarks2D?
19 | private var renderSize: MCSize
20 | var faceTrackingRequest: VNTrackObjectRequest? = nil
21 | let sequenceRequestHandler: VNSequenceRequestHandler = VNSequenceRequestHandler()
22 | var isDetection: Bool = true
23 | var observation: VNDetectedObjectObservation
24 |
25 | public init(id: Int, observation: VNFaceObservation, landmarks: VNFaceLandmarks2D?, renderSize: MCSize) {
26 | self.id = id
27 | let uuid = NSUUID().uuidString
28 | self.queue = DispatchQueue(label: "MetalCanvas.FaceDetection.FaceItem.\(uuid).queue")
29 | //self.queue = DispatchQueue(label: "MetalCanvas.FaceDetection.FaceItem.queue")
30 | //self.faceTrackingRequest = VNTrackObjectRequest(detectedObjectObservation: observation)
31 | self.renderSize = renderSize
32 | self.observation = observation
33 |
34 | guard let landmarks: VNFaceLandmarks2D = landmarks else { return }
35 | self.landmarks = landmarks
36 | if let faceContour: VNFaceLandmarkRegion2D = landmarks.allPoints {
37 | let points: [CGPoint] = faceContour.pointsInImage(imageSize: self.renderSize.toCGSize())
38 | self.allPoints = points
39 | }
40 |
41 | }
42 |
43 | public func landmarkDetection(pixelBuffer: inout CVPixelBuffer, observation: VNDetectedObjectObservation) throws {
44 | let start: TimeInterval = Date().timeIntervalSince1970
45 | let faceLandmarksRequest: VNDetectFaceLandmarksRequest = VNDetectFaceLandmarksRequest(completionHandler: { [weak self] (request, error) in
46 | guard let self = self else { return }
47 | if error != nil {
48 | print("FaceDetection error: \(String(describing: error)).")
49 | }
50 |
51 | guard let faceDetectionRequest = request as? VNDetectFaceLandmarksRequest,
52 | let results = faceDetectionRequest.results as? [VNFaceObservation] else {
53 | return
54 | }
55 |
56 | print(results)
57 | let end: TimeInterval = Date().timeIntervalSince1970 - start
58 | print("VNDetectFaceLandmarksRequest time: \(end)")
59 |
60 | for faceObservation: VNFaceObservation in results {
61 | //self.observation = faceObservation
62 | guard let landmarks: VNFaceLandmarks2D = faceObservation.landmarks else { continue }
63 | if let faceContour: VNFaceLandmarkRegion2D = landmarks.allPoints {
64 | let points: [CGPoint] = faceContour.pointsInImage(imageSize: self.renderSize.toCGSize())
65 | self.boundingBox = observation.boundingBox
66 | self.allPoints = points
67 | self.landmarks = landmarks
68 | }
69 | }
70 |
71 | })
72 | let faceObservation = VNFaceObservation(boundingBox: observation.boundingBox)
73 | faceLandmarksRequest.inputFaceObservations = [faceObservation]
74 |
75 | let imageRequestHandler: VNImageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
76 | try imageRequestHandler.perform([faceLandmarksRequest])
77 | }
78 |
79 | public func tracking(pixelBuffer: inout CVPixelBuffer) throws {
80 | let start: TimeInterval = Date().timeIntervalSince1970
81 | var pixelBuffer = pixelBuffer
82 | //guard let faceTrackingRequest: VNTrackObjectRequest = self.faceTrackingRequest else { return }
83 |
84 | self.queue.async {
85 | var faceTrackingRequest = VNTrackObjectRequest.init(detectedObjectObservation: self.observation) { (_ request: VNRequest, error: Error?) in
86 | do {
87 | let end: TimeInterval = Date().timeIntervalSince1970 - start
88 | print("tracking time: \(end)")
89 |
90 | try self.faceTrackingComplete(request, pixelBuffer: &pixelBuffer, error: error)
91 |
92 | } catch {
93 | self.isDetection = false
94 | }
95 | }
96 |
97 | do {
98 | try self.sequenceRequestHandler.perform([faceTrackingRequest], on: pixelBuffer)
99 | } catch {
100 | self.isDetection = false
101 | }
102 |
103 | }
104 | /*
105 | //////////////////////////////////////////////////////////////
106 | self.queue.async { [weak self] in
107 | guard let self = self else { return }
108 | do {
109 | try self.sequenceRequestHandler.perform([faceTrackingRequest], on: pixelBuffer)
110 |
111 | //print(self.faceTrackingRequest.results?.first)
112 | guard let observation: VNDetectedObjectObservation = faceTrackingRequest.results?.first as? VNDetectedObjectObservation else { return }
113 |
114 | let size: CGSize = observation.boundingBox.size + 0.01
115 | let origin: CGPoint = observation.boundingBox.origin - 0.005
116 | let bounds: CGRect = CGRect.init(origin: origin, size: size)
117 |
118 | let o = VNDetectedObjectObservation.init(boundingBox: bounds)
119 | self.boundingBox = observation.boundingBox
120 | let end: TimeInterval = Date().timeIntervalSince1970 - start
121 | print("tracking time: \(end)")
122 | if !faceTrackingRequest.isLastFrame {
123 | if observation.confidence > 0.3 {
124 | faceTrackingRequest.inputObservation = o
125 | } else {
126 | faceTrackingRequest.isLastFrame = true
127 | }
128 | self.faceTrackingRequest = faceTrackingRequest
129 | try self.landmarkDetection(pixelBuffer: &pixelBuffer, observation: observation)
130 | self.isDetection = true
131 | } else {
132 | self.isDetection = false
133 | }
134 | } catch {
135 | self.isDetection = false
136 | }
137 | }
138 | //////////////////////////////////////////////////////////////
139 | */
140 | }
141 |
142 | public func faceTrackingComplete(_ request: VNRequest, pixelBuffer: inout CVPixelBuffer, error: Error?) throws {
143 | guard let faceTrackingRequest: VNTrackObjectRequest = request as? VNTrackObjectRequest else { return }
144 | guard let observation: VNDetectedObjectObservation = request.results?.first as? VNDetectedObjectObservation else { return }
145 | self.boundingBox = observation.boundingBox
146 | if !faceTrackingRequest.isLastFrame {
147 | if observation.confidence > 0.3 {
148 | faceTrackingRequest.inputObservation = observation
149 | } else {
150 | faceTrackingRequest.isLastFrame = true
151 | }
152 | try self.landmarkDetection(pixelBuffer: &pixelBuffer, observation: observation)
153 | self.isDetection = true
154 | } else {
155 | self.isDetection = false
156 | }
157 | }
158 |
159 | }
160 | }
161 | */
162 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Inference.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Inference.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/01/07.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol MCVisionImageRecognitionEventsProtocol {
12 | var onUpdate: ((_ result: [String : VisionResultProtocol]) ->Void)? {get set}
13 | init()
14 | }
15 |
16 | public extension MCVision {
17 | /*
18 | class Inference: NSObject {
19 | public fileprivate(set) var recognitionLayers: [VisionLayerProtocol] = []
20 | public fileprivate(set) var events: MCVisionImageRecognitionEventsProtocol?
21 |
22 | public func set(events: MCVisionImageRecognitionEventsProtocol) {
23 | self.events = events
24 | }
25 | }
26 | */
27 | }
28 | /*
29 | public extension MCVision.Inference {
30 | func process(sorce: MCTexture, queue: DispatchQueue) throws {
31 | try self.process(sorce: sorce, queue: queue, events: self.events)
32 | }
33 | func process(sorce: MCTexture, queue: DispatchQueue, events: MCVisionImageRecognitionEventsProtocol?) throws {
34 | var result: [String : VisionResultProtocol] = [:]
35 | for index in self.recognitionLayers.indices {
36 | try self.recognitionLayers[index].process(sorce: sorce, resul: &result, queue: queue)
37 | }
38 | self.events?.onUpdate?(result)
39 | }
40 | }
41 | */
42 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/MCVision.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCVisionNameSpace.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/14.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct MCVision {
12 | public enum ErrorType: Error {
13 | case rendering
14 | }
15 |
16 | public struct Depth { }
17 | public struct Detection { }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Protocol/VisionLayerProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VisionLayerProtocol.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/01/07.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | ///////////////////////////////////////////////////////////////////////////////////////////////////
12 |
13 | // MARK: - ImageRecognitionLayerErrorType
14 |
15 | public enum VisionLayerErrorType: Error {
16 | case decodeError
17 | case setupError
18 | case renderingError
19 | }
20 |
21 | ///////////////////////////////////////////////////////////////////////////////////////////////////
22 |
23 | public protocol VisionLayerProtocol {
24 | mutating func process(sorce: MCTexture, resul: inout [String : VisionResultProtocol], queue: DispatchQueue) throws
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/MCVision/Protocol/VisionResultProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VisionResultProtocol.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/01/07.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol VisionResultProtocol {}
12 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/Typealias.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Typealias.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/01/02.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreGraphics
11 | import MetalCanvasShaders
12 |
13 | public typealias MCSize = SIMD2
14 | public typealias MCPoint = SIMD2
15 |
16 | public extension MCSize {
17 | var w: Float { return self.x }
18 | var h: Float { return self.y }
19 |
20 | init(w: CGFloat, h: CGFloat) {
21 | self.init(Float(w), Float(h))
22 | }
23 |
24 | init(w: Int, h: Int) {
25 | self.init(Float(w), Float(h))
26 | }
27 |
28 | init(w: Float, h: Float) {
29 | self.init(w, h)
30 | }
31 |
32 | func toCGSize() -> CGSize {
33 | return CGSize(width: CGFloat(self.x), height: CGFloat(self.y))
34 | }
35 | }
36 | /*
37 | public extension MCPoint {
38 | func toCGPoint() -> CGPoint {
39 | return CGPoint.init(CGFloat(self.x), CGFloat(self.y))
40 | }
41 | }
42 | */
43 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/Utils/MCPixelFormatType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCTexture+PixelFormatType.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/04/04.
6 | // Copyright © 2020 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import CoreVideo
10 |
11 | public enum MCPixelFormatType: Int {
12 | case kCV1Monochrome = 0
13 | case kCV2Indexed
14 | case kCV4Indexed
15 | case kCV8Indexed
16 | case kCV1IndexedGray_WhiteIsZero
17 | case kCV2IndexedGray_WhiteIsZero
18 | case kCV4IndexedGray_WhiteIsZero
19 | case kCV8IndexedGray_WhiteIsZero
20 | case kCV16BE555
21 | case kCV16LE555
22 | case kCV16LE5551
23 | case kCV16BE565
24 | case kCV16LE565
25 | case kCV24RGB
26 | case kCV24BGR
27 | case kCV32ARGB
28 | case kCV32BGRA
29 | case kCV32ABGR
30 | case kCV32RGBA
31 | case kCV64ARGB
32 | case kCV48RGB
33 | case kCV32AlphaGray
34 | case kCV16Gray
35 | case kCV30RGB
36 | case kCV422YpCbCr8
37 | case kCV4444YpCbCrA8
38 | case kCV4444YpCbCrA8R
39 | case kCV4444AYpCbCr8
40 | case kCV4444AYpCbCr16
41 | case kCV444YpCbCr8
42 | case kCV422YpCbCr16
43 | case kCV422YpCbCr10
44 | case kCV444YpCbCr10
45 | case kCV420YpCbCr8Planar
46 | case kCV420YpCbCr8PlanarFullRange
47 | case kCV422YpCbCr_4A_8BiPlanar
48 | case kCV420YpCbCr8BiPlanarVideoRange
49 | case kCV420YpCbCr8BiPlanarFullRange
50 | case kCV422YpCbCr8_yuvs
51 | case kCV422YpCbCr8FullRange
52 | case kCVOneComponent8
53 | case kCVTwoComponent8
54 | case kCV30RGBLEPackedWideGamut
55 | case kCVARGB2101010LEPacked
56 | case kCVOneComponent16Half
57 | case kCVOneComponent32Float
58 | case kCVTwoComponent16Half
59 | case kCVTwoComponent32Float
60 | case kCV64RGBAHalf
61 | case kCV128RGBAFloat
62 | case kCV14Bayer_GRBG
63 | case kCV14Bayer_RGGB
64 | case kCV14Bayer_BGGR
65 | case kCV14Bayer_GBRG
66 | case kCVDisparityFloat16
67 | case kCVDisparityFloat32
68 | case kCVDepthFloat16
69 | case kCVDepthFloat32
70 | case kCV420YpCbCr10BiPlanarVideoRange
71 | case kCV422YpCbCr10BiPlanarVideoRange
72 | case kCV444YpCbCr10BiPlanarVideoRange
73 | case kCV420YpCbCr10BiPlanarFullRange
74 | case kCV422YpCbCr10BiPlanarFullRange
75 | case kCV444YpCbCr10BiPlanarFullRange
76 | case kCV420YpCbCr8VideoRange_8A_TriPlanar
77 |
78 | public var osType: OSType {
79 | switch self{
80 | case .kCV1Monochrome: return kCVPixelFormatType_1Monochrome /* 1 bit indexed */
81 | case .kCV2Indexed: return kCVPixelFormatType_2Indexed /* 2 bit indexed */
82 | case .kCV4Indexed: return kCVPixelFormatType_4Indexed /* 4 bit indexed */
83 | case .kCV8Indexed: return kCVPixelFormatType_8Indexed /* 8 bit indexed */
84 | case .kCV1IndexedGray_WhiteIsZero: return kCVPixelFormatType_1IndexedGray_WhiteIsZero /* 1 bit indexed gray, white is zero */
85 | case .kCV2IndexedGray_WhiteIsZero: return kCVPixelFormatType_2IndexedGray_WhiteIsZero /* 2 bit indexed gray, white is zero */
86 | case .kCV4IndexedGray_WhiteIsZero: return kCVPixelFormatType_4IndexedGray_WhiteIsZero /* 4 bit indexed gray, white is zero */
87 | case .kCV8IndexedGray_WhiteIsZero: return kCVPixelFormatType_8IndexedGray_WhiteIsZero /* 8 bit indexed gray, white is zero */
88 | case .kCV16BE555: return kCVPixelFormatType_16BE555 /* 16 bit BE RGB 555 */
89 | case .kCV16LE555: return kCVPixelFormatType_16LE555 /* 16 bit LE RGB 555 */
90 | case .kCV16LE5551: return kCVPixelFormatType_16LE5551 /* 16 bit LE RGB 5551 */
91 | case .kCV16BE565: return kCVPixelFormatType_16BE565 /* 16 bit BE RGB 565 */
92 | case .kCV16LE565: return kCVPixelFormatType_16LE565 /* 16 bit LE RGB 565 */
93 | case .kCV24RGB: return kCVPixelFormatType_24RGB /* 24 bit RGB */
94 | case .kCV24BGR: return kCVPixelFormatType_24BGR /* 24 bit BGR */
95 | case .kCV32ARGB: return kCVPixelFormatType_32ARGB /* 32 bit ARGB */
96 | case .kCV32BGRA: return kCVPixelFormatType_32BGRA /* 32 bit BGRA */
97 | case .kCV32ABGR: return kCVPixelFormatType_32ABGR /* 32 bit ABGR */
98 | case .kCV32RGBA: return kCVPixelFormatType_32RGBA /* 32 bit RGBA */
99 | case .kCV64ARGB: return kCVPixelFormatType_64ARGB /* 64 bit ARGB, 16-bit big-endian samples */
100 | case .kCV48RGB: return kCVPixelFormatType_48RGB /* 48 bit RGB, 16-bit big-endian samples */
101 | case .kCV32AlphaGray: return kCVPixelFormatType_32AlphaGray /* 32 bit AlphaGray, 16-bit big-endian samples, black is zero */
102 | case .kCV16Gray: return kCVPixelFormatType_16Gray /* 16 bit Grayscale, 16-bit big-endian samples, black is zero */
103 | case .kCV30RGB: return kCVPixelFormatType_30RGB /* 30 bit RGB, 10-bit big-endian samples, 2 unused padding bits (at least significant end). */
104 | case .kCV422YpCbCr8: return kCVPixelFormatType_422YpCbCr8 /* Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1 */
105 | case .kCV4444YpCbCrA8: return kCVPixelFormatType_4444YpCbCrA8 /* Component Y'CbCrA 8-bit 4:4:4:4, ordered Cb Y' Cr A */
106 | case .kCV4444YpCbCrA8R: return kCVPixelFormatType_4444YpCbCrA8R /* Component Y'CbCrA 8-bit 4:4:4:4, rendering format. full range alpha, zero biased YUV, ordered A Y' Cb Cr */
107 | case .kCV4444AYpCbCr8: return kCVPixelFormatType_4444AYpCbCr8 /* Component Y'CbCrA 8-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr. */
108 | case .kCV4444AYpCbCr16: return kCVPixelFormatType_4444AYpCbCr16 /* Component Y'CbCrA 16-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr, 16-bit little-endian samples. */
109 | case .kCV444YpCbCr8: return kCVPixelFormatType_444YpCbCr8 /* Component Y'CbCr 8-bit 4:4:4 */
110 | case .kCV422YpCbCr16: return kCVPixelFormatType_422YpCbCr16 /* Component Y'CbCr 10,12,14,16-bit 4:2:2 */
111 | case .kCV422YpCbCr10: return kCVPixelFormatType_422YpCbCr10 /* Component Y'CbCr 10-bit 4:2:2 */
112 | case .kCV444YpCbCr10: return kCVPixelFormatType_444YpCbCr10 /* Component Y'CbCr 10-bit 4:4:4 */
113 | case .kCV420YpCbCr8Planar: return kCVPixelFormatType_420YpCbCr8Planar /* Planar Component Y'CbCr 8-bit 4:2:0. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct */
114 | case .kCV420YpCbCr8PlanarFullRange: return kCVPixelFormatType_420YpCbCr8PlanarFullRange /* Planar Component Y'CbCr 8-bit 4:2:0, full range. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct */
115 | case .kCV422YpCbCr_4A_8BiPlanar: return kCVPixelFormatType_422YpCbCr_4A_8BiPlanar /* First plane: Video-range Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1; second plane: alpha 8-bit 0-255 */
116 | case .kCV420YpCbCr8BiPlanarVideoRange: return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */
117 | case .kCV420YpCbCr8BiPlanarFullRange: return kCVPixelFormatType_420YpCbCr8BiPlanarFullRange /* Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */
118 | case .kCV422YpCbCr8_yuvs: return kCVPixelFormatType_422YpCbCr8_yuvs /* Component Y'CbCr 8-bit 4:2:2, ordered Y'0 Cb Y'1 Cr */
119 | case .kCV422YpCbCr8FullRange: return kCVPixelFormatType_422YpCbCr8FullRange /* Component Y'CbCr 8-bit 4:2:2, full range, ordered Y'0 Cb Y'1 Cr */
120 | case .kCVOneComponent8: return kCVPixelFormatType_OneComponent8 /* 8 bit one component, black is zero */
121 | case .kCVTwoComponent8: return kCVPixelFormatType_TwoComponent8 /* 8 bit two component, black is zero */
122 | case .kCV30RGBLEPackedWideGamut: return kCVPixelFormatType_30RGBLEPackedWideGamut /* little-endian RGB101010, 2 MSB are zero, wide-gamut (384-895) */
123 | case .kCVARGB2101010LEPacked: return kCVPixelFormatType_ARGB2101010LEPacked /* little-endian ARGB2101010 full-range ARGB */
124 | case .kCVOneComponent16Half: return kCVPixelFormatType_OneComponent16Half /* 16 bit one component IEEE half-precision float, 16-bit little-endian samples */
125 | case .kCVOneComponent32Float: return kCVPixelFormatType_OneComponent32Float /* 32 bit one component IEEE float, 32-bit little-endian samples */
126 | case .kCVTwoComponent16Half: return kCVPixelFormatType_TwoComponent16Half /* 16 bit two component IEEE half-precision float, 16-bit little-endian samples */
127 | case .kCVTwoComponent32Float: return kCVPixelFormatType_TwoComponent32Float /* 32 bit two component IEEE float, 32-bit little-endian samples */
128 | case .kCV64RGBAHalf: return kCVPixelFormatType_64RGBAHalf /* 64 bit RGBA IEEE half-precision float, 16-bit little-endian samples */
129 | case .kCV128RGBAFloat: return kCVPixelFormatType_128RGBAFloat /* 128 bit RGBA IEEE float, 32-bit little-endian samples */
130 | case .kCV14Bayer_GRBG: return kCVPixelFormatType_14Bayer_GRBG /* Bayer 14-bit Little-Endian, packed in 16-bits, ordered G R G R... alternating with B G B G... */
131 | case .kCV14Bayer_RGGB: return kCVPixelFormatType_14Bayer_RGGB /* Bayer 14-bit Little-Endian, packed in 16-bits, ordered R G R G... alternating with G B G B... */
132 | case .kCV14Bayer_BGGR: return kCVPixelFormatType_14Bayer_BGGR /* Bayer 14-bit Little-Endian, packed in 16-bits, ordered B G B G... alternating with G R G R... */
133 | case .kCV14Bayer_GBRG: return kCVPixelFormatType_14Bayer_GBRG /* Bayer 14-bit Little-Endian, packed in 16-bits, ordered G B G B... alternating with R G R G... */
134 | case .kCVDisparityFloat16: return kCVPixelFormatType_DisparityFloat16 /* IEEE754-2008 binary16 (half float), describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) ) */
135 | case .kCVDisparityFloat32: return kCVPixelFormatType_DisparityFloat32 /* IEEE754-2008 binary32 float, describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) ) */
136 | case .kCVDepthFloat16: return kCVPixelFormatType_DepthFloat16 /* IEEE754-2008 binary16 (half float), describing the depth (distance to an object) in meters */
137 | case .kCVDepthFloat32: return kCVPixelFormatType_DepthFloat32 /* IEEE754-2008 binary32 float, describing the depth (distance to an object) in meters */
138 | case .kCV420YpCbCr10BiPlanarVideoRange: return kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange /* 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960]) */
139 | case .kCV422YpCbCr10BiPlanarVideoRange: return kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange /* 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960]) */
140 | case .kCV444YpCbCr10BiPlanarVideoRange: return kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange /* 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960]) */
141 | case .kCV420YpCbCr10BiPlanarFullRange: return kCVPixelFormatType_420YpCbCr10BiPlanarFullRange /* 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023) */
142 | case .kCV422YpCbCr10BiPlanarFullRange: return kCVPixelFormatType_422YpCbCr10BiPlanarFullRange /* 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023) */
143 | case .kCV444YpCbCr10BiPlanarFullRange: return kCVPixelFormatType_444YpCbCr10BiPlanarFullRange /* 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023) */
144 | case .kCV420YpCbCr8VideoRange_8A_TriPlanar: return kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar /* first and second planes as per 420YpCbCr8BiPlanarVideoRange (420v), alpha 8 bits in third plane full-range. No CVPlanarPixelBufferInfo struct. */
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/Utils/MCRenderPipelineState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MCRenderPipelineState.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/10/26.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Metal
11 |
12 | public struct MCRenderPipelineState {
13 | public let renderPipelineState: MTLRenderPipelineState
14 | public init(vertex: MTLFunction, fragment: MTLFunction, label: String, pixelFormat: MTLPixelFormat = .bgra8Unorm) throws {
15 | let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
16 | renderPipelineDescriptor.label = label
17 | renderPipelineDescriptor.vertexFunction = vertex
18 | renderPipelineDescriptor.fragmentFunction = fragment
19 | renderPipelineDescriptor.colorAttachments[0].pixelFormat = pixelFormat
20 | self.renderPipelineState = try MCCore.device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/Utils/MCShaderPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MetalShaderType.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/10/27.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MetalCanvasShaders
11 |
12 | public struct MCShaderPreset {
13 | public static let normalizedVertex: [MCVertexIn] = [
14 | MCVertexIn(position: SIMD4(-1, -1, 0, 1), texCoords: MCPoint(0, 1)),
15 | MCVertexIn(position: SIMD4(1, -1, 0, 1), texCoords: MCPoint(1, 1)),
16 | MCVertexIn(position: SIMD4(-1, 1, 0, 1), texCoords: MCPoint(0, 0)),
17 | MCVertexIn(position: SIMD4(1, 1, 0, 1), texCoords: MCPoint(1, 0)),
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/MetalCanvas/Utils/MCTools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tools.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/09/16.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import VideoToolbox
11 | import MetalPerformanceShaders
12 |
13 | public class MCTools {
14 | public static let shared: MCTools = MCTools()
15 | public let bundle: Bundle
16 |
17 | private init() {
18 | self.bundle = Bundle(for: type(of: self))
19 | }
20 |
21 | public let hasHEVCHardwareEncoder: Bool = {
22 | let spec: [CFString: Any]
23 | #if os(macOS)
24 | spec = [kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder: true]
25 | #else
26 | spec = [:]
27 | #endif
28 | var outID: CFString?
29 | var properties: CFDictionary?
30 | let result = VTCopySupportedPropertyDictionaryForEncoder(width: 1920, height: 1080, codecType: kCMVideoCodecType_HEVC, encoderSpecification: spec as CFDictionary, encoderIDOut: &outID, supportedPropertiesOut: &properties)
31 | if result == kVTCouldNotFindVideoEncoderErr {
32 | return false // no hardware HEVC encoder
33 | }
34 | return result == noErr
35 | }()
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/MetalCanvasShaders.m:
--------------------------------------------------------------------------------
1 | //
2 | // MetalCanvasShaders.m
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/06/28.
6 | //
7 |
8 | #import
9 | #import "include/MetalCanvasShaders.h"
10 |
11 | @implementation MetalCanvasShaders
12 |
13 | +(NSBundle*)getBundle {
14 | return SWIFTPM_MODULE_BUNDLE;
15 | }
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/MetalShaderType.metal:
--------------------------------------------------------------------------------
1 | //
2 | // test.metal
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/10/19.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "include/MCShaderTypes.h"
13 |
14 | using namespace metal;
15 |
16 | ////////////////////////////////////////////////
17 | struct PointVertexOut{
18 | float4 pos [[position]];
19 | float pointSize [[point_size]];
20 | float4 color;
21 | };
22 |
23 | ////////////////////////////////////////////////
24 | struct ImageColorInOut {
25 | float4 position [[ position ]];
26 | float2 texCoords;
27 | };
28 |
29 | ////////////////////////////////////////////////
30 | struct TriangleColorInOut {
31 | float4 position [[ position ]];
32 | float4 color;
33 | };
34 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ColorProcessing/Lut1DFilter.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Lut1DFilter.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/02/26.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | typedef struct
18 | {
19 | float intensity;
20 | } IntensityUniform;
21 |
22 | vertex ImageColorInOut vertex_Lut1DFilter(device float4 *positions [[ buffer(MCVertexIndex) ]],
23 | device float2 *texCoords [[ buffer(MCTexCoord) ]],
24 | uint vid [[ vertex_id ]])
25 | {
26 | ImageColorInOut out;
27 | out.position = positions[vid];
28 | out.texCoords = texCoords[vid];
29 | return out;
30 | }
31 | /*
32 | fragment float4 fragment_Lut1DFilter(ImageColorInOut in [[stage_in]],
33 | texture2d originalTexture [[texture(OriginalTextureIndex)]],
34 | texture2d lutTexture [[texture(1)]])
35 | //constant IntensityUniform& uniform [[ buffer(1) ]])
36 | {
37 |
38 | constexpr sampler quadSampler;
39 | float4 base = originalTexture.sample(quadSampler, in.texCoords);
40 |
41 | float LUT_Size = 16;
42 | float red = ( base.r * (LUT_Size - 1.0) + 0.4999 ) / (LUT_Size * LUT_Size);
43 | float green = ( base.g * (LUT_Size - 1.0) + 0.4999 ) / LUT_Size;
44 | float blue1 = (floor( base.b * (LUT_Size - 10.0) ) / LUT_Size) + red;
45 | float blue2 = (ceil( base.b * (LUT_Size - 1.0) ) / LUT_Size) + red;
46 | float maxv = max((base.b - blue1) / (blue2 - blue1), 0.0);
47 | float mixer = clamp(maxv, 0.0, 32.0);
48 |
49 | constexpr sampler quadSampler3;
50 | float4 newColor1 = lutTexture.sample(quadSampler3, float2( blue1, green ));
51 | constexpr sampler quadSampler4;
52 | float4 newColor2 = lutTexture.sample(quadSampler4, float2( blue2, green ));
53 |
54 | float4 newColor = (newColor1.z < 1.0) ? mix(newColor1, newColor2, fract(mixer)) : newColor1;
55 | return newColor1;
56 | //float4 newColor = mix(newColor1, newColor2, fract(mixer));
57 | //return float4(mix(base, float4(newColor.rgb, base.a), half(1.0)));
58 | //return half4(mix(base, half4(newColor.rgb, base.w), half(uniform.intensity)));
59 | }
60 | */
61 | fragment half4 fragment_Lut1DFilter(ImageColorInOut in [[stage_in]],
62 | texture2d originalTexture [[texture(OriginalTextureIndex)]],
63 | texture2d lutTexture [[texture(1)]])
64 | //constant IntensityUniform& uniform [[ buffer(1) ]])
65 | {
66 | constexpr sampler quadSampler;
67 | half4 base = originalTexture.sample(quadSampler, in.texCoords);
68 |
69 | half blueColor = base.b * 15.0h;
70 |
71 | half2 quad1;
72 | quad1.y = floor(floor(blueColor) / 8.0h);
73 | quad1.x = floor(blueColor) - (quad1.y * 8.0h);
74 |
75 | half2 quad2;
76 | quad2.y = floor(ceil(blueColor) / 1.0h);
77 | quad2.x = ceil(blueColor) - (quad2.y * 1.0h);
78 |
79 | float2 texPos1;
80 | texPos1.x = (quad1.x * 0.125) + 0.5/256.0 + ((0.125 - 1.0/256.0) * base.r);
81 | texPos1.y = (quad1.y * 0.125) + 0.5/256.0 + ((0.125 - 1.0/256.0) * base.g);
82 |
83 | float2 texPos2;
84 | texPos2.x = (quad2.x * 0.03125) + 0.5/16.0 + ((0.03125 - 1.0/16.0) * base.r);
85 | texPos2.y = (quad2.y * 0.03125) + 0.5/16.0 + ((0.03125 - 1.0/16.0) * base.g);
86 |
87 | constexpr sampler quadSampler3;
88 | half4 newColor1 = lutTexture.sample(quadSampler3, texPos1);
89 | constexpr sampler quadSampler4;
90 | half4 newColor2 = lutTexture.sample(quadSampler4, texPos2);
91 |
92 | half4 newColor = mix(newColor1, newColor2, fract(blueColor));
93 | //return newColor;
94 | return half4(mix(base, half4(newColor.rgb, base.a), half(1.0)));
95 | //return half4(mix(base, half4(newColor.rgb, base.w), half(uniform.intensity)));
96 | }
97 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ColorProcessing/Lut3DFilter.metal:
--------------------------------------------------------------------------------
1 | //
2 | // LutFilter.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/02/25.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace ColorProcessing {
18 | vertex ImageColorInOut vertex_Lut3DFilter(const device MCVertexIn *in [[ buffer(MCVertexIndex) ]],
19 | uint vid [[ vertex_id ]])
20 | {
21 | ImageColorInOut out;
22 | out.position = in[vid].position;
23 | out.texCoords = in[vid].texCoords;
24 | return out;
25 | }
26 |
27 | fragment half4 fragment_Lut3DFilter(ImageColorInOut in [[stage_in]],
28 | texture2d originalTexture [[texture(OriginalTextureIndex)]],
29 | texture2d lutTexture [[texture(1)]],
30 | constant float &intensity [[ buffer(MCIntensity) ]])
31 | {
32 | constexpr sampler quadSampler;
33 | half4 color = originalTexture.sample(quadSampler, in.texCoords);
34 |
35 | half blueColor = color.b * 63.0h;
36 |
37 | half2 quad1;
38 | quad1.y = floor(floor(blueColor) / 8.0h);
39 | quad1.x = floor(blueColor) - (quad1.y * 8.0h);
40 |
41 | half2 quad2;
42 | quad2.y = floor(ceil(blueColor) / 8.0h);
43 | quad2.x = ceil(blueColor) - (quad2.y * 8.0h);
44 |
45 | float2 texPos1;
46 | texPos1.x = (quad1.x * 0.125) + 0.5 / 512.0 + ((0.125 - 1.0 / 512.0) * color.r);
47 | texPos1.y = (quad1.y * 0.125) + 0.5 / 512.0 + ((0.125 - 1.0 / 512.0) * color.g);
48 |
49 | float2 texPos2;
50 | texPos2.x = (quad2.x * 0.125) + 0.5 / 512.0 + ((0.125 - 1.0 / 512.0) * color.r);
51 | texPos2.y = (quad2.y * 0.125) + 0.5 / 512.0 + ((0.125 - 1.0 / 512.0) * color.g);
52 |
53 | half4 newColor1 = lutTexture.sample(quadSampler, texPos1);
54 | half4 newColor2 = lutTexture.sample(quadSampler, texPos2);
55 |
56 | half4 newColor = mix(newColor1, newColor2, fract(blueColor));
57 |
58 | return half4(mix(color, half4(newColor.rgb, color.w), half(intensity)));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ColorSpace/RGBToY.metal:
--------------------------------------------------------------------------------
1 | //
2 | // RGBToY.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2020/10/04.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace ColorSpace {
18 | kernel void kernel_RGBToY(texture2d sorceRGB [[texture(0)]],
19 | texture2d destinationY [[texture(1)]],
20 | uint2 gid [[thread_position_in_grid]])
21 | {
22 | float4 sorceRGBColor = sorceRGB.read(gid);
23 | float Y = (0.299f * sorceRGBColor.r) + (0.587f * sorceRGBColor.g) + (0.114f * sorceRGBColor.b);
24 | destinationY.write(float4(Y, Y, Y, 1.0f), gid);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ColorSpace/RGBToYCbCr.metal:
--------------------------------------------------------------------------------
1 | //
2 | // YCbCrToRGB.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace ColorSpace {
18 | kernel void kernel_RGBToYCbCr(texture2d sorceRGB [[texture(0)]],
19 | texture2d destinationY [[texture(1)]],
20 | texture2d destinationCb [[texture(2)]],
21 | texture2d destinationCr [[texture(3)]],
22 | uint2 gid [[thread_position_in_grid]])
23 | {
24 | constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);
25 |
26 | float4 sorceRGBColor = sorceRGB.read(gid);
27 | uint2 gidCbCr = uint2(gid.x / 2, gid.y / 2);
28 |
29 | float Y = (0.257 * sorceRGBColor.r) + (0.504 * sorceRGBColor.g) + (0.098 * sorceRGBColor.b) + (16.0 / 255.0);
30 | float Cb = (-0.148 * sorceRGBColor.r) - (0.291 * sorceRGBColor.g) + (0.439 * sorceRGBColor.b) + (128.0/ 255.0);
31 | float Cr = (0.439 * sorceRGBColor.r) - (0.368 * sorceRGBColor.g) - (0.071 * sorceRGBColor.b) + (128.0/ 255.0);
32 |
33 | destinationY.write(float4(Y, Y, Y, 1.0f), gid);
34 | destinationCb.write(float4(Cb, 0.0, 0.0, 1.0), gidCbCr);
35 | destinationCr.write(float4(0.0, Cr, 0.0, 1.0), gidCbCr);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ColorSpace/YCbCrToRGB.metal:
--------------------------------------------------------------------------------
1 | //
2 | // YCbCrToRGB.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace ColorSpace {
18 | vertex ImageColorInOut vertex_YCbCrToRGB(const device MCVertexIn *in [[ buffer(MCVertexIndex) ]],
19 | uint vid [[ vertex_id ]])
20 | {
21 | ImageColorInOut out;
22 | out.position = in[vid].position;
23 | out.texCoords = in[vid].texCoords;
24 | return out;
25 | }
26 |
27 | fragment float4 fragment_YCbCrToRGB(ImageColorInOut in [[stage_in]],
28 | texture2d capturedImageTextureY [[ texture(0) ]],
29 | texture2d capturedImageTextureCbCr [[ texture(1) ]])
30 | {
31 |
32 | constexpr sampler colorSampler(mip_filter::linear,
33 | mag_filter::linear,
34 | min_filter::linear);
35 |
36 | const float4x4 ycbcrToRGBTransform = float4x4(
37 | float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
38 | float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
39 | float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
40 | float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
41 | );
42 |
43 | // Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
44 | float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler, in.texCoords).r,
45 | capturedImageTextureCbCr.sample(colorSampler, in.texCoords).rg, 1.0);
46 |
47 | // Return converted RGB color
48 | return ycbcrToRGBTransform * ycbcr;
49 | }
50 |
51 | float4 YCbCrToRGBYCbCr(float4 sorceYColor, float4 sorceCbCrColor) {
52 | const float4x4 ycbcrToRGBTransform = float4x4(
53 | float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
54 | float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
55 | float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
56 | float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
57 | );
58 | float4 ycbcr = float4(sorceYColor.r, sorceCbCrColor.r, sorceCbCrColor.g, 1.0);
59 | return ycbcrToRGBTransform * ycbcr;
60 | }
61 |
62 | ////////////////////////////////////////////////
63 | kernel void kernel_YCbCrToRGB(texture2d sorceY [[texture(0)]],
64 | texture2d sorceCbCr [[texture(1)]],
65 | texture2d destinationRGB [[texture(2)]],
66 | uint2 gid [[thread_position_in_grid]])
67 | {
68 |
69 | constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);
70 |
71 | float4 sorceYColor = sorceY.read(gid);
72 | float4 sorceCbCrColor = sorceCbCr.read(gid);
73 | float4 RGB = YCbCrToRGBYCbCr(sorceYColor, sorceCbCrColor);
74 | destinationRGB.write(RGB, gid);
75 | }
76 |
77 | ////////////////////////////////////////////////
78 | kernel void kernel_YCbCrToRGBYCbCr(texture2d sorceY [[texture(0)]],
79 | texture2d sorceCbCr [[texture(1)]],
80 | texture2d destinationRGB [[texture(2)]],
81 | texture2d destinationY [[texture(3)]],
82 | texture2d destinationCb [[texture(4)]],
83 | texture2d destinationCr [[texture(5)]],
84 | uint2 gid [[thread_position_in_grid]])
85 | {
86 |
87 | constexpr sampler colorSampler(mip_filter::linear, mag_filter::linear, min_filter::linear);
88 |
89 | const float4x4 ycbcrToRGBTransform = float4x4(
90 | float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
91 | float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
92 | float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
93 | float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
94 | );
95 |
96 | float4 textureYColor = sorceY.read(gid);
97 | float4 textureCbCrColor = sorceCbCr.read(gid);
98 | float4 ycbcr = float4(textureYColor.r, textureCbCrColor.r, textureCbCrColor.g, 1.0);
99 | destinationRGB.write(ycbcrToRGBTransform * ycbcr, gid);
100 |
101 | /*
102 | float4 sorceYColor = sorceY.read(gid);
103 | float4 sorceCbCrColor = sorceCbCr.read(gid);
104 | float4 RGB = YCbCrToRGBYCbCr(sorceYColor, sorceCbCrColor);
105 | destinationRGB.write(RGB, gid);
106 |
107 | float Y = sorceYColor.r;
108 | destinationY.write(float4(Y, Y, Y, 1.0f), gid);
109 |
110 | float Cb = sorceCbCrColor.g;
111 | destinationCb.write(float4(Cb, Cb, Cb, 1.0f), gid);
112 |
113 | float Cr = sorceCbCrColor.r;
114 | destinationCr.write(float4(Cr, Cr, Cr, 1.0f), gid);
115 | */
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/Geometrics/Transform.metal:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/10/04.
6 | //
7 |
8 | #include
9 | #include
10 | #include
11 | #import "../../include/MCShaderTypes.h"
12 | #import "../Shader.metal"
13 |
14 | using namespace metal;
15 |
16 | namespace Geometrics {
17 | vertex ImageColorInOut vertex_Transform(const device MCVertexIn *in [[ buffer(0) ]],
18 | const device float4x4 &projectionMat [[buffer(1)]],
19 | const device float4x4 &objMat [[buffer(2)]],
20 | uint vid [[ vertex_id ]])
21 | {
22 | float4x4 mat = projectionMat * objMat;
23 |
24 | ImageColorInOut out;
25 | out.position = mat * in[vid].position;
26 | out.texCoords = in[vid].texCoords;
27 | return out;
28 | }
29 |
30 | fragment float4 fragment_Transform(ImageColorInOut in [[ stage_in ]],
31 | texture2d texture [[ texture(0) ]])
32 | {
33 | constexpr sampler colorSampler;
34 | float4 color = texture.sample(colorSampler, in.texCoords);
35 | return color;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ImageBlending/AlphaBlending.metal:
--------------------------------------------------------------------------------
1 | //
2 | // kernel_imageAlphaBlending.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/02.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace imageBlending {
18 | kernel void kernel_imageBlending_alphaBlending(texture2d originalTexture [[texture(OriginalTextureIndex)]],
19 | texture2d overTexture [[texture(OverTextureIndex)]],
20 | texture2d destinationTexture [[texture(DestinationTextureIndex)]],
21 | uint2 gid [[thread_position_in_grid]])
22 | {
23 | float4 originalColor = originalTexture.read(gid);
24 | float4 overColor = overTexture.read(gid);
25 | float4 resultColor = float4((originalColor.rgb * (1.0 - overColor.a)) + overColor.rgb, 1.0);
26 |
27 | destinationTexture.write(resultColor, gid);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/ImageBlending/DepthBlending.metal:
--------------------------------------------------------------------------------
1 | //
2 | // DepthBlending.swift
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/18.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | namespace imageBlending {
18 | ////////////////////////////////////////////////
19 | kernel void kernel_imageBlending_depthBlending(texture2d originalTexture [[texture(OriginalTextureIndex)]],
20 | texture2d overTexture [[texture(OverTextureIndex)]],
21 | texture2d destinationTexture [[texture(DestinationTextureIndex)]],
22 | uint2 gid [[thread_position_in_grid]])
23 | {
24 | float4 originalColor = originalTexture.read(gid);
25 | float4 overColor = overTexture.read(gid);
26 |
27 | //float3 normal = normalize(overColor.rgb);
28 | //float4 black = float4(1.0, 0.0, 0.0, 1.0);
29 | //float a = (normal.r + normal.g + normal.b) / 3.0;
30 | float a = (overColor.r + overColor.g + overColor.b) / 3.0;
31 | //float4 resultColor = float4((originalColor.rgb * (1.0 - normal)) + black.rgb, 1.0);
32 | //float4 resultColor = float4((originalColor.rgb * (normal)) + overColor.rgb, 1.0);
33 | //float4 resultColor = float4((black.rgb * (normal)) + originalColor.rgb, 1.0);
34 | //float4 resultColor = float4((originalColor.rgb * (1.0 - overColor.a)) + overColor.rgb, 1.0);
35 |
36 | float4 resultColor = float4((originalColor.rgb * (1.0 - a)), 1.0);
37 |
38 | destinationTexture.write(resultColor, gid);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/Primitive/Image.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Image.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/03.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | //namespace primitive {
18 | vertex ImageColorInOut vertex_primitive_image(const device MCVertexIn *in [[ buffer(MCVertexIndex) ]],
19 | const device float4x4 &projectionMat [[buffer(MCProjectionMatrixIndex)]],
20 | const device float4x4 &objMat [[buffer(30)]],
21 | uint vid [[ vertex_id ]])
22 | {
23 | float4x4 mat = projectionMat * objMat;
24 |
25 | ImageColorInOut out;
26 | out.position = mat * in[vid].position;
27 | out.texCoords = in[vid].texCoords;
28 | return out;
29 | }
30 |
31 | fragment float4 fragment_primitive_image(ImageColorInOut in [[ stage_in ]],
32 | texture2d texture [[ texture(0) ]])
33 | {
34 | constexpr sampler colorSampler;
35 | float4 color = texture.sample(colorSampler, in.texCoords);
36 | return color;
37 | }
38 | //}
39 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/Primitive/Point.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Point.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/01/02.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | //namespace primitive {
18 | vertex PointVertexOut vertex_primitive_points(constant packed_float3 *vertex_array [[buffer(MCVertexIndex)]],
19 | const device float4 &colors [[buffer(MCColorIndex)]],
20 | const device float &size [[buffer(MCSizeIndex)]],
21 | const device float4x4 &projectionMatrix [[buffer(MCProjectionMatrixIndex)]],
22 | unsigned int vid [[vertex_id]]) {
23 |
24 | float4x4 mat = projectionMatrix;
25 | float4 pos = float4(vertex_array[vid], 1.0);
26 |
27 | PointVertexOut out;
28 | out.pos = mat * pos;
29 | out.pointSize = size;
30 | out.color = colors;
31 | return out;
32 | }
33 |
34 | fragment half4 fragment_primitive_points(PointVertexOut in [[stage_in]]) {
35 | return half4(in.color.rgba);
36 | }
37 |
38 | vertex PointVertexOut vertex_primitive_point(constant MCPointIn *vertex_array [[buffer(MCVertexIndex)]],
39 | const device float4x4 &projectionMatrix [[buffer(MCProjectionMatrixIndex)]],
40 | unsigned int vid [[vertex_id]]) {
41 |
42 | float4x4 mat = projectionMatrix;
43 | float4 pos = float4(vertex_array[vid].position, 1.0);
44 |
45 | PointVertexOut out;
46 | out.pos = mat * pos;
47 | out.pointSize = vertex_array[vid].size;
48 | out.color = vertex_array[vid].color;
49 | return out;
50 | }
51 |
52 | fragment half4 fragment_primitive_point(PointVertexOut in [[stage_in]]) {
53 | return half4(in.color.rgba);
54 | }
55 | //}
56 |
57 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/Primitive/Triangle.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Triangle.metal
3 | // MetalCanvas
4 | //
5 | // Created by hideyuki machida on 2019/05/15.
6 | // Copyright © 2019 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../../include/MCShaderTypes.h"
13 | #import "../Shader.metal"
14 |
15 | using namespace metal;
16 |
17 | //namespace primitive {
18 | vertex TriangleColorInOut vertex_primitive_triangle(const device MCPrimitiveVertexIn *in [[ buffer(MCVertexIndex) ]],
19 | const device float4x4 &projectionMatrix [[buffer(MCProjectionMatrixIndex)]],
20 | unsigned int vid [[vertex_id]])
21 | {
22 | float4x4 mat = projectionMatrix;
23 | float4 pos = in[vid].position;
24 |
25 | TriangleColorInOut out;
26 | out.position = mat * pos;
27 | out.color = in[vid].color;
28 | return out;
29 | }
30 |
31 | fragment float4 fragment_primitive_triangle(TriangleColorInOut in [[ stage_in ]])
32 | {
33 | return in.color;
34 | }
35 | //}
36 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/Shader/Shader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // test.metal
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/10/19.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 | #include
12 | #import "../include/MCShaderTypes.h"
13 |
14 | using namespace metal;
15 |
16 | ////////////////////////////////////////////////
17 | struct PointVertexOut{
18 | float4 pos [[position]];
19 | float pointSize [[point_size]];
20 | float4 color;
21 | };
22 |
23 | ////////////////////////////////////////////////
24 | struct ImageColorInOut {
25 | float4 position [[ position ]];
26 | float2 texCoords;
27 | };
28 |
29 | ////////////////////////////////////////////////
30 | struct TriangleColorInOut {
31 | float4 position [[ position ]];
32 | float4 color;
33 | };
34 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/include/MCShaderTypes.h:
--------------------------------------------------------------------------------
1 | //
2 | // MCShaderTypes.h
3 | // CameraCore
4 | //
5 | // Created by hideyuki machida on 2018/12/25.
6 | // Copyright © 2018 hideyuki machida. All rights reserved.
7 | //
8 |
9 | #ifndef MCShaderTypes_h
10 | #define MCShaderTypes_h
11 |
12 | #include
13 |
14 | typedef enum VertexInputIndex
15 | {
16 | MCProjectionMatrixIndex = 0,
17 | MCVertexIndex = 1,
18 | MCColorIndex = 2,
19 | MCSizeIndex = 3,
20 | MCTexCoord = 4,
21 | MCIntensity = 5,
22 | } VertexInputIndex;
23 |
24 | typedef enum KernelImageBlendingInputIndex
25 | {
26 | OriginalTextureIndex = 0,
27 | OverTextureIndex = 1,
28 | DestinationTextureIndex = 2,
29 | } KernelImageBlendingInputIndex;
30 |
31 | typedef struct
32 | {
33 | vector_float3 position;
34 | vector_float4 color;
35 | float size;
36 | } MCPointIn;
37 |
38 | typedef struct
39 | {
40 | vector_float3 position;
41 | vector_float4 color;
42 | } MCLine;
43 |
44 | typedef struct
45 | {
46 | vector_float4 position;
47 | vector_float2 texCoords;
48 | } MCVertexIn;
49 |
50 | typedef struct
51 | {
52 | vector_float4 position;
53 | vector_float4 color;
54 | } MCPrimitiveVertexIn;
55 |
56 | typedef struct
57 | {
58 | matrix_float4x4 projectionMat;
59 | matrix_float4x4 objectMat;
60 | } MCMatrixIn;
61 |
62 |
63 | #endif /* MTLCTypes_h */
64 |
--------------------------------------------------------------------------------
/Sources/MetalCanvasShaders/include/MetalCanvasShaders.h:
--------------------------------------------------------------------------------
1 | //
2 | // Header.h
3 | //
4 | //
5 | // Created by hideyuki machida on 2020/06/28.
6 | //
7 |
8 | #import
9 | #import "MCShaderTypes.h"
10 |
11 | @interface MetalCanvasShaders : NSObject
12 | +(NSBundle*)getBundle;
13 | @end
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import MetalCanvasTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += MetalCanvasTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Tests/MetalCanvasTests/MetalCanvasTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import MetalCanvas
3 |
4 | final class MetalCanvasTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(MetalCanvas().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/MetalCanvasTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(MetalCanvasTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------