I refer to MetalKitEssentials, and make model viewer application by MetalKit
in iOS 9.1
.
I want to make screen shot image(RGBA Format) from MTKView
.
But, I get only black image.
What should I do?
Thank you for reading!
Code:
@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
Self solved.
I found a similar topic.
And I write code for the following points.
1: using Objective-C
2: no using extension 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);
}
But, I get the following exec error in getBytes method.
failed assertion `texture must not be a framebufferOnly texture.'
This error has been fixed in the following fix. So, I change setting for MTKView.
metalView.framebufferOnly = false;
Thanks.
Refer : https://stackoverflow.com/a/33903182/767280.
For 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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With