我想捕获iOS的整个屏幕并将其保存到BMP(使用私有api),我首先得到了IOSurfaceRef
,IOMobileFramebufferConnection
然后找到一种方法将表面字节保存到BMP文件中。
我尝试了两种方法,方法是直接screenshot0:
从其中获取字节screenSurface
并将其保存到BMP,但是得到了一个模糊的错位图像;用于screenshot1:
将IOSurfaceAcceleratorTransferSurface
表面字节传输到新IOSurfaceRef
文件并将其保存为 BMP 文件的方法,得到清晰但镜像和 360 度旋转的图像。
我想知道,为什么我不能IOSurfaceRef
直接使用原始字节?中的字节是否IOSurfaceRef
被镜像?如何获得正确的 BMP 屏幕截图?谢谢!
screenshot0:
方法图片:
screenshot1:
方法图片:
- (NSString *)getBmpSavePath:(NSString *)savePath
{
NSString *path = nil;
if (![[[savePath pathExtension] lowercaseString] isEqualToString:@"bmp"]) {
path = [savePath stringByDeletingPathExtension];
path = [path stringByAppendingPathExtension:@"bmp"];
}
return path;
}
- (IOSurfaceRef)getScreenSurface
{
IOSurfaceRef screenSurface = NULL;
io_service_t framebufferService = NULL;
IOMobileFramebufferConnection framebufferConnection = NULL;
framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleH1CLCD"));
if(!framebufferService)
framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleM2CLCD"));
if(!framebufferService)
framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleCLCD"));
if (framebufferService) {
kern_return_t result;
result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &framebufferConnection);
if (result == KERN_SUCCESS) {
IOMobileFramebufferGetLayerDefaultSurface(framebufferConnection, 0, &screenSurface);
}
}
return screenSurface;
}
- (void)screenshot0:(NSString *)savePath
{
IOSurfaceRef screenSurface = [self getScreenSurface];
if (screenSurface) {
IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, NULL);
size_t width = IOSurfaceGetWidth(screenSurface);
size_t height = IOSurfaceGetHeight(screenSurface);
void *bytes = IOSurfaceGetBaseAddress(screenSurface);
NSString *path = [self getBmpSavePath:savePath];
bmp_write(bytes, width, height, [path UTF8String]);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, NULL);
}
}
- (void)screenshot1:(NSString *)savePath
{
IOSurfaceRef screenSurface = [self getScreenSurface];
if (screenSurface) {
IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, NULL);
size_t width = IOSurfaceGetWidth(screenSurface);
size_t height = IOSurfaceGetHeight(screenSurface);
size_t bytesPerElement = IOSurfaceGetBytesPerElement(screenSurface);
OSType pixelFormat = IOSurfaceGetPixelFormat(screenSurface);
size_t bytesPerRow = self.bytesPerElement * self.width;
size_t allocSize = bytesPerRow * self.height;
//============== Why shoud I do this step? Why can't I IOSurfaceGetBaseAddress directly from screenSurface like method screenshot0:???
NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kIOSurfaceIsGlobal,
[NSNumber numberWithUnsignedLong:bytesPerElement], kIOSurfaceBytesPerElement,
[NSNumber numberWithUnsignedLong:bytesPerRow], kIOSurfaceBytesPerRow,
[NSNumber numberWithUnsignedLong:width], kIOSurfaceWidth,
[NSNumber numberWithUnsignedLong:height], kIOSurfaceHeight,
[NSNumber numberWithUnsignedInt:pixelFormat], kIOSurfacePixelFormat,
[NSNumber numberWithUnsignedLong:allocSize], kIOSurfaceAllocSize,
nil];
IOSurfaceRef destSurf = IOSurfaceCreate((__bridge CFDictionaryRef)(properties));
IOSurfaceAcceleratorRef outAcc;
IOSurfaceAcceleratorCreate(NULL, 0, &outAcc);
IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, NULL);
IOSurfaceAcceleratorTransferSurface(outAcc, screenSurface, destSurf, (__bridge CFDictionaryRef)(properties), NULL);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, NULL);
CFRelease(outAcc);
//==============
void *bytes = IOSurfaceGetBaseAddress(destSurf);
NSString *path = [self getBmpSavePath:savePath];
bmp_write(bytes, width, height, [path UTF8String]);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, NULL);
}
}
int bmp_write(const void *image, size_t xsize, size_t ysize, const char *filename)
{
unsigned char header[54] = {
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 32, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
long file_size = (long)xsize * (long)ysize * 4 + 54;
header[2] = (unsigned char)(file_size &0x000000ff);
header[3] = (file_size >> 8) & 0x000000ff;
header[4] = (file_size >> 16) & 0x000000ff;
header[5] = (file_size >> 24) & 0x000000ff;
long width = xsize;
header[18] = width & 0x000000ff;
header[19] = (width >> 8) &0x000000ff;
header[20] = (width >> 16) &0x000000ff;
header[21] = (width >> 24) &0x000000ff;
long height = ysize;
header[22] = height &0x000000ff;
header[23] = (height >> 8) &0x000000ff;
header[24] = (height >> 16) &0x000000ff;
header[25] = (height >> 24) &0x000000ff;
char fname_bmp[128];
sprintf(fname_bmp, "%s", filename);
FILE *fp;
if (!(fp = fopen(fname_bmp, "wb")))
return -1;
fwrite(header, sizeof(unsigned char), 54, fp);
fwrite(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 4, fp);
fclose(fp);
return 0;
}