代码之家  ›  专栏  ›  技术社区  ›  neo

如何从MAC上的所有显示中获取屏幕截图?

  •  1
  • neo  · 技术社区  · 10 年前

    我试图将所有与我的MAC连接的监视器的屏幕截图转换为一张图片。我知道,如果每个监视器的屏幕截图都保存为不同的图片,我该怎么做,但这不是我想要的。我找到了功能 带矩形的CGGetDisplays ,但我的解决方案不起作用,因为输出图片为空。我预计,函数CGDisplayCreateImageForRect(*displays,rect)会出现问题,因为第一个参数必须是CGDirectDisplayID类型,而不是CGDirectDisplayID*。但我找不到可以用一些CGDirectDisplayID对象创建一张图片的函数。
    请帮帮我!!!

    #include <stdio.h>
    #include <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[])
    {
        CGDisplayCount displayCount;
        CGDirectDisplayID displays[32];
        memset(&displays, 0, 32);
        CGImageRef image[32];
        CGRect rect = CGRectNull;
    
        //grab the active displays
        if (CGGetActiveDisplayList(32, displays, &displayCount) != kCGErrorSuccess)
        {
            printf("Error occured: %s\n", strerror(errno));
        }
    
        //go through the list
        for (int i = 0; i < displayCount; i++)
        {
            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
            {
                continue;
            }
            //return the smallest rectangle wich contain the two source rectangles
            rect = CGRectUnion(rect, CGDisplayBounds(displays[i]));
            if (CGRectIsNull(rect))
            {
                printf("Error: %s", strerror(errno));
            }
        }
    
        CGFloat whitePoint[3];
        CGFloat blackPoint[3];
        CGFloat gamma[3];
        CGFloat matrix[9];
    
        CGColorSpaceRef colorSpace = CGColorSpaceCreateCalibratedRGB (&whitePoint[3], &blackPoint[3], &gamma[3], &matrix[9] );
        if(colorSpace == NULL)
        {
            printf("Error: %s", strerror(errno));
        }
    
        //CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    
        //Create bmp context for image
        CGContextRef context = CGBitmapContextCreate(NULL,                      //data
                                              CGRectGetWidth(rect),         //width
                                              CGRectGetHeight(rect),        //height
                                              8,                            //bitPerComponent, for RGB must be 8
                                              0,                            //if data == NULL, it must be 0
                                              colorSpace,                   //colorspace device independent
                                              kCGBitmapByteOrderDefault );  //bitmap info
        if(context == NULL)
        {
            printf("Error: %s", strerror(errno));
        }
    
        //Create a snapshot image
        for (int i = 0; i < displayCount; i++)
        {
            image[i] = CGBitmapContextCreateImage(context);
            if(image == NULL)
            {
                //printf("Error: %s", strerror(errno));
            }
        }
    
        //Create destination to image
        CFURLRef url = CFURLCreateWithString ( kCFAllocatorDefault, CFSTR("out.bmp"), NULL);
        if(url == NULL)
        {
            printf("Error: %s", strerror(errno));
        }
        CFErrorRef *error = NULL;
        CFURLRef urlToFile = CFURLCreateFilePathURL ( kCFAllocatorDefault, url, error );
        if(urlToFile == NULL)
        {
            //printf("Error: %s", error);
        }
    
        CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL(urlToFile, kUTTypeBMP, displayCount, NULL);
        if(imageDestination == NULL)
        {
            printf("Error: %s", strerror(errno));
        }
    
        //CGImageDestinationAddImage(imageDestination, image, NULL);
        CGImageDestinationFinalize(imageDestination);
    
        CGContextRelease(context);
        CGColorSpaceRelease(colorSpace);
        CFRelease(imageDestination);
        return 0;
    } 
    

    APDATE:我尝试了下面告诉我的smth,但现在我得到了错误:

    错误:CGBitmapContextCreate:不支持的参数组合:8个整数位/组件;24位/像素;三分量颜色空间;kCGImageAlphaNone;3456字节/行。

    1 回复  |  直到 10 年前
        1
  •  5
  •   Ken Thomases    9 年前

    这里有一些代码可以做到这一点。一方面,我还无法在多监视器系统上进行测试,但另一方面,编写代码时没有对使用哪种显示器或其位置进行任何假设。所以,它应该起作用。

        CGDirectDisplayID displays[32];
        uint32_t count;
        if (CGGetActiveDisplayList(sizeof(displays)/sizeof(displays[0]), displays, &count) != kCGErrorSuccess)
        {
            NSLog(@"failed to get display list");
            exit(EXIT_FAILURE);
        }
    
        CGRect rect = CGRectNull;
        CGRect primaryDisplayRect = CGRectZero;
        for (uint32_t i = 0; i < count; i++)
        {
            // if display is secondary mirror of another display, skip it
            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
                continue;
    
            CGRect displayRect = CGDisplayBounds(displays[i]);
            if (i == 0)
                primaryDisplayRect = displayRect;
            displayRect.origin.y = CGRectGetMaxY(primaryDisplayRect) - CGRectGetMaxY(displayRect);
            rect = CGRectUnion(rect, displayRect);
        }
    
        NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
                                                                             pixelsWide:CGRectGetWidth(rect)
                                                                             pixelsHigh:CGRectGetHeight(rect)
                                                                          bitsPerSample:8
                                                                        samplesPerPixel:4
                                                                               hasAlpha:YES
                                                                               isPlanar:NO
                                                                         colorSpaceName:NSCalibratedRGBColorSpace
                                                                           bitmapFormat:0
                                                                            bytesPerRow:0
                                                                           bitsPerPixel:32];
        if (!imageRep)
        {
            NSLog(@"failed to create bitmap image rep");
            exit(EXIT_FAILURE);
        }
    
        NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
        if (!context)
        {
            NSLog(@"failed to create graphics context");
            exit(EXIT_FAILURE);
        }
    
        [NSGraphicsContext saveGraphicsState];
        {
            [NSGraphicsContext setCurrentContext:context];
            CGContextRef cgcontext = [context graphicsPort];
    
            CGContextClearRect(cgcontext, CGRectMake(0, 0, CGRectGetWidth(rect), CGRectGetHeight(rect)));
    
            for (uint32_t i = 0; i < count; i++)
            {
                // if display is secondary mirror of another display, skip it
                if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
                    continue;
    
                CGRect displayRect = CGDisplayBounds(displays[i]);
                displayRect.origin.y = CGRectGetMaxY(primaryDisplayRect) - CGRectGetMaxY(displayRect);
                CGImageRef image = CGDisplayCreateImage(displays[i]);
                if (!image)
                    continue;
    
                CGRect dest = CGRectMake(displayRect.origin.x - rect.origin.x,
                                         displayRect.origin.y - rect.origin.y,
                                         displayRect.size.width,
                                         displayRect.size.height);
                CGContextDrawImage(cgcontext, dest, image);
                CGImageRelease(image);
            }
    
            [[NSGraphicsContext currentContext] flushGraphics];
        }
        [NSGraphicsContext restoreGraphicsState];
    
    
        NSData* data = [imageRep representationUsingType:NSPNGFileType properties:@{ }];
        [data writeToFile:@"/tmp/screenshot.png" atomically:YES];
    

    主要可能的失败点是为足够大的矩形分配位图图像上下文以包含所有显示。注意,所有显示的总矩形可以比任何一个显示的矩形大得多。例如,如果两个显示器的排列方式使它们几乎不接触角落,那么围绕它们的矩形将几乎与2x2排列方式中的四个显示器一样大。对于三个显示器,它可以在3x3的排列中多达9个显示器。等


    这里有一个不使用Cocoa的实现,只使用Core Graphics:

        CGDirectDisplayID displays[32];
        uint32_t count;
        if (CGGetActiveDisplayList(sizeof(displays)/sizeof(displays[0]), displays, &count) != kCGErrorSuccess)
        {
            NSLog(@"failed to get display list");
            exit(EXIT_FAILURE);
        }
    
        CGRect rect = CGRectNull;
        for (uint32_t i = 0; i < count; i++)
        {
            // if display is secondary mirror of another display, skip it
            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
                continue;
    
            rect = CGRectUnion(rect, CGDisplayBounds(displays[i]));
        }
    
        CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
        if (!colorspace)
        {
            NSLog(@"failed to create colorspace");
            exit(EXIT_FAILURE);
        }
    
        CGContextRef cgcontext = CGBitmapContextCreate(NULL, CGRectGetWidth(rect), CGRectGetHeight(rect), 8, 0, colorspace, (CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
        CGColorSpaceRelease(colorspace);
        if (!cgcontext)
        {
            NSLog(@"failed to create bitmap context");
            exit(EXIT_FAILURE);
        }
    
        CGContextClearRect(cgcontext, CGRectMake(0, 0, CGRectGetWidth(rect), CGRectGetHeight(rect)));
    
        for (uint32_t i = 0; i < count; i++)
        {
            // if display is secondary mirror of another display, skip it
            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
                continue;
    
            CGRect displayRect = CGDisplayBounds(displays[i]);
            CGImageRef image = CGDisplayCreateImage(displays[i]);
            if (!image)
                continue;
    
            CGRect dest = CGRectMake(displayRect.origin.x - rect.origin.x,
                                     displayRect.origin.y - rect.origin.y,
                                     displayRect.size.width,
                                     displayRect.size.height);
            CGContextDrawImage(cgcontext, dest, image);
            CGImageRelease(image);
        }
    
        CGImageRef image = CGBitmapContextCreateImage(cgcontext);
        CGContextRelease(cgcontext);
        if (!image)
        {
            NSLog(@"failed to create image from bitmap context");
            exit(EXIT_FAILURE);
        }
    
        CFURLRef url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/tmp/screenshot.png"), kCFURLPOSIXPathStyle, NO);
        if (!url)
        {
            NSLog(@"failed to create URL");
            exit(EXIT_FAILURE);
        }
    
        CGImageDestinationRef dest = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
        CFRelease(url);
        if (!dest)
        {
            NSLog(@"failed to create image destination");
            exit(EXIT_FAILURE);
        }
    
        CGImageDestinationAddImage(dest, image, NULL);
        CGImageRelease(image);
        if (!CGImageDestinationFinalize(dest))
        {
            NSLog(@"failed to finalize image destination");
            exit(EXIT_FAILURE);
        }
        CFRelease(dest);