Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Demo for MTLTexture to preview image #86

Merged
merged 2 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions Brightroom.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -402,6 +403,7 @@
4BA8513D25EE97A600BAB430 /* PixelEditorCodeBasedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelEditorCodeBasedView.swift; sourceTree = "<group>"; };
4BA8E737217C69DE00940518 /* FilterFade.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterFade.swift; sourceTree = "<group>"; };
4BA8E73C217C716700940518 /* ClassicImageEditOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassicImageEditOptions.swift; sourceTree = "<group>"; };
4BB01A1626139653002C831E /* DemoMTLTextureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMTLTextureViewController.swift; sourceTree = "<group>"; };
4BB4B0F7260F55AC007617D7 /* EditingStack.CropModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingStack.CropModifier.swift; sourceTree = "<group>"; };
4BB4B0FC260F55D6007617D7 /* EditingStack.Edit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingStack.Edit.swift; sourceTree = "<group>"; };
4BB4B104260F8589007617D7 /* LUT_64_Neutral.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = LUT_64_Neutral.png; path = Resources/LUT_64_Neutral.png; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -666,6 +668,7 @@
4BA41C98260CF035005E6FA7 /* DemoMaskingViewController.swift */,
4B58E888260F0DEA004A834F /* DemoPreviewViewController.swift */,
4B41035F2611EAB20061A218 /* DemoImitationsViewController.swift */,
4BB01A1626139653002C831E /* DemoMTLTextureViewController.swift */,
4B8B94682607A9AF009BF084 /* UIViewController+Picker.swift */,
);
path = Contents;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down
302 changes: 302 additions & 0 deletions Sources/Demo/Contents/DemoMTLTextureViewController.swift
Original file line number Diff line number Diff line change
@@ -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: _PreviewImageView())

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: _PreviewImageView())

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
}


}
7 changes: 7 additions & 0 deletions Sources/Demo/Contents/TopMenuViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down