Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
968 views
in Technique[技术] by (71.8m points)

macos - How can I get screenshot from all displays on MAC?

I try to get the screenshot from all monitors connected with my MAC to one picture. I know, how I can do this if every monitor's screenshot will saved to different pictures, but it is not what I want. I found function CGGetDisplaysWithRect, but my solution don't work, because output picture is empty. I expect, that problem with function CGDisplayCreateImageForRect (*displays, rect), because first parameter must be CGDirectDisplayID type, but not CGDirectDisplayID*. But I can't find function, which can create one picture with some CGDirectDisplayID objects.
Help me please!!!

#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
", 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: I tried smth that me told below, but now I get error:

Error: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 3-component color space; kCGImageAlphaNone; 3456 bytes/row.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Here's some code that should do it. On the one hand, I wasn't able to test on a multi-monitor system yet, but, on the other, the code was written without any assumptions about which display to use or where it is positioned. So, it should work.

    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];

The main possible point of failure is in allocating a bitmap image context for a rectangle large enough to encompass all displays. Note that the total rect for all displays can be much larger than the rect for any one. For example, if two monitors are arranged so that they barely touch at a corner, the rectangle encompassing them would be nearly as big as four monitors in a 2x2 arrangement. For three monitors, it can be as big as 9 monitors in a 3x3 arrangement. Etc.


Here's an implementation that doesn't use Cocoa, just 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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share

2.1m questions

2.1m answers

63 comments

56.6k users

...