http://www.jwz.org/xscreensaver/xscreensaver-5.09.tar.gz
[xscreensaver] / hacks / glx / glxfonts.c
index 55ee7f515f3cfa0eb5277328f81da6919c39777b..9197af975b2f69ecb4ab8e4e8fb7f0395ff615d1 100644 (file)
@@ -1,4 +1,4 @@
-/* glxfonts, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
+/* glxfonts, Copyright (c) 2001-2009 Jamie Zawinski <jwz@jwz.org>
  * Loads X11 fonts for use with OpenGL.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
@@ -46,6 +46,221 @@ extern char *progname;
                */
 
 
+/* Mostly lifted from the Mesa implementation of glXUseXFont(), since
+   Mac OS 10.6 no longer supports aglUseFont() which was their analog
+   of that.  This code could be in libjwxyz instead, but we might as
+   well use the same text-drawing code on both X11 and Cocoa.
+ */
+static void
+fill_bitmap (Display *dpy, Window win, GC gc,
+            unsigned int width, unsigned int height,
+            int x0, int y0, char c, GLubyte *bitmap)
+{
+  XImage *image;
+  int x, y;
+  Pixmap pixmap;
+
+  pixmap = XCreatePixmap (dpy, win, 8*width, height, 1);
+  XSetForeground(dpy, gc, 0);
+  XFillRectangle (dpy, pixmap, gc, 0, 0, 8*width, height);
+  XSetForeground(dpy, gc, 1);
+  XDrawString (dpy, pixmap, gc, x0, y0, &c, 1);
+
+  image = XGetImage (dpy, pixmap, 0, 0, 8*width, height, 1, XYPixmap);
+
+  /* Fill the bitmap (X11 and OpenGL are upside down wrt each other).  */
+  for (y = 0; y < height; y++)
+    for (x = 0; x < 8*width; x++)
+      if (XGetPixel (image, x, y))
+       bitmap[width*(height - y - 1) + x/8] |= (1 << (7 - (x % 8)));
+  
+  XFreePixmap (dpy, pixmap);
+  XDestroyImage (image);
+}
+
+
+#if 0
+static void
+dump_bitmap (unsigned int width, unsigned int height, GLubyte *bitmap)
+{
+  int x, y;
+
+  printf ("    ");
+  for (x = 0; x < 8*width; x++)
+    printf ("%o", 7 - (x % 8));
+  putchar ('\n');
+  for (y = 0; y < height; y++)
+    {
+      printf ("%3o:", y);
+      for (x = 0; x < 8*width; x++)
+        putchar ((bitmap[width*(height - y - 1) + x/8] & (1 << (7 - (x % 8))))
+                ? '#' : '.');
+      printf ("   ");
+      for (x = 0; x < width; x++)
+       printf ("0x%02x, ", bitmap[width*(height - y - 1) + x]);
+      putchar ('\n');
+    }
+}
+#endif
+
+
+void
+xscreensaver_glXUseXFont (Display *dpy, Font font, 
+                          int first, int count, int listbase)
+{
+  Window win = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
+  Pixmap pixmap;
+  GC gc;
+  XGCValues values;
+  unsigned long valuemask;
+
+  XFontStruct *fs;
+
+  GLint swapbytes, lsbfirst, rowlength;
+  GLint skiprows, skippixels, alignment;
+
+  unsigned int max_width, max_height, max_bm_width, max_bm_height;
+  GLubyte *bm;
+
+  int i;
+
+  fs = XQueryFont (dpy, font);  
+  if (!fs)
+    {
+      /*gl_error (CC->gl_ctx, GL_INVALID_VALUE,
+               "Couldn't get font structure information");*/
+      abort();
+      return;
+    }
+
+  /* Allocate a bitmap that can fit all characters.  */
+  max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
+  max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
+  max_bm_width = (max_width + 7) / 8;
+  max_bm_height = max_height;
+
+  bm = (GLubyte *) malloc ((max_bm_width * max_bm_height) * sizeof (GLubyte));
+  if (!bm)
+    {
+      /*gl_error (CC->gl_ctx, GL_OUT_OF_MEMORY,
+               "Couldn't allocate bitmap in glXUseXFont()");*/
+      abort();
+      return;
+    }
+
+  /* Save the current packing mode for bitmaps.  */
+  glGetIntegerv        (GL_UNPACK_SWAP_BYTES, &swapbytes);
+  glGetIntegerv        (GL_UNPACK_LSB_FIRST, &lsbfirst);
+  glGetIntegerv        (GL_UNPACK_ROW_LENGTH, &rowlength);
+  glGetIntegerv        (GL_UNPACK_SKIP_ROWS, &skiprows);
+  glGetIntegerv        (GL_UNPACK_SKIP_PIXELS, &skippixels);
+  glGetIntegerv        (GL_UNPACK_ALIGNMENT, &alignment);
+
+  /* Enforce a standard packing mode which is compatible with
+     fill_bitmap() from above.  This is actually the default mode,
+     except for the (non)alignment.  */
+  glPixelStorei        (GL_UNPACK_SWAP_BYTES, GL_FALSE);
+  glPixelStorei        (GL_UNPACK_LSB_FIRST, GL_FALSE);
+  glPixelStorei        (GL_UNPACK_ROW_LENGTH, 0);
+  glPixelStorei        (GL_UNPACK_SKIP_ROWS, 0);
+  glPixelStorei        (GL_UNPACK_SKIP_PIXELS, 0);
+  glPixelStorei        (GL_UNPACK_ALIGNMENT, 1);
+
+  pixmap = XCreatePixmap (dpy, win, 10, 10, 1);
+  values.foreground = 0;
+  values.background = 1;
+  values.font = fs->fid;
+  valuemask = GCForeground | GCBackground | GCFont;
+  gc = XCreateGC (dpy, pixmap, valuemask, &values);
+  XFreePixmap (dpy, pixmap);
+
+# ifdef HAVE_COCOA
+  /* Anti-aliasing of fonts looks like crap with 1-bit bitmaps.
+     It would be nice if we were using full-depth bitmaps, so
+     that the text showed up anti-aliased on screen, but
+     glBitmap() doesn't work that way. */
+  jwxyz_XSetAntiAliasing (dpy, gc, False);
+# endif
+
+  for (i = 0; i < count; i++)
+    {
+      unsigned int width, height, bm_width, bm_height;
+      GLfloat x0, y0, dx, dy;
+      XCharStruct *ch;
+      int x, y;
+      int c = first + i;
+      int list = listbase + i;
+
+      if (fs->per_char
+         && (c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2))
+       ch = &fs->per_char[c-fs->min_char_or_byte2];
+      else
+       ch = &fs->max_bounds;
+
+      /* I'm not entirely clear on why this is necessary on OSX, but
+         without it, the characters are clipped.  And it does not hurt
+         under real X11.  -- jwz. */
+      ch->lbearing--;
+      ch->ascent++;
+
+      /* glBitmap()'s parameters:
+         "Bitmap parameters xorig, yorig, width, and height are
+         computed from font metrics as descent-1, -lbearing,
+         rbearing-lbearing, and ascent+descent, respectively. 
+         xmove is taken from the glyph's width metric, and 
+         ymove is set to zero. Finally, the glyph's image is 
+         converted to the appropriate format for glBitmap."
+      */
+      width = ch->rbearing - ch->lbearing;
+      height = ch->ascent + ch->descent;
+      x0 = - ch->lbearing;
+      y0 = ch->descent - 1;
+      dx = ch->width;
+      dy = 0;
+
+      /* X11's starting point.  */
+      x = - ch->lbearing;
+      y = ch->ascent;
+      
+      /* Round the width to a multiple of eight.  We will use this also
+        for the pixmap for capturing the X11 font.  This is slightly
+        inefficient, but it makes the OpenGL part real easy.  */
+      bm_width = (width + 7) / 8;
+      bm_height = height;
+
+      glNewList (list, GL_COMPILE);
+        if ((c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2)
+           && (bm_width > 0) && (bm_height > 0))
+         {
+           memset (bm, '\0', bm_width * bm_height);
+           fill_bitmap (dpy, win, gc, bm_width, bm_height, x, y, c, bm);
+           glBitmap (width, height, x0, y0, dx, dy, bm);
+#if 0
+            printf ("width/height = %d/%d\n", width, height);
+            printf ("bm_width/bm_height = %d/%d\n", bm_width, bm_height);
+            dump_bitmap (bm_width, bm_height, bm);
+#endif
+         }
+       else
+         glBitmap (0, 0, 0.0, 0.0, dx, dy, NULL);
+      glEndList ();
+    }
+
+  free (bm);
+  XFreeFontInfo( NULL, fs, 0 );
+  XFreeGC (dpy, gc);
+
+  /* Restore saved packing modes.  */    
+  glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
+  glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
+  glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
+  glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
+  glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+}
+
+
+
 /* Loads the font named by the X resource "res".
    Returns an XFontStruct.
    Also converts the font to a set of GL lists and returns the first list.
@@ -95,39 +310,15 @@ load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
   last = f->max_char_or_byte2;
   
 
-# ifndef HAVE_COCOA /* Xlib version */
-
   if (dlist_ret)
     {
       clear_gl_error ();
       *dlist_ret = glGenLists ((GLuint) last+1);
       check_gl_error ("glGenLists");
-      glXUseXFont(id, first, last-first+1, *dlist_ret + first);
-      check_gl_error ("glXUseXFont");
-    }
-
-# else  /* HAVE_COCOA */
-
-  {
-    int afid, face, size;
-    afid = jwxyz_font_info (id, &size, &face);
-
-    if (dlist_ret)
-      {
-        clear_gl_error ();
-        *dlist_ret = glGenLists ((GLuint) last+1);
-        check_gl_error ("glGenLists");
-
-        AGLContext ctx = aglGetCurrentContext();
-        if (! aglUseFont (ctx, afid, face, size, 
-                          first, last-first+1, *dlist_ret + first)) {
-          check_gl_error ("aglUseFont");
-          abort();
-      }
+      xscreensaver_glXUseXFont(dpy, id, first, last-first+1,
+                               *dlist_ret + first);
+      check_gl_error ("xscreensaver_glXUseXFont");
     }
-  }
-
-# endif  /* HAVE_COCOA */
 
   if (font_ret)
     *font_ret = f;