From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2016 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Renders X11 fonts into textures for use with OpenGL.
12  * A higher level API is in glxfonts.c.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23
24 #ifdef HAVE_COCOA
25 # ifdef USE_IPHONE
26 #  include "jwzgles.h"
27 # else
28 #  include <OpenGL/glu.h>
29 # endif
30 #elif defined(HAVE_ANDROID)
31 # include <GLES/gl.h>
32 # include "jwzgles.h"
33 #else
34 # include <GL/glx.h>
35 # include <GL/glu.h>
36 #endif
37
38 #ifdef HAVE_JWZGLES
39 # include "jwzgles.h"
40 #endif /* HAVE_JWZGLES */
41
42 #ifdef HAVE_XSHM_EXTENSION
43 # include "xshm.h"
44 #endif /* HAVE_XSHM_EXTENSION */
45
46 #include "xft.h"
47 #include "resources.h"
48 #include "texfont.h"
49 #include "fps.h"        /* for current_device_rotation() */
50
51 #undef HAVE_XSHM_EXTENSION  /* doesn't actually do any good here */
52
53
54 /* These are in xlock-gl.c */
55 extern void clear_gl_error (void);
56 extern void check_gl_error (const char *type);
57
58 /* screenhack.h */
59 extern char *progname;
60
61 /* LRU cache of textures, to optimize the case where we're drawing the
62    same strings repeatedly.
63  */
64 typedef struct texfont_cache texfont_cache;
65 struct texfont_cache {
66   char *string;
67   GLuint texid;
68   XCharStruct extents;
69   int tex_width, tex_height;
70   texfont_cache *next;
71 };
72
73 struct texture_font_data {
74   Display *dpy;
75   XftFont *xftfont;
76   int cache_size;
77   texfont_cache *cache;
78 };
79
80
81 #undef countof
82 #define countof(x) (sizeof((x))/sizeof((*x)))
83
84
85 /* return the next larger power of 2. */
86 static int
87 to_pow2 (int i)
88 {
89   static const unsigned int pow2[] = { 
90     1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 
91     2048, 4096, 8192, 16384, 32768, 65536 };
92   int j;
93   for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
94     if (pow2[j] >= i) return pow2[j];
95   abort();  /* too big! */
96 }
97
98
99 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
100    RGB are averaged to grayscale, and the resulting value is treated as alpha.
101    Pass in the size of the pixmap; the size of the texture is returned
102    (it may be larger, since GL like powers of 2.)
103
104    We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
105    were drawn with antialiasing, that is preserved.
106  */
107 static void
108 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth,
109                    int *wP, int *hP)
110 {
111   Bool mipmap_p = True;
112   int ow = *wP;
113   int oh = *hP;
114   int w2 = to_pow2 (ow);
115   int h2 = to_pow2 (oh);
116   int x, y, max, scale;
117   XImage *image = 0;
118   unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
119   unsigned char *out = data;
120
121   /* If either dimension is larger than the supported size, reduce.
122      We still return the old size to keep the caller's math working,
123      but the texture itself will have fewer pixels in it.
124    */
125   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max);
126   scale = 1;
127   while (w2 > max || h2 > max)
128     {
129       w2 /= 2;
130       h2 /= 2;
131       scale *= 2;
132     }
133
134   /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
135      texture with 1 byte per pixel, the intensity value, we have
136      to use 2 bytes per pixel: solid white, and an alpha value.
137    */
138 # ifdef HAVE_JWZGLES
139 #  undef GL_INTENSITY
140 # endif
141
142 # ifdef GL_INTENSITY
143   GLuint iformat = GL_INTENSITY;
144   GLuint format  = GL_LUMINANCE;
145 # else
146   GLuint iformat = GL_LUMINANCE_ALPHA;
147   GLuint format  = GL_LUMINANCE_ALPHA;
148 # endif
149   GLuint type    = GL_UNSIGNED_BYTE;
150
151 # ifdef HAVE_XSHM_EXTENSION
152   Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
153   XShmSegmentInfo shm_info;
154 # endif /* HAVE_XSHM_EXTENSION */
155
156 # ifdef HAVE_XSHM_EXTENSION
157   if (use_shm)
158     {
159       image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info,
160                                  ow, oh);
161       if (image)
162         XShmGetImage (dpy, p, image, 0, 0, ~0L);
163       else
164         use_shm = False;
165     }
166 # endif /* HAVE_XSHM_EXTENSION */
167
168   if (!image)
169     image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
170
171 # ifdef HAVE_JWZGLES
172   /* This would work, but it's wasteful for no benefit. */
173   mipmap_p = False;
174 # endif
175
176 # ifdef DUMP_BITMAPS
177   fprintf (stderr, "\n");
178 # endif
179   for (y = 0; y < h2; y++) {
180     for (x = 0; x < w2; x++) {
181       /* Might be better to average a scale x scale square of source pixels,
182          but at the resolutions we're dealing with, this is probably good
183          enough. */
184       int sx = x * scale;
185       int sy = y * scale;
186       unsigned long pixel = (sx >= ow || sy >= oh ? 0 :
187                              XGetPixel (image, sx, sy));
188       /* instead of averaging all three channels, let's just use red,
189          and assume it was already grayscale. */
190       unsigned long r = pixel & visual->red_mask;
191       /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
192       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
193
194 # ifdef DUMP_BITMAPS
195       if (sx < ow && sy < oh)
196 #  ifdef HAVE_JWXYZ
197         fprintf (stderr, "%c", 
198                  r >= 0xFF000000 ? '#' : 
199                  r >= 0x88000000 ? '%' : 
200                  r ? '.' : ' ');
201 #  else
202         fprintf (stderr, "%c", 
203                  r >= 0xFF0000 ? '#' : 
204                  r >= 0x880000 ? '%' : 
205                  r ? '.' : ' ');
206 #  endif
207 # endif
208
209 # if 0  /* Debugging checkerboard */
210       if (sx < ow && sy < oh && (((sx / 4) & 1) ^ ((sy / 4) & 1)))
211         pixel = 0x3F;
212 # endif
213
214 # ifndef GL_INTENSITY
215       *out++ = 0xFF;  /* 2 bytes per pixel (luminance, alpha) */
216 # endif
217       *out++ = pixel;
218     }
219 # ifdef DUMP_BITMAPS
220     fprintf (stderr, "\n");
221 # endif
222   }
223
224 # ifdef HAVE_XSHM_EXTENSION
225   if (use_shm)
226     destroy_xshm_image (dpy, image, &shm_info);
227   else
228 # endif /* HAVE_XSHM_EXTENSION */
229     XDestroyImage (image);
230
231   image = 0;
232
233   if (mipmap_p)
234     gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
235   else
236     glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
237
238   {
239     char msg[100];
240     sprintf (msg, "texture font %s (%d x %d)",
241              mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
242              w2, h2);
243     check_gl_error (msg);
244   }
245
246   free (data);
247
248   *wP = w2 * scale;
249   *hP = h2 * scale;
250 }
251
252
253 /* Loads the font named by the X resource "res" and returns
254    a texture-font object.
255 */
256 texture_font_data *
257 load_texture_font (Display *dpy, char *res)
258 {
259   int screen = DefaultScreen (dpy);
260   char *font = get_string_resource (dpy, res, "Font");
261   const char *def1 = "-*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*";
262   const char *def2 = "-*-helvetica-medium-r-normal-*-*-140-*-*-*-*-*-*";
263   const char *def3 = "fixed";
264   XftFont *f = 0;
265   texture_font_data *data;
266   int cache_size = get_integer_resource (dpy, "texFontCacheSize", "Integer");
267
268   /* Hacks that draw a lot of different strings on the screen simultaneously,
269      like Star Wars, should set this to a larger value for performance. */
270   if (cache_size <= 0)
271     cache_size = 30;
272
273   if (!res || !*res) abort();
274
275   if (!strcmp (res, "fpsFont")) {  /* Kludge. */
276     def1 = "-*-courier-bold-r-normal-*-*-140-*-*-*-*-*-*";
277     cache_size = 0;  /* No need for a cache on FPS: already throttled. */
278   }
279
280   if (!font) font = strdup(def1);
281
282   f = XftFontOpenXlfd (dpy, screen, font);
283   if (!f && !!strcmp (font, def1))
284     {
285       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
286                progname, font, def1);
287       free (font);
288       font = strdup (def1);
289       f = XftFontOpenXlfd (dpy, screen, font);
290     }
291
292   if (!f && !!strcmp (font, def2))
293     {
294       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
295                progname, font, def2);
296       free (font);
297       font = strdup (def2);
298       f = XftFontOpenXlfd (dpy, screen, font);
299     }
300
301   if (!f && !!strcmp (font, def3))
302     {
303       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
304                progname, font, def3);
305       free (font);
306       font = strdup (def3);
307       f = XftFontOpenXlfd (dpy, screen, font);
308     }
309
310   if (!f)
311     {
312       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
313                progname, font);
314       abort();
315     }
316
317   free (font);
318   font = 0;
319
320   data = (texture_font_data *) calloc (1, sizeof(*data));
321   data->dpy = dpy;
322   data->xftfont = f;
323   data->cache_size = cache_size;
324
325   return data;
326 }
327
328
329 /* Measure the string, returning the overall metrics.
330    Newlines and tab stops are honored.
331    Any numbers inside [] will be rendered as a subscript.
332
333    The origin is at the origin of the first character, so subsequent
334    lines of a multi-line string look like descenders (below baseline).
335
336    If an XftDraw is supplied, render the string as well, at X,Y.
337    Positive Y is down (X11 style, not OpenGL style).
338  */
339 static void
340 iterate_texture_string (texture_font_data *data,
341                         const char *s,
342                         int draw_x, int draw_y,
343                         XftDraw *xftdraw, XftColor *xftcolor,
344                         XCharStruct *metrics_ret)
345 {
346   int line_height = data->xftfont->ascent + data->xftfont->descent;
347   int subscript_offset = line_height * 0.3;
348   const char *os = s;
349   Bool sub_p = False, osub_p = False;
350   int cw = 0, tabs = 0;
351   XCharStruct overall = { 0, };
352   int x  = 0, y  = 0;
353   int ox = x, oy = y;
354
355   while (1)
356     {
357       if (*s == 0 ||
358           *s == '\n' ||
359           *s == '\t' ||
360           (*s == '[' && isdigit(s[1])) ||
361           (*s == ']' && sub_p))
362         {
363           if (s != os)
364             {
365               XGlyphInfo e;
366               XCharStruct c;
367               int y2 = y;
368               if (sub_p) y2 += subscript_offset;
369
370               XftTextExtentsUtf8 (data->dpy, data->xftfont,
371                                   (FcChar8 *) os, (int) (s - os),
372                                   &e);
373               c.lbearing = -e.x;                /* XGlyphInfo to XCharStruct */
374               c.rbearing =  e.width  - e.x;
375               c.ascent   =  e.y;
376               c.descent  =  e.height - e.y;
377               c.width    =  e.xOff;
378
379 # undef MAX
380 # undef MIN
381 # define MAX(A,B) ((A)>(B)?(A):(B))
382 # define MIN(A,B) ((A)<(B)?(A):(B))
383               overall.ascent   = MAX (overall.ascent,   -y2 + c.ascent);
384               overall.descent  = MAX (overall.descent,   y2 + c.descent);
385               overall.lbearing = MIN (overall.lbearing, (x  + c.lbearing));
386               overall.rbearing = MAX (overall.rbearing,  x  + c.rbearing);
387               overall.width    = MAX (overall.width,     x  + c.width);
388               x += c.width;
389 # undef MAX
390 # undef MIN
391             }
392
393           if (*s == '\n')
394             {
395               x = 0;
396               y += line_height;
397               sub_p = False;
398             }
399           else if (*s == '\t')
400             {
401               if (! cw)
402                 {
403                   /* Measure "m" to determine tab width. */
404                   XGlyphInfo e;
405                   XftTextExtentsUtf8 (data->dpy, data->xftfont,
406                                       (FcChar8 *) "m", 1, &e);
407                   cw = e.xOff;
408                   if (cw <= 0) cw = 1;
409                   tabs = cw * 7;
410                 }
411               x = ((x + tabs) / tabs) * tabs;
412             }
413           else if (*s == '[' && isdigit(s[1]))
414             sub_p = True;
415           else if (*s == ']' && sub_p)
416             sub_p = False;
417
418           if (xftdraw && s != os)
419             XftDrawStringUtf8 (xftdraw, xftcolor, data->xftfont,
420                                draw_x + ox,
421                                draw_y +
422                                oy + (osub_p ? subscript_offset : 0),
423                                (FcChar8 *) os, (int) (s - os));
424           if (!*s) break;
425           os = s+1;
426           ox = x;
427           oy = y;
428           osub_p = sub_p;
429         }
430       s++;
431     }
432
433   if (metrics_ret)
434     *metrics_ret = overall;
435 }
436
437
438 /* Bounding box of the multi-line string, in pixels,
439    and overall ascent/descent of the font.
440  */
441 void
442 texture_string_metrics (texture_font_data *data, const char *s,
443                         XCharStruct *metrics_ret,
444                         int *ascent_ret, int *descent_ret)
445 {
446   if (metrics_ret)
447     iterate_texture_string (data, s, 0, 0, 0, 0, metrics_ret);
448   if (ascent_ret)  *ascent_ret  = data->xftfont->ascent;
449   if (descent_ret) *descent_ret = data->xftfont->descent;
450 }
451
452
453 /* Returns a cache entry for this string, with a valid texid.
454    If the returned entry has a string in it, the texture is valid.
455    Otherwise it is an empty entry waiting to be rendered.
456  */
457 static struct texfont_cache *
458 get_cache (texture_font_data *data, const char *string)
459 {
460   int count = 0;
461   texfont_cache *prev = 0, *prev2 = 0, *curr = 0, *next = 0;
462
463   if (data->cache)
464     for (prev2 = 0, prev = 0, curr = data->cache, next = curr->next;
465          curr;
466          prev2 = prev, prev = curr, curr = next,
467            next = (curr ? curr->next : 0), count++)
468       {
469         if (!strcmp (string, curr->string))
470           {
471             if (prev)
472               prev->next = next;       /* Unlink from list */
473             if (curr != data->cache)
474               {
475                 curr->next = data->cache;  /* Move to front */
476                 data->cache = curr;
477               }
478             return curr;
479           }
480       }
481
482   /* Made it to the end of the list without a hit.
483      If the cache is full, empty out the last one on the list,
484      and move it to the front.  Keep the texid.
485    */
486   if (count > data->cache_size)
487     {
488       if (!prev) abort();
489       free (prev->string);
490       prev->string     = 0;
491       prev->tex_width  = 0;
492       prev->tex_height = 0;
493       memset (&prev->extents, 0, sizeof(prev->extents));
494       if (prev2)
495         prev2->next = 0;
496       if (prev != data->cache)
497         prev->next = data->cache;
498       data->cache = prev;
499       return prev;
500     }
501
502   /* Not cached, and cache not full.  Add a new entry at the front,
503      and allocate a new texture for it.
504    */
505   curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
506   glGenTextures (1, &curr->texid);
507   curr->string = 0;
508   curr->next = data->cache;
509   data->cache = curr;
510
511   return curr;
512 }
513
514
515 /* Renders the given string into the prevailing texture.
516    Returns the metrics of the text, and size of the texture.
517  */
518 void
519 string_to_texture (texture_font_data *data, const char *string,
520                    XCharStruct *extents_ret,
521                    int *tex_width_ret, int *tex_height_ret)
522 {
523   Window window = RootWindow (data->dpy, 0);
524   Pixmap p;
525   XGCValues gcv;
526   GC gc;
527   XWindowAttributes xgwa;
528   XRenderColor rcolor;
529   XftColor xftcolor;
530   XftDraw *xftdraw;
531   int width, height;
532   XCharStruct overall;
533
534   /* Measure the string and create a Pixmap of the proper size.
535    */
536   XGetWindowAttributes (data->dpy, window, &xgwa);
537   iterate_texture_string (data, string, 0, 0, 0, 0, &overall);
538   width  = overall.rbearing - overall.lbearing;
539   height = overall.ascent   + overall.descent;
540   if (width  <= 0) width  = 1;
541   if (height <= 0) height = 1;
542   p = XCreatePixmap (data->dpy, window, width, height, xgwa.depth);
543
544   gcv.foreground = BlackPixelOfScreen (xgwa.screen);
545   gc = XCreateGC (data->dpy, p, GCForeground, &gcv);
546   XFillRectangle (data->dpy, p, gc, 0, 0, width, height);
547   XFreeGC (data->dpy, gc);
548
549   /* Render the string into the pixmap.
550    */
551   rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF;
552   XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap,
553                       &rcolor, &xftcolor);
554   xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap);
555   iterate_texture_string (data, string,
556                           -overall.lbearing, overall.ascent,
557                           xftdraw, &xftcolor, 0);
558   XftDrawDestroy (xftdraw);
559   XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor);
560
561   /* Copy the bits from the Pixmap into a texture, unless it's cached.
562    */
563   bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, 
564                      &width, &height);
565   XFreePixmap (data->dpy, p);
566
567   if (extents_ret)    *extents_ret    = overall;
568   if (tex_width_ret)  *tex_width_ret  = width;
569   if (tex_height_ret) *tex_height_ret = height;
570 }
571
572
573 /* Set the various OpenGL parameters for properly rendering things
574    with a texture generated by string_to_texture().
575  */
576 void
577 enable_texture_string_parameters (void)
578 {
579   glEnable (GL_TEXTURE_2D);
580
581   /* Texture-rendering parameters to make font pixmaps tolerable to look at.
582    */
583   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
584   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
585                    GL_LINEAR_MIPMAP_LINEAR);
586
587   /* LOD bias is part of OpenGL 1.4.
588      GL_EXT_texture_lod_bias has been present since the original iPhone.
589   */
590 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
591 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
592 # endif
593 # ifdef GL_TEXTURE_LOD_BIAS
594   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
595 # endif
596   clear_gl_error();  /* invalid enum on iPad 3 */
597
598   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
599   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
600
601   /* Don't write the transparent parts of the quad into the depth buffer. */
602   glAlphaFunc (GL_GREATER, 0.01);
603   glEnable (GL_ALPHA_TEST);
604   glEnable (GL_BLEND);
605   glDisable (GL_LIGHTING);
606   glDisable (GL_TEXTURE_GEN_S);
607   glDisable (GL_TEXTURE_GEN_T);
608   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
609 }
610
611
612 /* Draws the string in the scene at the origin.
613    Newlines and tab stops are honored.
614    Any numbers inside [] will be rendered as a subscript.
615    Assumes the font has been loaded as with load_texture_font().
616
617    The origin is at the origin of the first character, so subsequent
618    lines of a multi-line string are below that.
619  */
620 void
621 print_texture_string (texture_font_data *data, const char *string)
622 {
623   XCharStruct overall;
624   int tex_width, tex_height;
625   texfont_cache *cache;
626   GLint old_texture;
627
628   if (!*string) return;
629
630   clear_gl_error ();
631
632   /* Save the prevailing texture ID, and bind ours.  Restored at the end. */
633   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
634
635   cache = get_cache (data, string);
636
637   glBindTexture (GL_TEXTURE_2D, cache->texid);
638   check_gl_error ("texture font binding");
639
640   /* Measure the string and make a pixmap that will fit it,
641      unless it's cached.
642    */
643   if (cache->string)
644     {
645       overall    = data->cache->extents;
646       tex_width  = data->cache->tex_width;
647       tex_height = data->cache->tex_height;
648     }
649   else
650     string_to_texture (data, string, &overall, &tex_width, &tex_height);
651
652   {
653     int ofront, oblend;
654     Bool alpha_p, blend_p, light_p, gen_s_p, gen_t_p;
655     GLfloat omatrix[16];
656     GLfloat qx0, qy0, qx1, qy1;
657     GLfloat tx0, ty0, tx1, ty1;
658
659     /* If face culling is not enabled, draw front and back. */
660     Bool draw_back_face_p = !glIsEnabled (GL_CULL_FACE);
661
662     /* Save the prevailing texture environment, and set up ours.
663      */
664     glGetIntegerv (GL_FRONT_FACE, &ofront);
665     glGetIntegerv (GL_BLEND_DST, &oblend);
666     glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
667     blend_p = glIsEnabled (GL_BLEND);
668     alpha_p = glIsEnabled (GL_ALPHA_TEST);
669     light_p = glIsEnabled (GL_LIGHTING);
670     gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S);
671     gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T);
672
673     glPushMatrix();
674
675     glNormal3f (0, 0, 1);
676     glFrontFace (GL_CW);
677
678     glMatrixMode (GL_TEXTURE);
679     glLoadIdentity ();
680     glMatrixMode (GL_MODELVIEW);
681
682     enable_texture_string_parameters();
683
684     /* Draw a quad with that texture on it, possibly using a cached texture.
685        Position the XCharStruct origin at 0,0 in the scene.
686      */
687     qx0 =  overall.lbearing;
688     qy0 = -overall.descent;
689     qx1 =  overall.rbearing;
690     qy1 =  overall.ascent;
691
692     tx0 = 0;
693     ty1 = 0;
694     tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width;
695     ty0 = (overall.ascent + overall.descent)    / (GLfloat) tex_height;
696
697     glEnable (GL_CULL_FACE);
698     glFrontFace (GL_CCW);
699     glBegin (GL_QUADS);
700     glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
701     glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
702     glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
703     glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
704     glEnd();
705
706     if (draw_back_face_p)
707       {
708         glFrontFace (GL_CW);
709         glBegin (GL_QUADS);
710         glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
711         glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
712         glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
713         glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
714         glEnd();
715         glDisable (GL_CULL_FACE);
716       }
717
718     glPopMatrix();
719
720     /* Reset to the caller's texture environment.
721      */
722     glBindTexture (GL_TEXTURE_2D, old_texture);
723     glFrontFace (ofront);
724     if (!alpha_p) glDisable (GL_ALPHA_TEST);
725     if (!blend_p) glDisable (GL_BLEND);
726     if (light_p) glEnable (GL_LIGHTING);
727     if (gen_s_p) glEnable (GL_TEXTURE_GEN_S);
728     if (gen_t_p) glEnable (GL_TEXTURE_GEN_T);
729
730     glBlendFunc (GL_SRC_ALPHA, oblend);
731   
732     glMatrixMode (GL_TEXTURE);
733     glMultMatrixf (omatrix);
734     glMatrixMode (GL_MODELVIEW);
735
736     check_gl_error ("texture font print");
737
738     /* Store this string into the cache, unless that's where it came from.
739      */
740     if (!cache->string)
741       {
742         cache->string     = strdup (string);
743         cache->extents    = overall;
744         cache->tex_width  = tex_width;
745         cache->tex_height = tex_height;
746       }
747   }
748 }
749
750
751 /* Draws the string on the window at the given pixel position.
752    Newlines and tab stops are honored.
753    Any numbers inside [] will be rendered as a subscript.
754    Assumes the font has been loaded as with load_texture_font().
755
756    Position is 0 for center, 1 for top left, 2 for bottom left.
757  */
758 void
759 print_texture_label (Display *dpy,
760                      texture_font_data *data,
761                      int window_width, int window_height,
762                      int position,
763                      const char *string)
764 {
765   GLfloat color[4];
766
767   Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
768   Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
769   Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
770   Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
771   Bool cull_p  = glIsEnabled (GL_CULL_FACE);
772   Bool fog_p   = glIsEnabled (GL_FOG);
773   GLint ovp[4];
774
775 #  ifndef HAVE_JWZGLES
776   GLint opoly[2];
777   glGetIntegerv (GL_POLYGON_MODE, opoly);
778 #  endif
779
780   glGetIntegerv (GL_VIEWPORT, ovp);
781
782   glGetFloatv (GL_CURRENT_COLOR, color);
783
784   glEnable (GL_TEXTURE_2D);
785   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
786   glPolygonMode (GL_FRONT, GL_FILL);
787
788   glDisable (GL_TEXTURE_GEN_S);
789   glDisable (GL_TEXTURE_GEN_T);
790   glDisable (GL_CULL_FACE);
791   glDisable (GL_FOG);
792
793   glDisable (GL_DEPTH_TEST);
794
795   /* Each matrix mode has its own stack, so we need to push/pop
796      them separately.
797    */
798   glMatrixMode(GL_PROJECTION);
799   glPushMatrix();
800   {
801     glLoadIdentity();
802
803     glMatrixMode(GL_MODELVIEW);
804     glPushMatrix();
805     {
806       XCharStruct cs;
807       int ascent, descent;
808       int x, y, w, h, swap;
809       /* int rot = (int) current_device_rotation(); */
810       int rot = 0;  /* Since GL hacks rotate now */
811
812       glLoadIdentity();
813       glViewport (0, 0, window_width, window_height);
814       glOrtho (0, window_width, 0, window_height, -1, 1);
815
816       while (rot <= -180) rot += 360;
817       while (rot >   180) rot -= 360;
818
819       texture_string_metrics (data, string, &cs, &ascent, &descent);
820       h = cs.ascent + cs.descent;
821       w  = cs.width;
822
823 # ifdef USE_IPHONE
824       {
825         /* Size of the font is in points, so scale iOS pixels to points. */
826         GLfloat scale = ((window_width > window_height
827                           ? window_width : window_height)
828                          / 768.0);
829         if (scale < 1) scale = 1;
830
831         /* jwxyz-XLoadFont has already doubled the font size, to compensate
832            for physically smaller screens.  Undo that, since OpenGL hacks
833            use full-resolution framebuffers, unlike X11 hacks. */
834         scale /= 2;
835
836         window_width  /= scale;
837         window_height /= scale;
838         glScalef (scale, scale, scale);
839       }
840 # endif /* USE_IPHONE */
841
842       if (rot > 135 || rot < -135)              /* 180 */
843         {
844           glTranslatef (window_width, window_height, 0);
845           glRotatef (180, 0, 0, 1);
846         }
847       else if (rot > 45)                        /* 90 */
848         {
849           glTranslatef (window_width, 0, 0);
850           glRotatef (90, 0, 0, 1);
851           swap = window_width;
852           window_width = window_height;
853           window_height = swap;
854         }
855       else if (rot < -45)                       /* 270 */
856         {
857           glTranslatef(0, window_height, 0);
858           glRotatef (-90, 0, 0, 1);
859           swap = window_width;
860           window_width = window_height;
861           window_height = swap;
862         }
863
864       switch (position) {
865       case 0:                                   /* center */
866         x = (window_width  - w) / 2;
867         y = (window_height + h) / 2 - ascent;
868         break;
869       case 1:                                   /* top */
870         x = ascent;
871         y = window_height - ascent*2;
872         break;
873       case 2:                                   /* bottom */
874         x = ascent;
875         y = h;
876         break;
877       default:
878         abort();
879       }
880
881       glTranslatef (x, y, 0);
882
883       /* draw the text five times, to give it a border. */
884       {
885         const XPoint offsets[] = {{ -1, -1 },
886                                   { -1,  1 },
887                                   {  1,  1 },
888                                   {  1, -1 },
889                                   {  0,  0 }};
890         int i;
891
892         glColor3f (0, 0, 0);
893         for (i = 0; i < countof(offsets); i++)
894           {
895             if (offsets[i].x == 0)
896               glColor4fv (color);
897             glPushMatrix();
898             glTranslatef (offsets[i].x, offsets[i].y, 0);
899             print_texture_string (data, string);
900             glPopMatrix();
901           }
902       }
903     }
904     glPopMatrix();
905   }
906   glMatrixMode(GL_PROJECTION);
907   glPopMatrix();
908
909   if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
910   if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
911   if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
912   if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
913   if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
914   if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
915
916   glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
917
918 # ifndef HAVE_JWZGLES
919   glPolygonMode (GL_FRONT, opoly[0]);
920 # endif
921
922   glMatrixMode(GL_MODELVIEW);
923 }
924
925
926 #ifdef HAVE_JWXYZ
927 char *
928 texfont_unicode_character_name (texture_font_data *data, unsigned long uc)
929 {
930   Font fid = data->xftfont->xfont->fid;
931   return jwxyz_unicode_character_name (fid, uc);
932 }
933 #endif /* HAVE_JWXYZ */
934
935
936
937 /* Releases the font and texture.
938  */
939 void
940 free_texture_font (texture_font_data *data)
941 {
942   while (data->cache)
943     {
944       texfont_cache *next = data->cache->next;
945       glDeleteTextures (1, &data->cache->texid);
946       free (data->cache);
947       data->cache = next;
948     }
949   if (data->xftfont)
950     XftFontClose (data->dpy, data->xftfont);
951   free (data);
952 }