2015-01-29 4 views
0

Я пытаюсь получить снимок экрана со всех мониторов, связанных с моим MAC, на одну фотографию. Я знаю, как я могу это сделать, если скриншот каждого монитора будет сохранен на разные снимки, но это не то, что я хочу. Я нашел функцию CGGetDisplaysWithRect, но мое решение не работает, потому что выходное изображение пуст. Я ожидаю, что проблема с функцией CGDisplayCreateImageForRect (* показывает, rect), потому что первый параметр должен быть CGDirectDisplayID, но не CGDirectDisplayID *. Но я не могу найти функцию, которая может создать одно изображение с некоторыми объектами CGDirectDisplayID.
Помогите мне пожалуйста !!!Как я могу получить снимок экрана со всех дисплеев на MAC?

#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: Я пытался что-л я сказал ниже, но теперь я получаю ошибку:

Ошибки: CGBitmapContextCreate: неподдерживаемую комбинацию параметров: 8 целочисленных бит/компонент; 24 бит/пиксель; 3-компонентное цветовое пространство; kCGImageAlphaNone; 3456 байт/строка.

+0

Действительно ??? Никто не может мне помочь ??? – neo

+0

Функции, скорее всего, помогут вам, если вы проверите возвращаемые значения, как я предложил. –

+1

@MarkSetchell, malloc работает правильно, я проверил его вчера. – neo

ответ

3

Вот код, который должен это сделать. С одной стороны, я не смог протестировать систему с несколькими мониторами, но, с другой стороны, код был написан без каких-либо предположений о том, какой дисплей использовать или где он позиционируется. Таким образом, это должно сработать.

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. Для трех мониторов он может быть как 9 мониторов в схеме 3x3. Etc.


Вот реализация, которая не использует какао, только 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); 
+1

Большое вам спасибо за вашу работу! проверьте этот код на нескольких мониторах!)))) А потом напишите результат здесь! Еще раз спасибо))) – neo

+1

Какие аналоги функция в в CoreFoundation: 1. graphicsContextWithBitmapImageRep 2. saveGraphicsState 3. representationUsingType – neo

+1

Вы можете создать '' CGContext' с помощью CGBitmapContextCreate() '. В этом случае вы не будете использовать 'NSBitmapImageRep' или' NSGraphicsContext'. После того, как вы рисуете отдельные изображения в контексте, вы попросите его создать 'CGImage' с его содержимым, используя' CGBitmapContextCreateImage() '. Затем вы создадите 'CGImageDestination', подобный тому, что вы изначально сделали, и добавьте это изображение. Как я уже отмечал ранее, 'CGImageDestination' должен указывать URL-адрес напрямую, если вы просто собираетесь записать его в файл. Материал Cocoa, который я использовал, в основном просто обертывает те функции Core Graphics. –