From 8d40eff7db843d5d9bd4454129d502fee5e54fec Mon Sep 17 00:00:00 2001 From: Muukii Date: Wed, 31 Mar 2021 03:10:47 +0900 Subject: [PATCH 1/2] Patch --- Brightroom.xcodeproj/project.pbxproj | 4 + .../DemoMTLTextureViewController.swift | 302 ++++++++++++++++++ .../Demo/Contents/TopMenuViewController.swift | 7 + 3 files changed, 313 insertions(+) create mode 100644 Sources/Demo/Contents/DemoMTLTextureViewController.swift diff --git a/Brightroom.xcodeproj/project.pbxproj b/Brightroom.xcodeproj/project.pbxproj index 6fbf02fa..90100181 100644 --- a/Brightroom.xcodeproj/project.pbxproj +++ b/Brightroom.xcodeproj/project.pbxproj @@ -157,6 +157,7 @@ 4BA8513E25EE97A600BAB430 /* PixelEditorCodeBasedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA8513D25EE97A600BAB430 /* PixelEditorCodeBasedView.swift */; }; 4BA8E738217C69DE00940518 /* FilterFade.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA8E737217C69DE00940518 /* FilterFade.swift */; }; 4BA8E73D217C716700940518 /* ClassicImageEditOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA8E73C217C716700940518 /* ClassicImageEditOptions.swift */; }; + 4BB01A1726139653002C831E /* DemoMTLTextureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB01A1626139653002C831E /* DemoMTLTextureViewController.swift */; }; 4BB4B0F8260F55AC007617D7 /* EditingStack.CropModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4B0F7260F55AC007617D7 /* EditingStack.CropModifier.swift */; }; 4BB4B0FD260F55D6007617D7 /* EditingStack.Edit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4B0FC260F55D6007617D7 /* EditingStack.Edit.swift */; }; 4BB4B105260F8589007617D7 /* LUT_64_Neutral.png in Resources */ = {isa = PBXBuildFile; fileRef = 4BB4B104260F8589007617D7 /* LUT_64_Neutral.png */; }; @@ -402,6 +403,7 @@ 4BA8513D25EE97A600BAB430 /* PixelEditorCodeBasedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelEditorCodeBasedView.swift; sourceTree = ""; }; 4BA8E737217C69DE00940518 /* FilterFade.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterFade.swift; sourceTree = ""; }; 4BA8E73C217C716700940518 /* ClassicImageEditOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicImageEditOptions.swift; sourceTree = ""; }; + 4BB01A1626139653002C831E /* DemoMTLTextureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMTLTextureViewController.swift; sourceTree = ""; }; 4BB4B0F7260F55AC007617D7 /* EditingStack.CropModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingStack.CropModifier.swift; sourceTree = ""; }; 4BB4B0FC260F55D6007617D7 /* EditingStack.Edit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingStack.Edit.swift; sourceTree = ""; }; 4BB4B104260F8589007617D7 /* LUT_64_Neutral.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = LUT_64_Neutral.png; path = Resources/LUT_64_Neutral.png; sourceTree = SOURCE_ROOT; }; @@ -666,6 +668,7 @@ 4BA41C98260CF035005E6FA7 /* DemoMaskingViewController.swift */, 4B58E888260F0DEA004A834F /* DemoPreviewViewController.swift */, 4B41035F2611EAB20061A218 /* DemoImitationsViewController.swift */, + 4BB01A1626139653002C831E /* DemoMTLTextureViewController.swift */, 4B8B94682607A9AF009BF084 /* UIViewController+Picker.swift */, ); path = Contents; @@ -1441,6 +1444,7 @@ 4B8B94692607A9AF009BF084 /* UIViewController+Picker.swift in Sources */, 4BD2728E260A05440044B3D8 /* DemoCIKernelViewController.swift in Sources */, 4B8B910726079256009BF084 /* Components.swift in Sources */, + 4BB01A1726139653002C831E /* DemoMTLTextureViewController.swift in Sources */, 4B8B911E2607961D009BF084 /* Mocks.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/Demo/Contents/DemoMTLTextureViewController.swift b/Sources/Demo/Contents/DemoMTLTextureViewController.swift new file mode 100644 index 00000000..c2020599 --- /dev/null +++ b/Sources/Demo/Contents/DemoMTLTextureViewController.swift @@ -0,0 +1,302 @@ +import AsyncDisplayKit +@testable import BrightroomEngine +@testable import BrightroomUI +import GlossButtonNode +import MetalKit +import TextureSwiftSupport +import UIKit + +final class DemoMTLTextureViewController: StackScrollNodeViewController { + + override init() { + super.init() + title = "MTLTexture" + } + + override func viewDidLoad() { + super.viewDidLoad() + + stackScrollNode.append(nodes: [ + + Components.makeSelectionCell( + title: "MTKView <- CIImage <- MTLTexture <- CGImage", + onTap: { [unowned self] in + + let device = MTLCreateSystemDefaultDevice()! + let loader = MTKTextureLoader(device: device) + let texture = try! loader.newTexture(cgImage: Asset.l1000316.image.cgImage!, options: [:]) + let sourceImage = CIImage(mtlTexture: texture, options: [:])! + + let controller = _MTLTextureViewController(sourceImage: sourceImage, displayView: SampleMTLTextureDisplayView()) + + navigationController?.pushViewController(controller, animated: true) + + } + ), + + Components.makeSelectionCell( + title: "MTKView <- CIImage <- CGImage", + onTap: { [unowned self] in + + let image = CIImage(image: Asset.l1000316.image, options: [:])! + + let controller = _MTLTextureViewController(sourceImage: image, displayView: SampleMTLTextureDisplayView()) + + navigationController?.pushViewController(controller, animated: true) + + } + ), + + Components.makeSelectionCell( + title: "UIImageView <- CIImage <- MTLTexture <- CGImage", + onTap: { [unowned self] in + + let device = MTLCreateSystemDefaultDevice()! + let loader = MTKTextureLoader(device: device) + let texture = try! loader.newTexture(cgImage: Asset.l1000316.image.cgImage!, options: [:]) + let sourceImage = CIImage(mtlTexture: texture, options: [:])! + + let controller = _MTLTextureViewController(sourceImage: sourceImage, displayView: _ImageView()) + + navigationController?.pushViewController(controller, animated: true) + + } + ), + + Components.makeSelectionCell( + title: "UIImageView <- CIImage <- CGImage", + onTap: { [unowned self] in + + let image = CIImage(image: Asset.l1000316.image, options: [:])! + + let controller = _MTLTextureViewController(sourceImage: image, displayView: _ImageView()) + + navigationController?.pushViewController(controller, animated: true) + + } + ), + + ]) + } + +} + +private final class _MTLTextureViewController: UIViewController { + + private let displayView: CIImageDisplaying & UIView + private let slider = UISlider() + + private let sourceImage: CIImage + + init(sourceImage: CIImage, displayView: CIImageDisplaying & UIView) { + self.displayView = displayView + self.sourceImage = sourceImage + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + + view.addSubview(displayView) + view.addSubview(slider) + + displayView.edgesToSuperview( + excluding: .bottom, + insets: .init(top: 20, left: 20, bottom: 20, right: 20), + usingSafeArea: true + ) + displayView.aspectRatio(1) + + slider.topToBottom(of: displayView, offset: 20) + slider.horizontalToSuperview(insets: .init(top: 0, left: 20, bottom: 0, right: 20)) + + slider.addTarget(self, action: #selector(handleValueChanged), for: .valueChanged) + slider.minimumValue = 0 + slider.maximumValue = 200 + + displayView.display(image: sourceImage) + } + + @objc private func handleValueChanged() { + + let value = slider.value + +// let blurredImage = +// sourceImage +// .clampedToExtent() +// .applyingGaussianBlur(sigma: Double(value)) +// .cropped(to: sourceImage.extent) + + displayView.postProcessing = { sourceImage in + sourceImage + .clampedToExtent() + .applyingGaussianBlur(sigma: Double(value)) + .cropped(to: sourceImage.extent) + } + +// displayView.display(image: sourceImage) + } + +} + + +private final class SampleMTLTextureDisplayView: MTKView, MTKViewDelegate, CIImageDisplaying { + + private let defaultColorSpace = CGColorSpaceCreateDeviceRGB() + + func display(image: CIImage?) { + self.image = image + setNeedsDisplay() + } + + var postProcessing: (CIImage) -> CIImage = { $0 } { + didSet { + setNeedsDisplay() + } + } + + private var image: CIImage? + + private lazy var commandQueue: MTLCommandQueue = { [unowned self] in + self.device!.makeCommandQueue()! + }() + + private lazy var ciContext: CIContext = { + [unowned self] in + CIContext(mtlDevice: self.device!) + }() + + public override init( + frame frameRect: CGRect, + device: MTLDevice? + ) { + super.init( + frame: frameRect, + device: device ?? MTLCreateSystemDefaultDevice() + ) + if super.device == nil { + fatalError("Device doesn't support Metal") + } + isOpaque = false + backgroundColor = .clear + framebufferOnly = false + delegate = self + enableSetNeedsDisplay = true + autoResizeDrawable = true + contentMode = .scaleAspectFill + clearColor = .init(red: 0, green: 0, blue: 0, alpha: 0) + clearsContextBeforeDrawing = true + + #if targetEnvironment(simulator) + #else + /// For supporting wide-color - extended sRGB + colorPixelFormat = .bgra10_xr + #endif + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { + + } + + func draw(in view: MTKView) { + + guard + let image = image, + let targetTexture = currentDrawable?.texture + else { + return + } + + let commandBuffer = commandQueue.makeCommandBuffer() + + let bounds = CGRect( + origin: .zero, + size: drawableSize + ) + + let fixedImage = image.removingExtentOffset() + + let resolvedImage = downsample(image: fixedImage, bounds: bounds, contentMode: contentMode) + + let processedImage = postProcessing(resolvedImage) + + ciContext.render( + processedImage, + to: targetTexture, + commandBuffer: commandBuffer, + bounds: bounds, + colorSpace: image.colorSpace ?? defaultColorSpace + ) + + commandBuffer?.present(currentDrawable!) + commandBuffer?.commit() + + } + + private func downsample(image: CIImage, bounds: CGRect, contentMode: UIView.ContentMode) + -> CIImage + { + + let targetRect: CGRect + + switch contentMode { + case .scaleAspectFill: + targetRect = Geometry.rectThatAspectFill( + aspectRatio: image.extent.size, + minimumRect: bounds + ) + case .scaleAspectFit: + targetRect = Geometry.rectThatAspectFit( + aspectRatio: image.extent.size, + boundingRect: bounds + ) + default: + targetRect = Geometry.rectThatAspectFit( + aspectRatio: image.extent.size, + boundingRect: bounds + ) + assertionFailure("ContentMode:\(contentMode) is not supported.") + } + + let scaleX = targetRect.width / image.extent.width + let scaleY = targetRect.height / image.extent.height + let scale = min(scaleX, scaleY) + + let resolvedImage: CIImage + + #if targetEnvironment(simulator) + // Fixes geometry in Metal + resolvedImage = + image + .transformed( + by: CGAffineTransform(scaleX: 1, y: -1) + .concatenating(.init(translationX: 0, y: image.extent.height)) + .concatenating(.init(scaleX: scale, y: scale)) + .concatenating(.init(translationX: targetRect.origin.x, y: targetRect.origin.y)) + ) + + #else + resolvedImage = + image + .transformed(by: CGAffineTransform(scaleX: scale, y: scale)) + .transformed( + by: CGAffineTransform(translationX: targetRect.origin.x, y: targetRect.origin.y) + ) + + #endif + + return resolvedImage + } + + +} diff --git a/Sources/Demo/Contents/TopMenuViewController.swift b/Sources/Demo/Contents/TopMenuViewController.swift index 45c5325e..ed6f19b6 100644 --- a/Sources/Demo/Contents/TopMenuViewController.swift +++ b/Sources/Demo/Contents/TopMenuViewController.swift @@ -47,6 +47,13 @@ final class TopMenuViewController: StackScrollNodeViewController { let menu = DemoImitationsViewController() self.navigationController?.pushViewController(menu, animated: true) }), + + Components.makeSelectionCell( + title: "Metal", + onTap: { [unowned self] in + let menu = DemoMTLTextureViewController() + self.navigationController?.pushViewController(menu, animated: true) + }), /* Components.makeSelectionCell( title: "Components: MetalImageView", From 44f69a417ad545df7274d3943c3a31d3b75c93a9 Mon Sep 17 00:00:00 2001 From: Muukii Date: Wed, 31 Mar 2021 03:39:55 +0900 Subject: [PATCH 2/2] Update Demo --- .../Shared/Components/ImageViews/ImagePreviewView.swift | 2 +- .../Shared/Components/ImageViews/MetalImageView.swift | 5 ++++- Sources/Demo/Contents/DemoMTLTextureViewController.swift | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Sources/BrightroomUI/Shared/Components/ImageViews/ImagePreviewView.swift b/Sources/BrightroomUI/Shared/Components/ImageViews/ImagePreviewView.swift index bf903ab7..54974555 100644 --- a/Sources/BrightroomUI/Shared/Components/ImageViews/ImagePreviewView.swift +++ b/Sources/BrightroomUI/Shared/Components/ImageViews/ImagePreviewView.swift @@ -183,7 +183,7 @@ public final class ImagePreviewView: PixelEditorCodeBasedView { } } -private final class _PreviewImageView: UIImageView, CIImageDisplaying { +final class _PreviewImageView: UIImageView, CIImageDisplaying { var postProcessing: (CIImage) -> CIImage = { $0 } { didSet { update() diff --git a/Sources/BrightroomUI/Shared/Components/ImageViews/MetalImageView.swift b/Sources/BrightroomUI/Shared/Components/ImageViews/MetalImageView.swift index 8368c5f3..a646c658 100644 --- a/Sources/BrightroomUI/Shared/Components/ImageViews/MetalImageView.swift +++ b/Sources/BrightroomUI/Shared/Components/ImageViews/MetalImageView.swift @@ -25,6 +25,9 @@ import UIKit import BrightroomEngine #endif +/** + https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf + */ open class MetalImageView: MTKView, CIImageDisplaying, MTKViewDelegate { public var postProcessing: (CIImage) -> CIImage = { $0 } { didSet { @@ -75,7 +78,7 @@ open class MetalImageView: MTKView, CIImageDisplaying, MTKViewDelegate { #if targetEnvironment(simulator) #else /// For supporting wide-color - extended sRGB - colorPixelFormat = .bgra10_xr +// colorPixelFormat = .bgra10_xr #endif } diff --git a/Sources/Demo/Contents/DemoMTLTextureViewController.swift b/Sources/Demo/Contents/DemoMTLTextureViewController.swift index c2020599..567f3853 100644 --- a/Sources/Demo/Contents/DemoMTLTextureViewController.swift +++ b/Sources/Demo/Contents/DemoMTLTextureViewController.swift @@ -56,7 +56,7 @@ final class DemoMTLTextureViewController: StackScrollNodeViewController { let texture = try! loader.newTexture(cgImage: Asset.l1000316.image.cgImage!, options: [:]) let sourceImage = CIImage(mtlTexture: texture, options: [:])! - let controller = _MTLTextureViewController(sourceImage: sourceImage, displayView: _ImageView()) + let controller = _MTLTextureViewController(sourceImage: sourceImage, displayView: _PreviewImageView()) navigationController?.pushViewController(controller, animated: true) @@ -69,7 +69,7 @@ final class DemoMTLTextureViewController: StackScrollNodeViewController { let image = CIImage(image: Asset.l1000316.image, options: [:])! - let controller = _MTLTextureViewController(sourceImage: image, displayView: _ImageView()) + let controller = _MTLTextureViewController(sourceImage: image, displayView: _PreviewImageView()) navigationController?.pushViewController(controller, animated: true) @@ -196,7 +196,7 @@ private final class SampleMTLTextureDisplayView: MTKView, MTKViewDelegate, CIIma #if targetEnvironment(simulator) #else /// For supporting wide-color - extended sRGB - colorPixelFormat = .bgra10_xr +// colorPixelFormat = .bgra10_xr #endif }