]>
Commit | Line | Data |
---|---|---|
759271f8 TC |
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 | } |