0.014 release
[imager-screenshot.git] / scdarwin2.c
1 /* Darwin support via Quartz Display Services */
2 #include "imext.h"
3 #include "imss.h"
4 #include <ApplicationServices/ApplicationServices.h>
5
6 /* Largely based on:
7
8 http://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-uiimage-cocoa-touch-or-cgimage-core-graphics
9
10 */
11
12 i_img *
13 imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom) {
14   i_clear_error();
15   CGDirectDisplayID disp = CGMainDisplayID();
16   if (!disp) {
17     i_push_error(0, "No main display");
18     return NULL;
19   }
20   
21   /* for now, only interested in the first display */
22   CGRect rect = CGDisplayBounds(disp);
23   i_img_dim screen_width = rect.size.width;
24   i_img_dim screen_height = rect.size.height;
25
26   /* adjust negative/zero values to window size */
27   if (left < 0)
28     left += screen_width;
29   if (top < 0)
30     top += screen_height;
31   if (right <= 0)
32     right += screen_width;
33   if (bottom <= 0)
34     bottom += screen_height;
35   
36   /* clamp */
37   if (left < 0)
38     left = 0;
39   if (right > screen_width)
40     right = screen_width;
41   if (top < 0)
42     top = 0;
43   if (bottom > screen_height)
44     bottom = screen_height;
45
46   /* validate */
47   if (right <= left || bottom <= top) {
48     i_push_error(0, "image would be empty");
49     return NULL;
50   }
51
52   i_img_dim width = right - left;
53   i_img_dim height = bottom - top;
54
55   CGRect cap_rect;
56   cap_rect.origin.x = left;
57   cap_rect.origin.y = top; /* flipped relative to I::S API */
58   cap_rect.size.width = width;
59   cap_rect.size.height = height;
60
61   CGImageRef image = CGDisplayCreateImageForRect(disp, cap_rect);
62   if (!image) {
63     i_push_error(0, "CGDisplayCreateImageForRect failed");
64     return NULL;
65   }
66
67   int channels = CGImageGetAlphaInfo(image) == kCGImageAlphaNone ? 3 : 4;
68   i_img *result = i_img_8_new(width, height, channels);
69   if (!result) {
70     CGImageRelease(image);
71     return NULL;
72   }
73
74   /* bytes per row - round up to the closest 16 byte boundary */
75   size_t bytes_per_row = channels * width;
76   bytes_per_row = (bytes_per_row + 15) & ~15U;
77   
78   unsigned char *data = mymalloc(bytes_per_row * height);
79   CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
80
81   CGContextRef context = CGBitmapContextCreate
82     (data, width, height, 8, bytes_per_row, color_space,
83      kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
84   CGColorSpaceRelease(color_space);
85
86   CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
87   CGContextRelease(context);
88
89   CGImageRelease(image);
90
91   const unsigned char *p = data;
92   i_img_dim y;
93   for (y = 0; y < height; ++y) {
94     i_psamp(result, 0, width, y, p, NULL, channels);
95
96     p += bytes_per_row;
97   }
98
99   i_tags_setn(&result->tags, "ss_window_width", width);
100   i_tags_setn(&result->tags, "ss_window_height", height);
101   i_tags_set(&result->tags, "ss_type", "Darwin", 6);
102   i_tags_set(&result->tags, "ss_variant", "11+", 3);
103   i_tags_setn(&result->tags, "ss_left", left);
104   i_tags_setn(&result->tags, "ss_top", top);
105
106   return result;
107 }