From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / texfont.c
index 845d0b6a7f9540a372304f0c1026d6231b0ec99a..8f39ea86bddfb08778255a089e518eb9dd412759 100644 (file)
@@ -1,5 +1,4 @@
-/* texfonts, Copyright (c) 2005-2007 Jamie Zawinski <jwz@jwz.org>
- * Loads X11 fonts into textures for use with OpenGL.
+/* texfonts, Copyright (c) 2005-2014 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
@@ -8,9 +7,11 @@
  * documentation.  No representations are made about the suitability of this
  * software for any purpose.  It is provided "as is" without express or 
  * implied warranty.
+ *
+ * Renders X11 fonts into textures for use with OpenGL.
+ * A higher level API is in glxfonts.c.
  */
 
-
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <ctype.h>
 
 #ifdef HAVE_COCOA
-# include <OpenGL/glu.h>
+# ifdef USE_IPHONE
+#  include "jwzgles.h"
+# else
+#  include <OpenGL/glu.h>
+# endif
 #else
 # include <GL/glx.h>
 # include <GL/glu.h>
 #endif
 
+#ifdef HAVE_JWZGLES
+# include "jwzgles.h"
+#endif /* HAVE_JWZGLES */
+
 #include "resources.h"
 #include "texfont.h"
 
+#define DO_SUBSCRIPTS
+
+
 /* These are in xlock-gl.c */
 extern void clear_gl_error (void);
 extern void check_gl_error (const char *type);
@@ -82,11 +94,30 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
   int h2 = to_pow2 (oh);
   int x, y;
   XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
-  unsigned char *data = (unsigned char *) calloc (w2, (h2 + 1));
+  unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
   unsigned char *out = data;
+
+  /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
+     texture with 1 byte per pixel, the intensity value, we have
+     to use 2 bytes per pixel: solid white, and an alpha value.
+   */
+# ifdef HAVE_JWZGLES
+#  undef GL_INTENSITY
+# endif
+
+# ifdef GL_INTENSITY
   GLuint iformat = GL_INTENSITY;
-  GLuint format = GL_LUMINANCE;
-  GLuint type = GL_UNSIGNED_BYTE;
+  GLuint format  = GL_LUMINANCE;
+# else
+  GLuint iformat = GL_LUMINANCE_ALPHA;
+  GLuint format  = GL_LUMINANCE_ALPHA;
+# endif
+  GLuint type    = GL_UNSIGNED_BYTE;
+
+# ifdef HAVE_JWZGLES
+  /* This would work, but it's wasteful for no benefit. */
+  mipmap_p = False;
+# endif
 
   for (y = 0; y < h2; y++)
     for (x = 0; x < w2; x++) {
@@ -94,7 +125,11 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
       /* instead of averaging all three channels, let's just use red,
          and assume it was already grayscale. */
       unsigned long r = pixel & visual->red_mask;
+      /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
+# ifndef GL_INTENSITY
+      *out++ = 0xFF;  /* 2 bytes per pixel */
+# endif
       *out++ = pixel;
     }
   XDestroyImage (image);
@@ -118,6 +153,20 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                    mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
 
+
+  /* This makes scaled font pixmaps tolerable to look at.
+     LOD bias is part of OpenGL 1.4.
+     GL_EXT_texture_lod_bias has been present since the original iPhone.
+   */
+# if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
+#   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
+# endif
+# ifdef GL_TEXTURE_LOD_BIAS
+  if (mipmap_p)
+    glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
+# endif
+  clear_gl_error();  /* invalid enum on iPad 3 */
+
   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
@@ -128,69 +177,20 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
 }
 
 
-/* Loads the font named by the X resource "res" and returns
-   a texture-font object.
-*/
-texture_font_data *
-load_texture_font (Display *dpy, char *res)
+static texture_font_data *
+load_texture_xfont (Display *dpy, XFontStruct *f)
 {
   Screen *screen = DefaultScreenOfDisplay (dpy);
   Window root = RootWindowOfScreen (screen);
   XWindowAttributes xgwa;
-
-  texture_font_data *data = 0;
-  char *font = get_string_resource (dpy, res, "Font");
-  const char *def1 = "-*-times-bold-r-normal-*-240-*";
-  const char *def2 = "-*-times-bold-r-normal-*-180-*";
-  const char *def3 = "fixed";
-  XFontStruct *f;
   int which;
+  GLint old_texture = 0;
+  texture_font_data *data = 0;
 
-  check_gl_error ("stale texture font");
+  glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
 
   XGetWindowAttributes (dpy, root, &xgwa);
 
-  if (!res || !*res) abort();
-  if (!font) font = strdup(def1);
-
-  f = XLoadQueryFont(dpy, font);
-  if (!f && !!strcmp (font, def1))
-    {
-      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
-               progname, font, def1);
-      free (font);
-      font = strdup (def1);
-      f = XLoadQueryFont(dpy, font);
-    }
-
-  if (!f && !!strcmp (font, def2))
-    {
-      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
-               progname, font, def2);
-      free (font);
-      font = strdup (def2);
-      f = XLoadQueryFont(dpy, font);
-    }
-
-  if (!f && !!strcmp (font, def3))
-    {
-      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
-               progname, font, def3);
-      free (font);
-      font = strdup (def3);
-      f = XLoadQueryFont(dpy, font);
-    }
-
-  if (!f)
-    {
-      fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
-               progname, font);
-      exit (1);
-    }
-
-  free (font);
-  font = 0;
-
   data = (texture_font_data *) calloc (1, sizeof(*data));
   data->dpy = dpy;
   data->font = f;
@@ -257,13 +257,13 @@ load_texture_font (Display *dpy, char *res)
 
           /* See comment in print_texture_string for bit layout explanation.
            */
-          int lbearing = (f->per_char
+          int lbearing = (f->per_char && ii >= f->min_char_or_byte2
                           ? f->per_char[ii - f->min_char_or_byte2].lbearing
                           : f->min_bounds.lbearing);
-          int ascent   = (f->per_char
+          int ascent   = (f->per_char && ii >= f->min_char_or_byte2
                           ? f->per_char[ii - f->min_char_or_byte2].ascent
                           : f->max_bounds.ascent);
-          int width    = (f->per_char
+          int width    = (f->per_char && ii >= f->min_char_or_byte2
                           ? f->per_char[ii - f->min_char_or_byte2].width
                           : f->max_bounds.width);
 
@@ -278,75 +278,138 @@ load_texture_font (Display *dpy, char *res)
       data->tex_width  = w;
       data->tex_height = h;
 
-#if 0  /* debugging: splat the bitmap onto the desktop root window */
-      {
-        Window win = RootWindow (dpy, 0);
-        GC gc2 = XCreateGC (dpy, win, 0, &gcv);
-        XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
-        XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
-        XCopyArea (dpy, p, win, gc2, 0, 0, w, h, 0, 0);
-        XFreeGC (dpy, gc2);
-        XSync(dpy, False);
-        usleep (100000);
-      }
-#endif
-
 #if 0  /* debugging: write the bitmap to a pgm file */
       {
         char file[255];
         XImage *image;
         int x, y;
-        FILE *f;
+        FILE *ff;
         sprintf (file, "/tmp/%02d.pgm", which);
         image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
-        f = fopen (file, "w");
-        fprintf (f, "P5\n%d %d\n255\n", w, h);
+        ff = fopen (file, "w");
+        fprintf (ff, "P5\n%d %d\n255\n", w, h);
         for (y = 0; y < h; y++)
           for (x = 0; x < w; x++) {
             unsigned long pix = XGetPixel (image, x, y);
             unsigned long r = (pix & xgwa.visual->red_mask);
             r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
-            fprintf (f, "%c", (char) r);
+            fprintf (ff, "%c", (char) r);
           }
-        fclose (f);
+        fclose (ff);
         XDestroyImage (image);
-        fprintf (stderr, "%s: wrote %s\n", progname, file);
+        fprintf (stderr, "%s: wrote %s (%d x %d)\n", progname, file,
+                 f->max_bounds.rbearing - f->min_bounds.lbearing,
+                 f->max_bounds.ascent   + f->max_bounds.descent);
       }
-#endif
+#endif /* 0 */
 
       bitmap_to_texture (dpy, p, xgwa.visual, 
                          &data->tex_width, &data->tex_height);
       XFreePixmap (dpy, p);
     }
 
+  /* Reset to the caller's default */
+  glBindTexture (GL_TEXTURE_2D, old_texture);
+
   return data;
 }
 
 
-/* Width of the string in pixels.
+/* Loads the font named by the X resource "res" and returns
+   a texture-font object.
+*/
+texture_font_data *
+load_texture_font (Display *dpy, char *res)
+{
+  char *font = get_string_resource (dpy, res, "Font");
+  const char *def1 = "-*-helvetica-medium-r-normal-*-240-*";
+  const char *def2 = "-*-helvetica-medium-r-normal-*-180-*";
+  const char *def3 = "fixed";
+  XFontStruct *f;
+
+  if (!strcmp (res, "fpsFont"))
+    def1 = "-*-courier-bold-r-normal-*-180-*";  /* Kludge. Sue me. */
+
+  if (!res || !*res) abort();
+  if (!font) font = strdup(def1);
+
+  f = XLoadQueryFont(dpy, font);
+  if (!f && !!strcmp (font, def1))
+    {
+      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+               progname, font, def1);
+      free (font);
+      font = strdup (def1);
+      f = XLoadQueryFont(dpy, font);
+    }
+
+  if (!f && !!strcmp (font, def2))
+    {
+      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+               progname, font, def2);
+      free (font);
+      font = strdup (def2);
+      f = XLoadQueryFont(dpy, font);
+    }
+
+  if (!f && !!strcmp (font, def3))
+    {
+      fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+               progname, font, def3);
+      free (font);
+      font = strdup (def3);
+      f = XLoadQueryFont(dpy, font);
+    }
+
+  if (!f)
+    {
+      fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
+               progname, font);
+      exit (1);
+    }
+
+  free (font);
+  font = 0;
+
+  return load_texture_xfont (dpy, f);
+}
+
+
+/* Bounding box of the multi-line string, in pixels.
  */
 int
-texture_string_width (texture_font_data *data, const char *c,
-                      int *line_height_ret)
+texture_string_width (texture_font_data *data, const char *c, int *height_ret)
 {
-  int w = 0;
   XFontStruct *f = data->font;
+  int x = 0;
+  int max_w = 0;
+  int lh = f->ascent + f->descent;
+  int h = lh;
   while (*c)
     {
       int cc = *((unsigned char *) c);
-      w += (f->per_char
-            ? f->per_char[cc-f->min_char_or_byte2].width
-            : f->max_bounds.width);
+      if (*c == '\n')
+        {
+          if (x > max_w) max_w = x;
+          x = 0;
+          h += lh;
+        }
+      else
+        x += (f->per_char
+              ? f->per_char[cc-f->min_char_or_byte2].width
+              : f->min_bounds.rbearing);
       c++;
     }
-  if (line_height_ret)
-    *line_height_ret = f->ascent + f->descent;
-  return w;
+  if (x > max_w) max_w = x;
+  if (height_ret) *height_ret = h;
+  return max_w;
 }
 
 
 /* Draws the string in the scene at the origin.
    Newlines and tab stops are honored.
+   Any numbers inside [] will be rendered as a subscript.
+   Assumes the font has been loaded as with load_texture_font().
  */
 void
 print_texture_string (texture_font_data *data, const char *string)
@@ -361,12 +424,24 @@ print_texture_string (texture_font_data *data, const char *string)
   int tabs = cw * 7;
   int x, y;
   unsigned int i;
+  GLint old_texture = 0;
+  GLfloat omatrix[16];
+  int ofront;
+
+  glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
+  glGetIntegerv (GL_FRONT_FACE, &ofront);
+  glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
 
   clear_gl_error ();
 
   glPushMatrix();
 
   glNormal3f (0, 0, 1);
+  glFrontFace (GL_CW);
+
+  glMatrixMode (GL_TEXTURE);
+  glLoadIdentity ();
+  glMatrixMode (GL_MODELVIEW);
 
   x = 0;
   y = 0;
@@ -380,7 +455,8 @@ print_texture_string (texture_font_data *data, const char *string)
         }
       else if (c == '\t')
         {
-          x = ((x + tabs) / tabs) * tabs;  /* tab to tab stop */
+          if (tabs)
+            x = ((x + tabs) / tabs) * tabs;  /* tab to tab stop */
         }
 # ifdef DO_SUBSCRIPTS
       else if (c == '[' && (isdigit (string[i+1])))
@@ -430,23 +506,23 @@ print_texture_string (texture_font_data *data, const char *string)
              We want to make a quad from point A to point C.
              We want to position that quad so that point B lies at x,y.
            */
-          int lbearing = (f->per_char
+          int lbearing = (f->per_char && c >= f->min_char_or_byte2
                           ? f->per_char[c - f->min_char_or_byte2].lbearing
                           : f->min_bounds.lbearing);
-          int rbearing = (f->per_char
+          int rbearing = (f->per_char && c >= f->min_char_or_byte2
                           ? f->per_char[c - f->min_char_or_byte2].rbearing
                           : f->max_bounds.rbearing);
-          int ascent   = (f->per_char
+          int ascent   = (f->per_char && c >= f->min_char_or_byte2
                           ? f->per_char[c - f->min_char_or_byte2].ascent
                           : f->max_bounds.ascent);
-          int descent  = (f->per_char
+          int descent  = (f->per_char && c >= f->min_char_or_byte2
                           ? f->per_char[c - f->min_char_or_byte2].descent
                           : f->max_bounds.descent);
-          int cwidth   = (f->per_char
+          int cwidth   = (f->per_char && c >= f->min_char_or_byte2
                           ? f->per_char[c - f->min_char_or_byte2].width
                           : f->max_bounds.width);
 
-          char cc = c % (256 / data->ntextures);
+          unsigned char cc = c % (256 / data->ntextures);
 
           int gs = (16 / data->grid_mag);                /* grid size */
 
@@ -456,8 +532,8 @@ print_texture_string (texture_font_data *data, const char *string)
           int bx = ax - lbearing;                         /* point B */
           int by = ay + ascent;
 
-          int cx = bx + rbearing;                         /* point C */
-          int cy = by + descent;
+          int cx = bx + rbearing + 1;                     /* point C */
+          int cy = by + descent  + 1;
 
           GLfloat tax = (GLfloat) ax / data->tex_width;  /* tex coords of A */
           GLfloat tay = (GLfloat) ay / data->tex_height;
@@ -499,6 +575,14 @@ print_texture_string (texture_font_data *data, const char *string)
       }
   glPopMatrix();
 
+  /* Reset to the caller's default */
+  glBindTexture (GL_TEXTURE_2D, old_texture);
+  glFrontFace (ofront);
+  
+  glMatrixMode (GL_TEXTURE);
+  glMultMatrixf (omatrix);
+  glMatrixMode (GL_MODELVIEW);
+
   check_gl_error ("texture font print");
 }
 
@@ -508,10 +592,13 @@ void
 free_texture_font (texture_font_data *data)
 {
   int i;
-  if (data->font)
-    XFreeFont (data->dpy, data->font);
+
   for (i = 0; i < data->ntextures; i++)
     if (data->texid[i])
       glDeleteTextures (1, &data->texid[i]);
+
+  if (data->font)
+    XFreeFont (data->dpy, data->font);
+
   free (data);
 }