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. [self.view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
  10. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  11. UIGraphicsEndImageContext();
  12. NSData * data = UIImagePNGRepresentation(image);
  13. NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  14. NSString *filename = [[path objectAtIndex:0] stringByAppendingPathComponent:@"foo.png"];
  15. [data writeToFile:filename atomically:YES];
  16. }

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

这段代码是抓下了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. UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
  6. CGRect rec = CGRectMake(videoView.frame.origin.x, videoView.frame.origin.y, videoView.bounds.size.width, videoView.bounds.size.height);
  7. [self.view drawViewHierarchyInRect:rec afterScreenUpdates:YES];
  8. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  9. UIGraphicsEndImageContext();
  10. NSData * data = UIImagePNGRepresentation(image);
  11. NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  12. NSString *filename = [[path objectAtIndex:0] stringByAppendingPathComponent:@"foo.png"];
  13. [data writeToFile:filename atomically:YES];
  14. }

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

iOS应用截屏的更多相关文章

  1. IOS上架截屏 屏幕快照

    IOS上架截屏,屏幕快照,4种屏幕尺寸,每种尺寸5张软件功能截图. 大小等于对应设备的屏幕的像素大小.使用模拟器,command +s截图就可以了虚拟机里的手机截屏就保存在mac 桌面上了.jpg,p ...

  2. iOS 模拟器截屏快捷键

    iOS 模拟器截屏快捷键: cmd+S

  3. IOS中截屏的实现,很简易的方法

    // 添加QuartzCore.framework库 #import <QuartzCore/QuartzCore.h> -(void) screenShot { // 截屏 UIGrap ...

  4. iOS手机截屏使用

    .截屏 保存 .data //登录成功进行截屏 //截取屏幕大小 UIGraphicsBeginImageContext([[UIScreen mainScreen]bounds].size); [s ...

  5. iOS实现截屏 并合适保存

     本文转载至:http://blog.csdn.net/zeng11088/article/details/8664510 分类: UIImageView2013-03-12 16:42 122人阅读 ...

  6. iOS 手机截屏

    百度地图自带截图功能,可以截取路线列表,保存到本地.可是对比发现截下来的图片并不是app中看到的那样,截图中头部加入了搜索的起点和终点,每段路程的详细站点都已展开,而且图片会根据路线的长短自动判断图片 ...

  7. iOS屏幕截屏

    #import "ViewController.h" @interface ViewController () @end @implementation ViewControlle ...

  8. ios 代码截屏模糊问题解决办法

    我们常用的截图方法如下所示: //尺寸是按照 UIGraphicsBeginImageContext(CGSizeMake(, )); //currentView 当前的view 创建一个基于位图的图 ...

  9. ios实现截屏(转)

    -(UIImage*) makeImage {  UIGraphicsBeginImageContext(self.view.bounds.size);  [self.view.layer rende ...

随机推荐

  1. R 中的哪些命令或者包让你相见恨晚?--转载知乎

    https://www.zhihu.com/question/24501195 节选: 看了这么多答案,觉得 Hadley Wickhamhad.co.nz 在R使用者的地位好高啊.其实我也觉得Had ...

  2. HTML之实现页面缓存

    一般来说,对于html页面,一个站点,每个页面都会有相同的公共文件,比如页面的头部.尾部.侧边栏目.公共JS等.访问站点下的每一个页面,相同的公共文件,都需要重复从服务器下载.从性能和带宽角度看,重复 ...

  3. 浅谈java中源码常见的几个关键字(native,strictfp,transient,volatile)

    最近看源码总发现一些没见过的关键字,今天就来整理一下native,strictfp,transient,volatile native 本地 native是与C++联合开发的时候用的!java自己开发 ...

  4. String写时拷贝实现

    头文件部分 1 /* 版权信息:狼 文件名称:String.h 文件标识: 摘 要:对于上版本简易的String进行优化跟进. 改进 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需 ...

  5. C++:tinyxml的使用

    1. 简介 TinyXML2(最新版本)是一个开源的功能齐全的XML解析库 For C++,源码见:github. 2. 开始使用 首先从Github上获得源码,是一个完整的演示工程,我们只需要其中的 ...

  6. TCP/IP网路协议复习

    1.OSI (Open System Interconnect Protocol) 开放互联协议,这是一个七层的计算机网络协议,包括:物理层.数据链路层.网络层.传输层.回话层.表示层.应用层.    ...

  7. Prism 4 文档 ---第10章 Silverlight和WPF之间共享代码

        本主题帮助你理解来自Prism的多目标和它的优缺点.多目标的代码针对两个不同的平台有大致相同的代码库.这允许同时保持代码尽可能多一样能够产生针对两种不同技术的二进制文件.在这种情况下,本节介绍 ...

  8. LeetCode OJ:Reorder List(重序链表)

    Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do thi ...

  9. va_start、va_arg、va_end、va_copy 可变参函数

    1.应用与原理         在C语言中,有时我们无法给出一个函数参数的列表,比如: int printf(const char *format, ...); int fprintf(FILE *s ...

  10. mysql数据库的笔记

    增删改查置顶: 插入数据: 基本语法 : insert into [表名](字段名1,字段名2……) values(记录1),(记录2): insert into [表名] values(记录1),( ...