How can I make screen shot image from MTKView in iOS?

微笑、不失礼 提交于 2020-01-14 13:51:08

问题


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

回答1:


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.




回答2:


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
    }
}



回答3:


self.mtkView.framebufferOnly = NO;

- (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;
}


来源:https://stackoverflow.com/questions/35115605/how-can-i-make-screen-shot-image-from-mtkview-in-ios

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!