我试过了:
let scale = UIScreen.mainScreen().scale
UIGraphicsBeginImageContextWithOptions(metalLayer.bounds.size,false,scale)
// metalLayer.renderInContext(UIGraphicsGetCurrentContext()!)
// self.view.layer ...
metalLayer.presentationLayer()!.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
但结果是一个空截图。任何帮助都会很好!
请记住,我想拍摄CAMetalLayer的快照
答案 0 :(得分:7)
要制作屏幕截图,您需要获得帧缓冲区MTLTexture
。
<强> 1。如果您使用MTKView
:
let texture = view.currentDrawable!.texture
<强> 2。如果您不使用MTKView
这就是我要做的事情 - 我会有一个属性,其中包含最后一个可绘制的属性:
let lastDrawableDisplayed: CAMetalDrawable?
然后当你将drawable呈现给屏幕时,我会更新它:
let commandBuffer = commandQueue.commandBuffer()
commandBuffer.addCompletedHandler({
(buffer:MTLCommandBuffer!) -> Void in
self.lastDrawableDisplayed = drawable
})
现在,无论何时需要制作屏幕截图,都可以获得如下纹理:
let texture = lastDrawableDisplayed.texture
好的,现在当你有MTLTexture
时,你可以将其转换为CGImage
,然后转换为UIImage
或NSImage
。
以下是OS X playground 的代码(MetalKit.MTLTextureLoader不适用于iOS游乐场),我将MTLTexture
转换为CGImage
我为MTLTexture
做了一个小扩展。
import Metal
import MetalKit
import Cocoa
let device = MTLCreateSystemDefaultDevice()!
let textureLoader = MTKTextureLoader(device: device)
// PATH TO YOUR IMAGE FILE
let path = "/Users/haawa799/Desktop/Metal_Snapshot.playground/Resources/q.jpg"
let data = NSData(contentsOfFile: path)!
let texture = try! textureLoader.newTextureWithData(data, options: nil)
extension MTLTexture {
func bytes() -> UnsafeMutablePointer<Void> {
let width = self.width
let height = self.height
let rowBytes = self.width * 4
let p = malloc(width * height * 4)
self.getBytes(p, bytesPerRow: rowBytes, fromRegion: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)
return p
}
func toImage() -> CGImage? {
let p = bytes()
let pColorSpace = CGColorSpaceCreateDeviceRGB()
let rawBitmapInfo = CGImageAlphaInfo.NoneSkipFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue
let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)
let selftureSize = self.width * self.height * 4
let rowBytes = self.width * 4
let provider = CGDataProviderCreateWithData(nil, p, selftureSize, nil)
let cgImageRef = CGImageCreate(self.width, self.height, 8, 32, rowBytes, pColorSpace, bitmapInfo, provider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
return cgImageRef
}
}
if let imageRef = texture.toImage() {
let image = NSImage(CGImage: imageRef, size: NSSize(width: texture.width, height: texture.height))
答案 1 :(得分:2)
对于swift 4.0, 只需转换haawa提供的代码
let lastDrawableDisplayed = metalView?.currentDrawable?.texture
if let imageRef = lastDrawableDisplayed?.toImage() {
let uiImage:UIImage = UIImage.init(cgImage: imageRef)
}
extension MTLTexture {
func bytes() -> UnsafeMutableRawPointer {
let width = self.width
let height = self.height
let rowBytes = self.width * 4
let p = malloc(width * height * 4)
self.getBytes(p!, bytesPerRow: rowBytes, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)
return p!
}
func toImage() -> CGImage? {
let p = bytes()
let pColorSpace = CGColorSpaceCreateDeviceRGB()
let rawBitmapInfo = CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)
let selftureSize = self.width * self.height * 4
let rowBytes = self.width * 4
let releaseMaskImagePixelData: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
return
}
let provider = CGDataProvider(dataInfo: nil, data: p, size: selftureSize, releaseData: releaseMaskImagePixelData)
let cgImageRef = CGImage(width: self.width, height: self.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: rowBytes, space: pColorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)!
return cgImageRef
}
}
答案 2 :(得分:0)
我无法在iPhone 6s上使用XCode 9.1在Swift 4 / Metal 2中使用已接受的答案。因此,我使用了稍微不同的方法,假设lastDrawableDisplayed
被保存,如接受的答案中所述。快速而肮脏,没有任何异常处理:
let context = CIContext()
let texture = self.lastDrawableDisplayed!.texture
let cImg = CIImage(mtlTexture: texture, options: nil)!
let cgImg = context.createCGImage(cImg, from: cImg.extent)!
let uiImg = UIImage(cgImage: cgImg)
这是基于使用的CIImage Initializer:
的文档
init(mtlTexture:options:)
使用Metal纹理提供的数据初始化图像对象。
和CIImage Processing,其中介绍了如何使用CGImage
创建CIContext
:
CIContext()
创建[s] CIContext对象(使用默认选项)[...]context.createCGImage
将输出图像渲染为可以显示或保存到的Core Graphics图像文件。
希望对使用Swift 4的任何人都有帮助。
编辑:此外,我的项目中有多个重叠CAMetalLayer
,并希望将它们合并为一个UIImage
。因此,需要引用每个层的最后一个CAMetalDrawable
对象。在添加新图层(因此用作nextDrawable()
的提供者)之前,我只需将lastDrawableDisplayed
添加到数组[CAMetalDrawable]
即可。当“导出”图层时,我只需将所有UII图像随后写入基于位图的图形上下文,并使用UIGraphicsGetImageFromCurrentImageContext()
获取最终图像。
编辑:如果您在定位方面遇到问题,请尝试以下操作:
let uiImg = UIImage(cgImage: cgImg, scale: 1.0, orientation: UIImageOrientation.downMirrored)
答案 3 :(得分:0)
迅速4.2
扩展名MTLTexture {
func bytes() -> UnsafeMutableRawPointer {
let width = self.width
let height = self.height
let rowBytes = self.width * 4
let p = malloc(width * height * 4)
self.getBytes(p!, bytesPerRow: rowBytes, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)
return p!
}
func toImage() -> CGImage? {
let p = bytes()
let pColorSpace = CGColorSpaceCreateDeviceRGB()
let rawBitmapInfo = CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)
let selftureSize = self.width * self.height * 4
let rowBytes = self.width * 4
let releaseMaskImagePixelData: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
return
}
let provider = CGDataProvider(dataInfo: nil, data: p, size: selftureSize, releaseData: releaseMaskImagePixelData)
let cgImageRef = CGImage(width: self.width, height: self.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: rowBytes, space: pColorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)!
return cgImageRef
}
}