├── .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 | --------------------------------------------------------------------------------