zoukankan      html  css  js  c++  java
  • 截屏

    iOS中正确的截屏姿势

    昨天写了个用到截屏功能的插件,结果问题不断,今天终于解决好了,把debug过程中所有尝试过的截屏方法都贴出来吧~

    第一种

    这是iOS 3时代开始就被使用的方法,它被废止于iOS 7。iOS的私有方法,效率很高。

    #import
    extern "C" CGImageRef UIGetScreenImage();
    UIImage * screenshot(void) NS_DEPRECATED_IOS(3_0,7_0);
    UIImage * screenshot(){
        UIImage *image = [UIImage imageWithCGImage:UIGetScreenImage()];
        return image;
    }

    第二种

    这是在比较常见的截图方法,不过不支持Retina屏幕。

    UIImage * screenshot(UIView *);
    UIImage * screenshot(UIView *view){
      UIGraphicsBeginImageContext(view.frame.size);
      [view.layer renderInContext:UIGraphicsGetCurrentContext()];
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      return image;
    }
    

    第三种

    从iPhone 4、iPod Touch 4开始,Apple逐渐采用Retina屏幕,于是在iOS 4的SDK中我们有了,上面的截图方法也自然变成了这样。

    UIImage * screenshot(UIView *) NS_AVAILABLE_IOS(4_0);
    UIImage * screenshot(UIView *view){
      if(UIGraphicsBeginImageContextWithOptions != NULL)
      {
        UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
      } else {
        UIGraphicsBeginImageContext(view.frame.size);
      }
      [view.layer renderInContext:UIGraphicsGetCurrentContext()];
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      return image;
    }
    

    第四种

    或许你会说有时Hook的是一个按钮的方法,用第三个方法的话,根本找不到view来传值,不过还好,iOS 7又提供了一些UIScreen的API。

    UIImage * screenshot(void) NS_AVAILABLE_IOS(7_0);
    UIImage * screenshot(){
      UIView * view = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:YES];
      if(UIGraphicsBeginImageContextWithOptions != NULL)
      {
        UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
      } else {
        UIGraphicsBeginImageContext(view.frame.size);
      }
      [view.layer renderInContext:UIGraphicsGetCurrentContext()];
      UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
      return image;
    }
    

    第五种

    @interface SBScreenShotter : NSObject
    + (id)sharedInstance;
    - (void)saveScreenshot:(_Bool)arg1;
    @end

    然后直接

    [[SBScreenShotter sharedInstance] saveScreenshot:YES];

    一道白光之后,咱们就模拟了用户截屏的动作,不过这个方法在只需要截屏时比较好,如果要对屏幕录像(其实就是不断截图)的话,那不得闪瞎了。。 而且我们也拿不到UIImage的实例去拼成一个视频呀。即使通过Hook别的类拿到UIImage的实例,这个私有API的效率大概也是达不到 30FPS的视频要求的。

    那么现在我们有5种方法了,第一种是私有API,私有API通常效率和质量都比Documented API的好,可是它在iOS 7以后就被废除了啊,就没有别的了吗?

    答案当然是————有的!用Private Framework来完成这项任务!直接走底层拿屏幕的缓冲数据,然后生成UIImage的实例。

    第六种

    #import #import #import #import #import extern "C" IOReturn IOSurfaceLock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed);
    extern "C" IOReturn IOSurfaceUnlock(IOSurfaceRef buffer, uint32_t options, uint32_t *seed);
    extern "C" size_t IOSurfaceGetWidth(IOSurfaceRef buffer);
    extern "C" size_t IOSurfaceGetHeight(IOSurfaceRef buffer);
    extern "C" IOSurfaceRef IOSurfaceCreate(CFDictionaryRef properties);
    extern "C" void *IOSurfaceGetBaseAddress(IOSurfaceRef buffer);
    extern "C" size_t IOSurfaceGetBytesPerRow(IOSurfaceRef buffer);
    extern const CFStringRef kIOSurfaceAllocSize;
    extern const CFStringRef kIOSurfaceWidth;
    extern const CFStringRef kIOSurfaceHeight;
    extern const CFStringRef kIOSurfaceIsGlobal;
    extern const CFStringRef kIOSurfaceBytesPerRow;
    extern const CFStringRef kIOSurfaceBytesPerElement;
    extern const CFStringRef kIOSurfacePixelFormat;
    enum
    {
      kIOSurfaceLockReadOnly  =0x00000001,
      kIOSurfaceLockAvoidSync =0x00000002
    };
    UIImage * screenshot(void);
    UIImage * screenshot(){
      IOMobileFramebufferConnection connect;
      kern_return_t result;
    CoreSurfaceBufferRef screenSurface = NULL;
      io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("AppleH1CLCD"));
    if(!framebufferService)
        framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("AppleM2CLCD"));
    if(!framebufferService)
        framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault,IOServiceMatching("AppleCLCD"));
    result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);
    result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface);
      uint32_t aseed;
      IOSurfaceLock((IOSurfaceRef)screenSurface, 0x00000001, &aseed);
      size_t width = IOSurfaceGetWidth((IOSurfaceRef)screenSurface);
      size_t height = IOSurfaceGetHeight((IOSurfaceRef)screenSurface);
      CFMutableDictionaryRef dict;
    size_t pitch = width*4, size = width*height*4;
      int bPE=4;
      char pixelFormat[4] = {'A','R','G','B'};
      dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
      CFDictionarySetValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue);
      CFDictionarySetValue(dict, kIOSurfaceBytesPerRow, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &pitch));
      CFDictionarySetValue(dict, kIOSurfaceBytesPerElement, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &bPE));
      CFDictionarySetValue(dict, kIOSurfaceWidth, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &width));
      CFDictionarySetValue(dict, kIOSurfaceHeight, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &height));
      CFDictionarySetValue(dict, kIOSurfacePixelFormat, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, pixelFormat));
      CFDictionarySetValue(dict, kIOSurfaceAllocSize, CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type, &size));
      IOSurfaceRef destSurf = IOSurfaceCreate(dict);
      IOSurfaceAcceleratorRef outAcc;
      IOSurfaceAcceleratorCreate(NULL, 0, &outAcc);
      IOSurfaceAcceleratorTransferSurface(outAcc, (IOSurfaceRef)screenSurface, destSurf, dict,NULL);
      IOSurfaceUnlock((IOSurfaceRef)screenSurface, kIOSurfaceLockReadOnly, &aseed);
    CFRelease(outAcc);
      CGDataProviderRef provider =  CGDataProviderCreateWithData(NULL,  IOSurfaceGetBaseAddress(destSurf), (width * height * 4), NULL);
      CGImageRef cgImage = CGImageCreate(width, height, 8,
    8*4, IOSurfaceGetBytesPerRow(destSurf),
     CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst |kCGBitmapByteOrder32Little,provider, NULL, YES, kCGRenderingIntentDefault);
      UIImage *image = [UIImage imageWithCGImage:cgImage];
      return image;
    }
    

    需要注意的是,第五种方法需要修改一下IOMobileFramebuffer的头文件。

    typedef void * IOMobileFramebufferConnection;

    In the reversed header, IOMobileFramebufferConnection is typedef'd to io_connect_t, which is typedef'd to io_object_t, which is mach_port_t, which is __darwin_mach_port_t, which is __darwin_mach_port_name_t, which is __darwin_natural_t, which is unsigned int! Int just happens to be pointer-sized on 32-bit, but is not under 64-bit。

    —— StackoverFlow

    修改好的头文件 顺便也丢上来吧,解压后放在Project的根目录下。

    如果你使用的是theos的话,记得在Makefile里写上,

    YOUR_TWEAK_NAME_PRIVATE_FRAMEWORKS = IOSurface IOKit IOMobileFramebuffer

    YOUR_TWEAK_NAME_CFLAGS = -I./headers/ -I./headers/IOSurface

    如果是XCode上的Logos Tweak的话,在Build Settings -> Search Paths -> Header Search Paths里面添加一项:$(PROJECT_DIR)/YOUR_PROJECT_NAME/headers, 搜索方式为recursive. 最后在Build Phases里Link上IOSurface IOKit IOMobileFramebuffer这三个私有Framework。

  • 相关阅读:
    一秒解决element容器布局显示内部调节框的问题
    操作管理员
    Linux_网络基础管理
    如何解决在WordPress安装Redis插件时需要输入FTP问题?
    如何在我的EC2实例状态更改时获取自定义电子邮件通知
    利用S3fs在Amazon EC2 Linux实例上挂载S3存储桶
    源码安装Apache(httpd)
    Linux_源码安装包管理理论概述
    Linux_yum命令详解
    Linux_yum仓库管理
  • 原文地址:https://www.cnblogs.com/bugismyalllife/p/4818313.html
Copyright © 2011-2022 走看看