add support for OS X Lion, which broke the old Darwin support
authorTony Cook <tony@develop-help.com>
Thu, 8 Mar 2012 09:02:47 +0000 (20:02 +1100)
committerTony Cook <tony@develop-help.com>
Thu, 8 Mar 2012 10:06:42 +0000 (21:06 +1100)
Makefile.PL
scdarwin.c
scdarwin2.c [new file with mode: 0644]
t/50darwin.t

index 11745f3..65868b3 100644 (file)
@@ -42,12 +42,20 @@ if (find_header('windows.h', "Win32 header")
   print "Found Win32\n";
 }
 
-if ($^O eq "darwin") {
+if ($^O eq "darwin" and my ($rel) = `uname -r` =~ /^([0-9]+)/) {
   # this test is overly simple
-  push @objs, "scdarwin.o";
-  push @cflags, "-DSS_DARWIN";
-  push @lddlflags, qw/-framework OpenGL -framework Cocoa/;
-  print "Found OS X\n";
+  if ($rel < 11) {
+    push @objs, "scdarwin.o";
+    push @cflags, "-DSS_DARWIN";
+    push @lddlflags, qw/-framework OpenGL -framework Cocoa/;
+    print "Found OS X (<11)\n";
+  }
+  else {
+    push @objs, "scdarwin2.o";
+    push @cflags, "-DSS_DARWIN";
+    push @lddlflags, qw/-framework Cocoa/;
+    print "Found OS X (>=11)\n";
+  }
 }
 
 unless (@objs > 1) {
index 5eca0f1..96da761 100644 (file)
@@ -130,6 +130,7 @@ imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom) {
     i_tags_setn(&im->tags, "ss_window_width", width);
     i_tags_setn(&im->tags, "ss_window_height", height);
     i_tags_set(&im->tags, "ss_type", "Darwin", 6);
+    i_tags_set(&im->tags, "ss_variant", "<11", 3);
     i_tags_setn(&im->tags, "ss_left", left);
     i_tags_setn(&im->tags, "ss_top", top);
   }
diff --git a/scdarwin2.c b/scdarwin2.c
new file mode 100644 (file)
index 0000000..950ae98
--- /dev/null
@@ -0,0 +1,107 @@
+/* Darwin support via Quartz Display Services */
+#include "imext.h"
+#include "imss.h"
+#include <ApplicationServices/ApplicationServices.h>
+
+/* Largely based on:
+
+http://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-uiimage-cocoa-touch-or-cgimage-core-graphics
+
+*/
+
+i_img *
+imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom) {
+  i_clear_error();
+  CGDirectDisplayID disp = CGMainDisplayID();
+  if (!disp) {
+    i_push_error(0, "No main display");
+    return NULL;
+  }
+  
+  /* for now, only interested in the first display */
+  CGRect rect = CGDisplayBounds(disp);
+  i_img_dim screen_width = rect.size.width;
+  i_img_dim screen_height = rect.size.height;
+
+  /* adjust negative/zero values to window size */
+  if (left < 0)
+    left += screen_width;
+  if (top < 0)
+    top += screen_height;
+  if (right <= 0)
+    right += screen_width;
+  if (bottom <= 0)
+    bottom += screen_height;
+  
+  /* clamp */
+  if (left < 0)
+    left = 0;
+  if (right > screen_width)
+    right = screen_width;
+  if (top < 0)
+    top = 0;
+  if (bottom > screen_height)
+    bottom = screen_height;
+
+  /* validate */
+  if (right <= left || bottom <= top) {
+    i_push_error(0, "image would be empty");
+    return NULL;
+  }
+
+  i_img_dim width = right - left;
+  i_img_dim height = bottom - top;
+
+  CGRect cap_rect;
+  cap_rect.origin.x = left;
+  cap_rect.origin.y = top; /* flipped relative to I::S API */
+  cap_rect.size.width = width;
+  cap_rect.size.height = height;
+
+  CGImageRef image = CGDisplayCreateImageForRect(disp, cap_rect);
+  if (!image) {
+    i_push_error(0, "CGDisplayCreateImageForRect failed");
+    return NULL;
+  }
+
+  int channels = CGImageGetAlphaInfo(image) == kCGImageAlphaNone ? 3 : 4;
+  i_img *result = i_img_8_new(width, height, channels);
+  if (!result) {
+    CGImageRelease(image);
+    return NULL;
+  }
+
+  /* bytes per row - round up to the closest 16 byte boundary */
+  size_t bytes_per_row = channels * width;
+  bytes_per_row = (bytes_per_row + 15) & ~15U;
+  
+  unsigned char *data = mymalloc(bytes_per_row * height);
+  CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
+
+  CGContextRef context = CGBitmapContextCreate
+    (data, width, height, 8, bytes_per_row, color_space,
+     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
+  CGColorSpaceRelease(color_space);
+
+  CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
+  CGContextRelease(context);
+
+  CGImageRelease(image);
+
+  const unsigned char *p = data;
+  i_img_dim y;
+  for (y = 0; y < height; ++y) {
+    i_psamp(result, 0, width, y, p, NULL, channels);
+
+    p += bytes_per_row;
+  }
+
+  i_tags_setn(&result->tags, "ss_window_width", width);
+  i_tags_setn(&result->tags, "ss_window_height", height);
+  i_tags_set(&result->tags, "ss_type", "Darwin", 6);
+  i_tags_set(&result->tags, "ss_variant", "11+", 3);
+  i_tags_setn(&result->tags, "ss_left", left);
+  i_tags_setn(&result->tags, "ss_top", top);
+
+  return result;
+}
index 0a2d46b..0b45019 100644 (file)
@@ -18,13 +18,22 @@ unless ($im) {
     or plan skip_all => "User doen't have a display";
 }
 
-plan tests => 7;
+plan tests => 8;
 
 {
   my $im = screenshot(darwin => 0);
   ok($im, "got an image");
-  is($im->getchannels, 3, "we have some color");
 
+  my $variant = $im->tags(name => "ss_variant");
+ SKIP:
+  {
+    # only the older version guarantees 3 channels
+    $variant eq "<11"
+      or skip "we can't be sure how many channels Lion returns", 1;
+    is($im->getchannels, 3, "we have some color");
+  }
+
+  like($variant, qr/^(<11|11\+)$/, "check ss_variant tag");
   is($im->tags(name => "ss_window_width"), $im->getwidth,
      "check ss_window_width tag");
   is($im->tags(name => 'ss_window_height'), $im->getheight,