http://www.jwz.org/xscreensaver/xscreensaver-5.08.tar.gz
[xscreensaver] / OSX / jwxyz.m
index 590b3ccad0d0c4995e242eaec8394a227c9abe7a..17a37734f24e47cae5d63e91bf2806ffa56c31e5 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2006 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2008 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
@@ -395,6 +395,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 +442,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 +460,8 @@ XDrawPoints (Display *dpy, Drawable d, GC gc,
 
 # endif /* ! XDRAWPOINTS_IMAGES */
 
+  pop_gc (d, gc);
+
   return 0;
 }
 
@@ -1479,6 +1482,13 @@ XPutImage (Display *dpy, Drawable d, GC gc, XImage *ximage,
 {
   CGRect wr = d->frame;
 
+  Assert ((w < 65535), "improbably large width");
+  Assert ((h < 65535), "improbably large height");
+  Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
+  Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
+  Assert ((dest_x < 65535 && dest_x > -65535), "improbably large dest_x");
+  Assert ((dest_y < 65535 && dest_y > -65535), "improbably large dest_y");
+
   // Clip width and height to the bounds of the Drawable
   //
   if (dest_x + w > wr.size.width) {
@@ -1608,6 +1618,11 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
   int depth, ibpp, ibpl;
   NSBitmapImageRep *bm = 0;
   
+  Assert ((width  < 65535), "improbably large width");
+  Assert ((height < 65535), "improbably large height");
+  Assert ((x < 65535 && x > -65535), "improbably large x");
+  Assert ((y < 65535 && y > -65535), "improbably large y");
+
   if (d->type == PIXMAP) {
     depth = d->pixmap.depth;
     ibpp = CGBitmapContextGetBitsPerPixel (d->cgc);
@@ -1628,10 +1643,11 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
     ibpl = [bm bytesPerRow];
     data = [bm bitmapData];
     Assert (data, "NSBitmapImageRep initWithFocusedViewRect failed");
-
-    data += (y * ibpl) + (x * (ibpp/8));
   }
   
+  // data points at (x,y) with ibpl rowstride.  ignore x,y from now on.
+  data += (y * ibpl) + (x * (ibpp/8));
+  
   format = (depth == 1 ? XYPixmap : ZPixmap);
   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
                                 0, 0);
@@ -1646,10 +1662,10 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
   int xx, yy;
   if (depth == 1) {
     const unsigned char *iline = data;
-    for (yy = y; yy < y+height; yy++) {
+    for (yy = 0; yy < height; yy++) {
 
       const unsigned char *iline2 = iline;
-      for (xx = x; xx < x+width; xx++) {
+      for (xx = 0; xx < width; xx++) {
 
         iline2++;                     // ignore b or a
         iline2++;                     // ignore g or r
@@ -1664,12 +1680,11 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
     Assert (ibpp == 24 || ibpp == 32, "weird obpp");
     const unsigned char *iline = data;
     unsigned char *oline = (unsigned char *) image->data;
-    oline += (y * obpl);
-    for (yy = y; yy < y+height; yy++) {
+    for (yy = 0; yy < height; yy++) {
 
       const unsigned char *iline2 = iline;
       unsigned char *oline2 = oline;
-      for (xx = x; xx < x+width; xx++) {
+      for (xx = 0; xx < width; xx++) {
 
         unsigned char a = (ibpp == 32 ? (*iline2++) : 0xFF);
         unsigned char r = *iline2++;
@@ -1681,7 +1696,7 @@ XGetImage (Display *dpy, Drawable d, int x, int y,
                                (b <<  0));
         *((unsigned int *) oline2) = pixel;
         oline2 += 4;
-     }
+      }
       oline += obpl;
       iline += ibpl;
     }
@@ -1692,9 +1707,62 @@ 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)
+                    XRectangle *geom_ret, int exif_rotation)
 {
   NSImage *nsimg = (NSImage *) nsimg_arg;
 
@@ -1709,17 +1777,30 @@ jwxyz_draw_NSImage (Display *dpy, Drawable d, void *nsimg_arg,
   CGImageRef cgi = CGImageSourceCreateImageAtIndex (cgsrc, 0, NULL);
 
   NSSize imgr = [nsimg size];
+  Bool rot_p = (exif_rotation >= 5);
+
+  if (rot_p)
+    imgr = NSMakeSize (imgr.height, imgr.width);
+
   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)
@@ -1729,8 +1810,17 @@ 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);
@@ -1842,6 +1932,15 @@ copy_pixmap (Pixmap p)
 static void
 query_font (Font fid)
 {
+  if (!fid || !fid->nsfont) {
+    NSLog(@"no NSFont in fid");
+    abort();
+  }
+  if (![fid->nsfont fontName]) {
+    NSLog(@"broken NSFont in fid");
+    abort();
+  }
+
   int first = 32;
   int last = 255;
 
@@ -1993,7 +2092,7 @@ copy_font (Font fid)
 
   // copy the other pointers
   fid2->ps_name = strdup (fid->ps_name);
-  [fid2->nsfont retain];
+//  [fid2->nsfont retain];
   fid2->metrics.fid = fid2;
 
   return fid2;
@@ -2004,23 +2103,45 @@ static NSFont *
 try_font (BOOL fixed, BOOL bold, BOOL ital, BOOL serif, float size,
           char **name_ret)
 {
-  const char *prefix = (fixed ? "Monaco" : (serif ? "Times" : "Helvetica"));
-  const char *suffix = (bold && ital
-                        ? (serif ? "-BoldItalic" : "-BoldOblique")
-                        : (bold ? "-Bold" :
-                           ital ? (serif ? "-Italic" : "-Oblique") : ""));
-  char *name = (char *) malloc (strlen(prefix) + strlen(suffix) + 1);
-  strcpy (name, prefix);
-  strcat (name, suffix);
+  Assert (size > 0, "zero font size");
+  const char *name;
+
+  if (fixed) {
+    // 
+    // "Monaco" only exists in plain.
+    // "LucidaSansTypewriterStd" gets an AGL bad value error.
+    // 
+    if (bold && ital) name = "Courier-BoldOblique";
+    else if (bold)    name = "Courier-Bold";
+    else if (ital)    name = "Courier-Oblique";
+    else              name = "Courier";
+
+  } else if (serif) {
+    // 
+    // "Georgia" looks better than "Times".
+    // 
+    if (bold && ital) name = "Georgia-BoldItalic";
+    else if (bold)    name = "Georgia-Bold";
+    else if (ital)    name = "Georgia-Italic";
+    else              name = "Georgia";
+
+  } else {
+    // 
+    // "Geneva" only exists in plain.
+    // "LucidaSansStd-BoldItalic" gets an AGL bad value error.
+    // "Verdana" renders smoother than "Helvetica" for some reason.
+    // 
+    if (bold && ital) name = "Verdana-BoldItalic";
+    else if (bold)    name = "Verdana-Bold";
+    else if (ital)    name = "Verdana-Italic";
+    else              name = "Verdana";
+  }
 
   NSString *nsname = [NSString stringWithCString:name
                                         encoding:NSUTF8StringEncoding];
   NSFont *f = [NSFont fontWithName:nsname size:size];
-  if (f) {
-    *name_ret = name;
-  } else {
-    free (name);
-  }
+  if (f)
+    *name_ret = strdup(name);
   return f;
 }
 
@@ -2055,8 +2176,8 @@ try_native_font (const char *name, char **name_ret, float *size_ret)
 static NSFont *
 random_font (BOOL bold, BOOL ital, float size, char **name_ret)
 {
-  NSFontTraitMask mask = ((bold ? NSUnboldFontMask   : NSBoldFontMask) |
-                          (ital ? NSUnitalicFontMask : NSItalicFontMask));
+  NSFontTraitMask mask = ((bold ? NSBoldFontMask   : NSUnboldFontMask) |
+                          (ital ? NSItalicFontMask : NSUnitalicFontMask));
   NSArray *fonts = [[NSFontManager sharedFontManager]
                      availableFontNamesWithTraits:mask];
   if (!fonts) return 0;
@@ -2230,7 +2351,7 @@ jwxyz_font_info (Font f, int *size_ret, int *face_ret)
   // WTF?  aglUseFont gets a BadValue if size is small!!
   if (size < 9) size = 9;
 
-  //NSLog (@"font %s %.1f => %d %d %d\n", f->ps_name, f->size, id, flags, size);
+  //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;
@@ -2251,7 +2372,14 @@ XUnloadFont (Display *dpy, Font fid)
 {
   free (fid->ps_name);
   free (fid->metrics.per_char);
-  [fid->nsfont release];
+
+  // #### DAMMIT!  I can't tell what's going wrong here, but I keep getting
+  //      crashes in [NSFont ascender] <- query_font, and it seems to go away
+  //      if I never release the nsfont.  So, fuck it, we'll just leak fonts.
+  //      They're probably not very big...
+  //
+  //  [fid->nsfont release];
+
   free (fid);
   return 0;
 }
@@ -2290,15 +2418,10 @@ XSetFont (Display *dpy, GC gc, Font fid)
   if (gc->gcv.font)
     XUnloadFont (dpy, gc->gcv.font);
   gc->gcv.font = copy_font (fid);
+  [gc->gcv.font->nsfont retain];
   return 0;
 }
 
-Font  // really GContext
-XGContextFromGC (GC gc)    // WTF is this actually supposed to do?
-{
-  return gc->gcv.font;
-}
-
 int
 XTextExtents (XFontStruct *f, const char *s, int length,
               int *dir_ret, int *ascent_ret, int *descent_ret,
@@ -2341,8 +2464,11 @@ static void
 set_font (CGContextRef cgc, GC gc)
 {
   Font font = gc->gcv.font;
-  if (! font)
-    font = gc->gcv.font = XLoadFont (0, 0);
+  if (! font) {
+    font = XLoadFont (0, 0);
+    gc->gcv.font = font;
+    [gc->gcv.font->nsfont retain];
+  }
   CGContextSelectFont (cgc, font->ps_name, font->size, kCGEncodingMacRoman);
   CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
 }