• iOS应用截屏


     iPhone自从推出后就自带了截屏功能,简单而易用,所以应用就没什么截屏的需求了,不过有些时候我们还是会遇到这个需求。比如,我们开发了一个播放器,用openGL进行video render,此时直接截屏有可能有OSD叠加内容,所以希望能截完全是视频的帧,这时就需要应用自己来实现了。

          从应用角度看,虽说都是截屏,但用不用openGL是不同的,因为openGL是直接写GPU frame buffer的,如果我们是直接用UIController来用做的界面:

    1. - (void)snapshotScreen  
    2. {  
    3.     // check the retina screen  
    4.     if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]){  
    5.         UIGraphicsBeginImageContextWithOptions(self.view.window.bounds.size, NO, [UIScreen mainScreen].scale);  
    6.     } else {  
    7.         UIGraphicsBeginImageContext(self.view.window.bounds.size);  
    8.     }  
    9.   
    10.     [self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];  
    11.     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();  
    12.     UIGraphicsEndImageContext();  
    13.     NSData * data = UIImagePNGRepresentation(image);  
    14.       
    15.     NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    16.     NSString *filename = [[path objectAtIndex:0] stringByAppendingPathComponent:@"foo.png"];  
    17.     [data writeToFile:filename atomically:YES];  
    18. }  


          这个代码前面部分是检查是否是retina屏幕的,因为iPhone都是retina的屏幕,但iPod有非retina的屏幕;最后一部分是把image存到应用的document目录下。

          如果要截openGL的内容,那么上面的代码就不能用了,思路是用openGL的API,glReadPixels去读出内容来。代码如下:

    1. - (void)getGLScreenShot {  
    2.     int       w = self.bounds.size.width;  
    3.     int       h = self.bounds.size.height;  
    4.     NSInteger myDataLength = w * h * 4;  
    5.       
    6.     // allocate array and read pixels into it.  
    7.     GLubyte *buffer = (GLubyte *) malloc(myDataLength);  
    8.     glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);  
    9.       
    10.     // gl renders "upside down" so swap top to bottom into new array.  
    11.     // there's gotta be a better way, but this works.  
    12.     GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);  
    13.     for(int y = 0; y < h; y++) {  
    14.         for(int x = 0; x <w * 4; x++) {  
    15.             buffer2[(h -1 - y) * w * 4 + x] = buffer[y * 44 * w + x];  
    16.         }  
    17.     }  
    18.       
    19.     // make data provider with data.  
    20.     CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);  
    21.       
    22.     // prep the ingredients  
    23.     int bitsPerComponent = 8;  
    24.     int bitsPerPixel = 32;  
    25.     int bytesPerRow = 44 * w;  
    26.     CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();  
    27.     CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;  
    28.     CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;  
    29.       
    30.     // make the cgimage  
    31.     CGImageRef imageRef = CGImageCreate(w, h, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);  
    32.       
    33.     // then make the uiimage from that  
    34.     UIImage *myImage = [UIImage imageWithCGImage:imageRef];  
    35.     UIImageWriteToSavedPhotosAlbum(myImage, self, @selector(GLImage:didFinishSavingWithError:contextInfo:), buffer2);  
    36.     CGImageRelease(imageRef);  
    37.     CGDataProviderRelease(provider);  
    38.     CGColorSpaceRelease(colorSpaceRef);  
    39.     free(buffer);  
    40. }  

          这段代码是抓下了openGL渲染的一个GLView的所有内容,因为openGL读出的内容是颠倒的,所以习惯上需要颠倒一下,中间的两层for循环就是这个用途,当然,这个写法效率不高,不过一时也没找到什么高效的方法,就先用一下。

          这段代码里面释放了buffer的内存,但没有释放buffer2的内存。因为图片的存储是异步的,所以是在存完图片之后,调用@selector(GLImage:didFinishSavingWithError:contextInfo:)这个方法进行清理,通过这个回调,在UI上也可以做一些限制,防止用户连续快速的截屏导致系统负载过重。

          顺便说一下,这里把存下的图片按照习惯放到了图片库之中。

          在iOS7之后,UIView有了UISnapshotting的category,这个真是大大的方便了我们截屏的实现,因为它既可以截屏普通的UIController,也可以截屏openGL的内容,

    1. // only support iOS7 or Above  
    2. - (void)snapshotScreenWithGL  
    3. {  
    4.     CGSize size = videoView.bounds.size;   
    5.       
    6.     UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);  
    7.       
    8.     CGRect rec = CGRectMake(videoView.frame.origin.x, videoView.frame.origin.y, videoView.bounds.size.width, videoView.bounds.size.height);  
    9.     [self.view drawViewHierarchyInRect:rec afterScreenUpdates:YES];  
    10.       
    11.     UIImage *image = UIGraphicsGetImageFromCurrentImageContext();  
    12.     UIGraphicsEndImageContext();  
    13.       
    14.     NSData * data = UIImagePNGRepresentation(image);  
    15.       
    16.     NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    17.     NSString *filename = [[path objectAtIndex:0] stringByAppendingPathComponent:@"foo.png"];  
    18.     [data writeToFile:filename atomically:YES];  
    19. }  

          综合起来看,iOS7之后,苹果考虑到了用户这方面的需求,提供了API,可以比较方便的实现功能。iOS7之前,需要自己手动实现,根据是否使用了openGL代码有所不同。

  • 相关阅读:
    重新理解js的执行环境和闭包
    给开发插上想象力的翅膀
    Vue源码的初始化以及数据驱动逻辑
    解析Vue源码之前
    前端模块化发展介绍和未来展望
    现代前端框架具备的特征分析及Vue、React对比
    始于Flux的单项数据流发展简单介绍
    用面向对象编程解决常见需求场景
    【Docker】之重启容器相关命令
    【Java】之获取CSV文件数据以及获取Excel文件数据
  • 原文地址:https://www.cnblogs.com/geek6/p/4104756.html
Copyright © 2020-2023  润新知