如何在 iOS 中从 MTKView 制作屏幕截图?
Posted
技术标签:
【中文标题】如何在 iOS 中从 MTKView 制作屏幕截图?【英文标题】:How can I make screen shot image from MTKView in iOS? 【发布时间】:2016-01-31 15:33:07 【问题描述】:我参考MetalKitEssentials,并通过MetalKit
在ios 9.1
中制作模型查看器应用程序。
我想从MTKView
制作屏幕截图(RGBA 格式)。
但是,我只得到黑色图像。
我该怎么办?
感谢您的阅读!
代码:
@implemtntation MetalViewController
MTKView *metalView;
- (void)viewDidLoad
[super viewDidload];
[self setup];
- (void)setup
metalDevice = MTLCreateSystemDefaultDevice();
commandQueue = [metalDevice newCommandQueue];
defaultLibrary = [metalDevice newDefaultLibrary];
metalView = [[MTKView alloc] initWithFrame:self.view.frame];
[self.view addSubview:metalView];
metalView.delegate = self;
metalView.device = metalDevice;
metalView.sampleCount = 4;
metalView.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
metalView.opaque = false;
metalView.framebufferOnly = true;
// and more set up for Metal ...
// if you make screen shot, call this method.
- (IBAction)onCapture:(id)sender
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContextWithOptions(screenRect.size, YES, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextFillRect(context, screenRect);
// draw image to context
[metalView.layer renderInContext:context];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(image, self,
@selector(completeSavedImage:didFinishSavingWithError:contextInfo:), nil);
- (void)completeSavedImage:(UIImage *)_image didFinishSavingWithError:(NSError *)_error contextInfo:(void *)_contextInfo
if (!_error)
NSLog(@"ok");
else
NSLog(@"error");
@end
【问题讨论】:
【参考方案1】:自行解决。
我找到了一个类似的topic。
我为以下几点编写代码。
1:使用 Objective-C
2:不使用扩展MTLTexture
- (IBAction)onCapture:(id)sender
id<MTLTexture> lastDrawableDisplayed = [metalView.currentDrawable texture];
int width = (int)[lastDrawableDisplayed width];
int height = (int)[lastDrawableDisplayed height];
int rowBytes = width * 4;
int selfturesize = width * height * 4;
void *p = malloc(selfturesize);
[lastDrawableDisplayed getBytes:p bytesPerRow:rowBytes fromRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
CGDataProviderRef provider = CGDataProviderCreateWithData(nil, p, selfturesize, nil);
CGImageRef cgImageRef = CGImageCreate(width, height, 8, 32, rowBytes, colorSpace, bitmapInfo, provider, nil, true, (CGColorRenderingIntent)kCGRenderingIntentDefault);
UIImage *getImage = [UIImage imageWithCGImage:cgImageRef];
CFRelease(cgImageRef);
free(p);
NSData *pngData = UIImagePNGRepresentation(getImage);
UIImage *pngImage = [UIImage imageWithData:pngData];
UIImageWriteToSavedPhotosAlbum(pngImage, self,
@selector(completeSavedImage:didFinishSavingWithError:contextInfo:), nil);
但是,我在 getBytes 方法中收到以下 exec 错误。
failed assertion `texture must not be a framebufferOnly texture.'
此错误已在以下修复中得到修复。 所以,我改变了 MTKView 的设置。
metalView.framebufferOnly = false;
谢谢。
【讨论】:
【参考方案2】:参考:https://***.com/a/33903182/767280.
对于 swift 4.0,
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
【讨论】:
【参考方案3】:self.mtkView.framebufferOnly = 否;
- (UIImage *)imageWithCurrentTexture:(MTKView *)mtkView
CIContext * context = [CIContext contextWithMTLDevice:mtkView.device];
CIImage *outputImage = [[CIImage alloc] initWithMTLTexture:mtkView.currentDrawable.texture options:@kCIImageColorSpace:(__bridge_transfer id)CGColorSpaceCreateDeviceRGB()];
CGImageRef cgImg = [context createCGImage:outputImage fromRect:CGRectMake(0, 0, [UIScreen mainScreen].nativeBounds.size.width, [UIScreen mainScreen].nativeBounds.size.height)];
UIImage *resultImg = [UIImage imageWithCGImage:cgImg scale:1.0 orientation:UIImageOrientationDownMirrored];
CGImageRelease(cgImg);
return resultImg;
【讨论】:
以上是关于如何在 iOS 中从 MTKView 制作屏幕截图?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Quickblox iOS 中从远程视频中捕获视频帧
在 iOS 中从 xcdatamodel 访问 FetchRequest