http://www.jwz.org/xscreensaver/xscreensaver-5.10.tar.gz
[xscreensaver] / OSX / jwxyz.m
index c1984c931f169fc661711fe8c233ffdf9aedab79..724ac5fe3586ea7ea1deecd590c37688202eee02 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2009 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -17,6 +17,7 @@
  */
 
 #import <stdlib.h>
+#import <stdint.h>
 #import <Cocoa/Cocoa.h>
 #import "jwxyz.h"
 #import "jwxyz-timers.h"
@@ -112,6 +113,7 @@ jwxyz_make_display (void *nsview_arg)
   // w->cgc = [[[view window] graphicsContext] graphicsPort];
   w->cgc = 0;
   w->window.view = view;
+  CFRetain (w->window.view);   // needed for garbage collection?
   w->window.background = BlackPixel(0,0);
 
   d->main_window = w;
@@ -395,6 +397,8 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
   int i;
   CGRect wr = d->frame;
 
+  push_fg_gc (d, gc, YES);
+
 # ifdef XDRAWPOINTS_IMAGES
 
   unsigned int argb = gc->gcv.foreground;
@@ -440,7 +444,6 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
   CGRect *rects = (CGRect *) malloc (count * sizeof(CGRect));
   CGRect *r = rects;
   
-  push_fg_gc (d, gc, YES);
   for (i = 0; i < count; i++) {
     r->size.width = r->size.height = 1;
     if (i > 0 && mode == CoordModePrevious) {
@@ -459,6 +462,8 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
 
 # endif /* ! XDRAWPOINTS_IMAGES */
 
+  pop_gc (d, gc);
+
   return 0;
 }
 
@@ -1172,7 +1177,9 @@ Status
 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
 {
   // store 32 bit ARGB in the pixel field.
-  color->pixel = ((                       0xFF  << 24) |
+  // (The uint32_t is so that 0xFF000000 doesn't become 0xFFFFFFFFFF000000)
+  color->pixel = (uint32_t)
+                 ((                       0xFF  << 24) |
                   (((color->red   >> 8) & 0xFF) << 16) |
                   (((color->green >> 8) & 0xFF) <<  8) |
                   (((color->blue  >> 8) & 0xFF)      ));
@@ -1306,17 +1313,18 @@ ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
 static unsigned long
 ximage_getpixel_32 (XImage *ximage, int x, int y)
 {
-  return *((unsigned long *) ximage->data +
-           (y * (ximage->bytes_per_line >> 2)) +
-           x);
+  return ((unsigned long)
+          *((uint32_t *) ximage->data +
+            (y * (ximage->bytes_per_line >> 2)) +
+            x));
 }
 
 static int
 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
 {
-  *((unsigned long *) ximage->data +
+  *((uint32_t *) ximage->data +
     (y * (ximage->bytes_per_line >> 2)) +
-    x) = pixel;
+    x) = (uint32_t) pixel;
   return 0;
 }
 
@@ -1612,7 +1620,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
            unsigned long plane_mask, int format)
 {
   const unsigned char *data = 0;
-  int depth, ibpp, ibpl;
+  int depth, ibpp, ibpl, alpha_first_p;
   NSBitmapImageRep *bm = 0;
   
   Assert ((width  < 65535), "improbably large width");
@@ -1622,6 +1630,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
 
   if (d->type == PIXMAP) {
     depth = d->pixmap.depth;
+    alpha_first_p = 1; // we created it with kCGImageAlphaNoneSkipFirst.
     ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
     ibpl = CGBitmapContextGetBytesPerRow (d->cgc);
     data = CGBitmapContextGetData (d->cgc);
@@ -1636,6 +1645,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
     nsfrom.size.height = height;
     [bm initWithFocusedViewRect:nsfrom];
     depth = 32;
+    alpha_first_p = ([bm bitmapFormat] & NSAlphaFirstBitmapFormat);
     ibpp = [bm bitsPerPixel];
     ibpl = [bm bytesPerRow];
     data = [bm bitmapData];
@@ -1655,6 +1665,10 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
   /* both PPC and Intel use word-ordered ARGB frame buffers, which
      means that on Intel it is BGRA when viewed by bytes (And BGR
      when using 24bpp packing).
+
+     BUT! Intel-64 stores alpha at the other end! 32bit=RGBA, 64bit=ARGB.
+     The NSAlphaFirstBitmapFormat bit in bitmapFormat seems to be the
+     indicator of this latest kink.
    */
   int xx, yy;
   if (depth == 1) {
@@ -1664,10 +1678,10 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
       const unsigned char *iline2 = iline;
       for (xx = 0; xx < width; xx++) {
 
-        iline2++;                     // ignore b or a
-        iline2++;                     // ignore g or r
-        unsigned char r = *iline2++;  //        r or g
-        if (ibpp == 32) iline2++;     // ignore a or b
+        iline2++;                     // ignore R  or  A  or  A  or  B
+        iline2++;                     // ignore G  or  B  or  R  or  G
+        unsigned char r = *iline2++;  // use    B  or  G  or  G  or  R
+        if (ibpp == 32) iline2++;     // ignore A  or  R  or  B  or  A
 
         XPutPixel (image, xx, yy, (r ? 1 : 0));
       }
@@ -1681,19 +1695,34 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
 
       const unsigned char *iline2 = iline;
       unsigned char *oline2 = oline;
-      for (xx = 0; xx < width; xx++) {
 
-        unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
-        unsigned char r = *iline2++;
-        unsigned char g = *iline2++;
-        unsigned char b = *iline2++;
-        unsigned long pixel = ((a << 24) |
-                               (r << 16) |
-                               (g <<  8) |
-                               (b <<  0));
-        *((unsigned int *) oline2) = pixel;
-        oline2 += 4;
-      }
+      if (alpha_first_p)                       // ARGB
+        for (xx = 0; xx < width; xx++) {
+          unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
+          unsigned char r = *iline2++;
+          unsigned char g = *iline2++;
+          unsigned char b = *iline2++;
+          uint32_t pixel = ((a << 24) |
+                            (r << 16) |
+                            (g <<  8) |
+                            (b <<  0));
+          *((uint32_t *) oline2) = pixel;
+          oline2 += 4;
+        }
+      else                                     // RGBA
+        for (xx = 0; xx < width; xx++) {
+          unsigned char r = *iline2++;
+          unsigned char g = *iline2++;
+          unsigned char b = *iline2++;
+          unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
+          uint32_t pixel = ((a << 24) |
+                            (r << 16) |
+                            (g <<  8) |
+                            (b <<  0));
+          *((uint32_t *) oline2) = pixel;
+          oline2 += 4;
+        }
+
       oline += obpl;
       iline += ibpl;
     }
@@ -1704,34 +1733,113 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
   return image;
 }
 
+
+/* Returns a transformation matrix to do rotation as per the provided
+   EXIF "Orientation" value.
+ */
+static CGAffineTransform
+exif_rotate (int rot, CGSize rect)
+{
+  CGAffineTransform trans = CGAffineTransformIdentity;
+  switch (rot) {
+  case 2:              // flip horizontal
+    trans = CGAffineTransformMakeTranslation (rect.width, 0);
+    trans = CGAffineTransformScale (trans, -1, 1);
+    break;
+
+  case 3:              // rotate 180
+    trans = CGAffineTransformMakeTranslation (rect.width, rect.height);
+    trans = CGAffineTransformRotate (trans, M_PI);
+    break;
+
+  case 4:              // flip vertical
+    trans = CGAffineTransformMakeTranslation (0, rect.height);
+    trans = CGAffineTransformScale (trans, 1, -1);
+    break;
+
+  case 5:              // transpose (UL-to-LR axis)
+    trans = CGAffineTransformMakeTranslation (rect.height, rect.width);
+    trans = CGAffineTransformScale (trans, -1, 1);
+    trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
+    break;
+
+  case 6:              // rotate 90
+    trans = CGAffineTransformMakeTranslation (0, rect.width);
+    trans = CGAffineTransformRotate (trans, 3 * M_PI / 2);
+    break;
+
+  case 7:              // transverse (UR-to-LL axis)
+    trans = CGAffineTransformMakeScale (-1, 1);
+    trans = CGAffineTransformRotate (trans, M_PI / 2);
+    break;
+
+  case 8:              // rotate 270
+    trans = CGAffineTransformMakeTranslation (rect.height, 0);
+    trans = CGAffineTransformRotate (trans, M_PI / 2);
+    break;
+
+  default: 
+    break;
+  }
+
+  return trans;
+}
+
+
 void
-jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
-                    XRectangle *geom_ret)
+jwxyz_draw_NSImage_or_CGImage (Display *dpy, Drawable d, 
+                                Bool nsimg_p, void *img_arg,
+                               XRectangle *geom_ret, int exif_rotation)
 {
-  NSImage *nsimg = (NSImage *) nsimg_arg;
+  CGImageRef cgi;
+  CGImageSourceRef cgsrc;
+  NSSize imgr;
+
+  if (nsimg_p) {
+
+    NSImage *nsimg = (NSImage *) img_arg;
+    imgr = [nsimg size];
+
+    // convert the NSImage to a CGImage via the toll-free-bridging 
+    // of NSData and CFData...
+    //
+    NSData *nsdata = [NSBitmapImageRep
+                       TIFFRepresentationOfImageRepsInArray:
+                         [nsimg representations]];
+    CFDataRef cfdata = (CFDataRef) nsdata;
+    cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
+    cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
+
+  } else {
+    cgi = (CGImageRef) img_arg;
+    imgr.width  = CGImageGetWidth (cgi);
+    imgr.height = CGImageGetHeight (cgi);
+  }
+
+  Bool rot_p = (exif_rotation >= 5);
+
+  if (rot_p)
+    imgr = NSMakeSize (imgr.height, imgr.width);
 
-  // convert the NSImage to a CGImage via the toll-free-bridging 
-  // of NSData and CFData...
-  //
-  NSData *nsdata = [NSBitmapImageRep
-                        TIFFRepresentationOfImageRepsInArray:
-                          [nsimg representations]];
-  CFDataRef cfdata = (CFDataRef) nsdata;
-  CGImageSourceRef cgsrc = CGImageSourceCreateWithData (cfdata, NULL);
-  CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
-
-  NSSize imgr = [nsimg size];
   CGRect winr = d->frame;
   float rw = winr.size.width  / imgr.width;
   float rh = winr.size.height / imgr.height;
   float r = (rw < rh ? rw : rh);
 
-  CGRect dst;
+  CGRect dst, dst2;
   dst.size.width  = imgr.width  * r;
   dst.size.height = imgr.height * r;
   dst.origin.x = (winr.size.width  - dst.size.width)  / 2;
   dst.origin.y = (winr.size.height - dst.size.height) / 2;
 
+  dst2.origin.x = dst2.origin.y = 0;
+  if (rot_p) {
+    dst2.size.width = dst.size.height; 
+    dst2.size.height = dst.size.width;
+  } else {
+    dst2.size = dst.size;
+  }
+
   // Clear the part not covered by the image to background or black.
   //
   if (d->type == WINDOW)
@@ -1741,11 +1849,22 @@ jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
     draw_rect (dpy, d, 0, 0, 0, winr.size.width, winr.size.height, NO, YES);
   }
 
+  CGAffineTransform trans = 
+    exif_rotate (exif_rotation, rot_p ? dst2.size : dst.size);
+
+  CGContextSaveGState (d->cgc);
+  CGContextConcatCTM (d->cgc, 
+                      CGAffineTransformMakeTranslation (dst.origin.x,
+                                                        dst.origin.y));
+  CGContextConcatCTM (d->cgc, trans);
   //Assert (CGImageGetColorSpace (cgi) == dpy->colorspace, "bad colorspace");
-  CGContextDrawImage (d->cgc, dst, cgi);
+  CGContextDrawImage (d->cgc, dst2, cgi);
+  CGContextRestoreGState (d->cgc);
 
-  CFRelease (cgsrc);
-  CGImageRelease (cgi);
+  if (nsimg_p) {
+    CFRelease (cgsrc);
+    CGImageRelease (cgi);
+  }
 
   if (geom_ret) {
     geom_ret->x = dst.origin.x;
@@ -1911,6 +2030,14 @@ query_font (Font fid)
       NSTextStorage *ts = [[NSTextStorage alloc] initWithString:nsstr];
       [ts setFont:fid->nsfont];
       NSLayoutManager *lm = [[NSLayoutManager alloc] init];
+
+      /* Without this, the layout manager ends up on a queue somewhere and is
+         referenced again after we return to the command loop.  Since we don't
+         use this layout manager again, by that time it may have been garbage
+         collected, and we crash.  Setting this seems to cause `lm' to no 
+         longer be referenced once we exit this block. */
+      [lm setBackgroundLayoutEnabled:NO];
+
       NSTextContainer *tc = [[NSTextContainer alloc] init];
       [lm addTextContainer:tc];
       [tc release];    // lm retains tc
@@ -2239,6 +2366,7 @@ XLoadFont (Display *dpy, const char *name)
     NSLog(@"no NSFont for \"%s\"", name);
     abort();
   }
+  CFRetain (fid->nsfont);   // needed for garbage collection?
 
   //NSLog(@"parsed \"%s\" to %s %.1f", name, fid->ps_name, fid->size);
 
@@ -2248,40 +2376,6 @@ XLoadFont (Display *dpy, const char *name)
 }
 
 
-/* This translates the NSFont into the numbers that aglUseFont() wants.
- */
-int
-jwxyz_font_info (Font f, int *size_ret, int *face_ret)
-{
-  char *name = strdup (f->ps_name);
-  char *dash = strchr (name, '-');
-  int flags = 0;
-  int size = f->size;
-  if (dash) {
-    // 0 = plain; 1=B; 2=I; 3=BI; 4=U; 5=UB; etc.
-    if (strcasestr (dash, "bold"))    flags |= 1;
-    if (strcasestr (dash, "italic"))  flags |= 2;
-    if (strcasestr (dash, "oblique")) flags |= 2;
-    *dash = 0;
-  }
-  NSString *nname = [NSString stringWithCString:name
-                                       encoding:NSUTF8StringEncoding];
-  ATSFontFamilyRef id =
-    ATSFontFamilyFindFromName ((CFStringRef) nname, kATSOptionFlagsDefault);
-
-
-  // WTF?  aglUseFont gets a BadValue if size is small!!
-  if (size < 9) size = 9;
-
-  //NSLog (@"font %s %.1f => %d %d %d", f->ps_name, f->size, id, flags, size);
-  Assert (id >= 0, "no ATS font family");
-
-  *size_ret = size;
-  *face_ret = flags;
-  return id;
-}
-
-
 XFontStruct *
 XLoadQueryFont (Display *dpy, const char *name)
 {
@@ -2301,6 +2395,7 @@ XUnloadFont (Display *dpy, Font fid)
   //      They're probably not very big...
   //
   //  [fid->nsfont release];
+  //  CFRelease (fid->nsfont);
 
   free (fid);
   return 0;
@@ -2341,6 +2436,7 @@ XSetFont (Display *dpy, GC gc, Font fid)
     XUnloadFont (dpy, gc->gcv.font);
   gc->gcv.font = copy_font (fid);
   [gc->gcv.font->nsfont retain];
+  CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
   return 0;
 }
 
@@ -2390,6 +2486,7 @@ set_font (CGContextRef cgc, GC gc)
     font = XLoadFont (0, 0);
     gc->gcv.font = font;
     [gc->gcv.font->nsfont retain];
+    CFRetain (gc->gcv.font->nsfont);   // needed for garbage collection?
   }
   CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
   CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
@@ -2426,8 +2523,8 @@ draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
   set_font (d->cgc, gc);
 
   CGContextSetTextDrawingMode (d->cgc, kCGTextFill);
-  if (gc->gcv.antialias_p)
-    CGContextSetShouldAntialias (d->cgc, YES);  // always antialias text
+  if (gc->gcv.antialias_p)
+    CGContextSetShouldAntialias (d->cgc, YES);
   CGContextShowTextAtPoint (d->cgc,
                             wr.origin.x + x,
                             wr.origin.y + wr.size.height - y,