From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2015 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_COCOA
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       free (prev->string);
489       prev->string     = 0;
490       prev->tex_width  = 0;
491       prev->tex_height = 0;
492       memset (&prev->extents, 0, sizeof(prev->extents));
493       if (prev2)
494         prev2->next = 0;
495       if (prev != data->cache)
496         prev->next = data->cache;
497       data->cache = prev;
498       return prev;
499     }
500
501   /* Not cached, and cache not full.  Add a new entry at the front,
502      and allocate a new texture for it.
503    */
504   curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
505   glGenTextures (1, &curr->texid);
506   curr->string = 0;
507   curr->next = data->cache;
508   data->cache = curr;
509
510   return curr;
511 }
512
513
514 /* Renders the given string into the prevailing texture.
515    Returns the metrics of the text, and size of the texture.
516  */
517 void
518 string_to_texture (texture_font_data *data, const char *string,
519                    XCharStruct *extents_ret,
520                    int *tex_width_ret, int *tex_height_ret)
521 {
522   Window window = RootWindow (data->dpy, 0);
523   Pixmap p;
524   XGCValues gcv;
525   GC gc;
526   XWindowAttributes xgwa;
527   XRenderColor rcolor;
528   XftColor xftcolor;
529   XftDraw *xftdraw;
530   int width, height;
531   XCharStruct overall;
532
533   /* Measure the string and create a Pixmap of the proper size.
534    */
535   XGetWindowAttributes (data->dpy, window, &xgwa);
536   iterate_texture_string (data, string, 0, 0, 0, 0, &overall);
537   width  = overall.rbearing - overall.lbearing;
538   height = overall.ascent   + overall.descent;
539   if (width  <= 0) width  = 1;
540   if (height <= 0) height = 1;
541   p = XCreatePixmap (data->dpy, window, width, height, xgwa.depth);
542
543   gcv.foreground = BlackPixelOfScreen (xgwa.screen);
544   gc = XCreateGC (data->dpy, p, GCForeground, &gcv);
545   XFillRectangle (data->dpy, p, gc, 0, 0, width, height);
546   XFreeGC (data->dpy, gc);
547
548   /* Render the string into the pixmap.
549    */
550   rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF;
551   XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap,
552                       &rcolor, &xftcolor);
553   xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap);
554   iterate_texture_string (data, string,
555                           -overall.lbearing, overall.ascent,
556                           xftdraw, &xftcolor, 0);
557   XftDrawDestroy (xftdraw);
558   XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor);
559
560   /* Copy the bits from the Pixmap into a texture, unless it's cached.
561    */
562   bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, 
563                      &width, &height);
564   XFreePixmap (data->dpy, p);
565
566   if (extents_ret)    *extents_ret    = overall;
567   if (tex_width_ret)  *tex_width_ret  = width;
568   if (tex_height_ret) *tex_height_ret = height;
569 }
570
571
572 /* Set the various OpenGL parameters for properly rendering things
573    with a texture generated by string_to_texture().
574  */
575 void
576 enable_texture_string_parameters (void)
577 {
578   glEnable (GL_TEXTURE_2D);
579
580   /* Texture-rendering parameters to make font pixmaps tolerable to look at.
581    */
582   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
583   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
584                    GL_LINEAR_MIPMAP_LINEAR);
585
586   /* LOD bias is part of OpenGL 1.4.
587      GL_EXT_texture_lod_bias has been present since the original iPhone.
588   */
589 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
590 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
591 # endif
592 # ifdef GL_TEXTURE_LOD_BIAS
593   glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
594 # endif
595   clear_gl_error();  /* invalid enum on iPad 3 */
596
597   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
598   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
599
600   /* Don't write the transparent parts of the quad into the depth buffer. */
601   glAlphaFunc (GL_GREATER, 0.01);
602   glEnable (GL_ALPHA_TEST);
603   glEnable (GL_BLEND);
604   glDisable (GL_LIGHTING);
605   glDisable (GL_TEXTURE_GEN_S);
606   glDisable (GL_TEXTURE_GEN_T);
607   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
608 }
609
610
611 /* Draws the string in the scene at the origin.
612    Newlines and tab stops are honored.
613    Any numbers inside [] will be rendered as a subscript.
614    Assumes the font has been loaded as with load_texture_font().
615
616    The origin is at the origin of the first character, so subsequent
617    lines of a multi-line string are below that.
618  */
619 void
620 print_texture_string (texture_font_data *data, const char *string)
621 {
622   XCharStruct overall;
623   int tex_width, tex_height;
624   texfont_cache *cache;
625   GLint old_texture;
626
627   if (!*string) return;
628
629   clear_gl_error ();
630
631   /* Save the prevailing texture ID, and bind ours.  Restored at the end. */
632   glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
633
634   cache = get_cache (data, string);
635
636   glBindTexture (GL_TEXTURE_2D, cache->texid);
637   check_gl_error ("texture font binding");
638
639   /* Measure the string and make a pixmap that will fit it,
640      unless it's cached.
641    */
642   if (cache->string)
643     {
644       overall    = data->cache->extents;
645       tex_width  = data->cache->tex_width;
646       tex_height = data->cache->tex_height;
647     }
648   else
649     string_to_texture (data, string, &overall, &tex_width, &tex_height);
650
651   {
652     int ofront, oblend;
653     Bool alpha_p, blend_p, light_p, gen_s_p, gen_t_p;
654     GLfloat omatrix[16];
655     GLfloat qx0, qy0, qx1, qy1;
656     GLfloat tx0, ty0, tx1, ty1;
657
658     /* If face culling is not enabled, draw front and back. */
659     Bool draw_back_face_p = !glIsEnabled (GL_CULL_FACE);
660
661     /* Save the prevailing texture environment, and set up ours.
662      */
663     glGetIntegerv (GL_FRONT_FACE, &ofront);
664     glGetIntegerv (GL_BLEND_DST, &oblend);
665     glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
666     blend_p = glIsEnabled (GL_BLEND);
667     alpha_p = glIsEnabled (GL_ALPHA_TEST);
668     light_p = glIsEnabled (GL_LIGHTING);
669     gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S);
670     gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T);
671
672     glPushMatrix();
673
674     glNormal3f (0, 0, 1);
675     glFrontFace (GL_CW);
676
677     glMatrixMode (GL_TEXTURE);
678     glLoadIdentity ();
679     glMatrixMode (GL_MODELVIEW);
680
681     enable_texture_string_parameters();
682
683     /* Draw a quad with that texture on it, possibly using a cached texture.
684        Position the XCharStruct origin at 0,0 in the scene.
685      */
686     qx0 =  overall.lbearing;
687     qy0 = -overall.descent;
688     qx1 =  overall.rbearing;
689     qy1 =  overall.ascent;
690
691     tx0 = 0;
692     ty1 = 0;
693     tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width;
694     ty0 = (overall.ascent + overall.descent)    / (GLfloat) tex_height;
695
696     glEnable (GL_CULL_FACE);
697     glFrontFace (GL_CCW);
698     glBegin (GL_QUADS);
699     glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
700     glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
701     glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
702     glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
703     glEnd();
704
705     if (draw_back_face_p)
706       {
707         glFrontFace (GL_CW);
708         glBegin (GL_QUADS);
709         glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
710         glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
711         glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
712         glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
713         glEnd();
714         glDisable (GL_CULL_FACE);
715       }
716
717     glPopMatrix();
718
719     /* Reset to the caller's texture environment.
720      */
721     glBindTexture (GL_TEXTURE_2D, old_texture);
722     glFrontFace (ofront);
723     if (!alpha_p) glDisable (GL_ALPHA_TEST);
724     if (!blend_p) glDisable (GL_BLEND);
725     if (light_p) glEnable (GL_LIGHTING);
726     if (gen_s_p) glEnable (GL_TEXTURE_GEN_S);
727     if (gen_t_p) glEnable (GL_TEXTURE_GEN_T);
728
729     glBlendFunc (GL_SRC_ALPHA, oblend);
730   
731     glMatrixMode (GL_TEXTURE);
732     glMultMatrixf (omatrix);
733     glMatrixMode (GL_MODELVIEW);
734
735     check_gl_error ("texture font print");
736
737     /* Store this string into the cache, unless that's where it came from.
738      */
739     if (!cache->string)
740       {
741         cache->string     = strdup (string);
742         cache->extents    = overall;
743         cache->tex_width  = tex_width;
744         cache->tex_height = tex_height;
745       }
746   }
747 }
748
749
750 /* Draws the string on the window at the given pixel position.
751    Newlines and tab stops are honored.
752    Any numbers inside [] will be rendered as a subscript.
753    Assumes the font has been loaded as with load_texture_font().
754
755    Position is 0 for center, 1 for top left, 2 for bottom left.
756  */
757 void
758 print_texture_label (Display *dpy,
759                      texture_font_data *data,
760                      int window_width, int window_height,
761                      int position,
762                      const char *string)
763 {
764   GLfloat color[4];
765
766   Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
767   Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
768   Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
769   Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
770   Bool cull_p  = glIsEnabled (GL_CULL_FACE);
771   Bool fog_p   = glIsEnabled (GL_FOG);
772   GLint ovp[4];
773
774 #  ifndef HAVE_JWZGLES
775   GLint opoly[2];
776   glGetIntegerv (GL_POLYGON_MODE, opoly);
777 #  endif
778
779   glGetIntegerv (GL_VIEWPORT, ovp);
780
781   glGetFloatv (GL_CURRENT_COLOR, color);
782
783   glEnable (GL_TEXTURE_2D);
784   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
785   glPolygonMode (GL_FRONT, GL_FILL);
786
787   glDisable (GL_TEXTURE_GEN_S);
788   glDisable (GL_TEXTURE_GEN_T);
789   glDisable (GL_CULL_FACE);
790   glDisable (GL_FOG);
791
792   glDisable (GL_DEPTH_TEST);
793
794   /* Each matrix mode has its own stack, so we need to push/pop
795      them separately.
796    */
797   glMatrixMode(GL_PROJECTION);
798   glPushMatrix();
799   {
800     glLoadIdentity();
801
802     glMatrixMode(GL_MODELVIEW);
803     glPushMatrix();
804     {
805       XCharStruct cs;
806       int ascent, descent;
807       int x, y, w, h, swap;
808       int rot = (int) current_device_rotation();
809
810       glLoadIdentity();
811       glViewport (0, 0, window_width, window_height);
812       glOrtho (0, window_width, 0, window_height, -1, 1);
813
814       while (rot <= -180) rot += 360;
815       while (rot >   180) rot -= 360;
816
817       texture_string_metrics (data, string, &cs, &ascent, &descent);
818       h = cs.ascent + cs.descent;
819       w  = cs.width;
820
821 # ifdef USE_IPHONE
822       {
823         /* Size of the font is in points, so scale iOS pixels to points. */
824         GLfloat scale = window_width / 768.0;
825         if (scale < 1) scale = 1;
826
827         /* jwxyz-XLoadFont has already doubled the font size, to compensate
828            for physically smaller screens.  Undo that, since OpenGL hacks
829            use full-resolution framebuffers, unlike X11 hacks. */
830         scale /= 2;
831
832         window_width  /= scale;
833         window_height /= scale;
834         glScalef (scale, scale, scale);
835       }
836 # endif /* USE_IPHONE */
837
838       if (rot > 135 || rot < -135)              /* 180 */
839         {
840           glTranslatef (window_width, window_height, 0);
841           glRotatef (180, 0, 0, 1);
842         }
843       else if (rot > 45)                        /* 90 */
844         {
845           glTranslatef (window_width, 0, 0);
846           glRotatef (90, 0, 0, 1);
847           swap = window_width;
848           window_width = window_height;
849           window_height = swap;
850         }
851       else if (rot < -45)                       /* 270 */
852         {
853           glTranslatef(0, window_height, 0);
854           glRotatef (-90, 0, 0, 1);
855           swap = window_width;
856           window_width = window_height;
857           window_height = swap;
858         }
859
860       switch (position) {
861       case 0:                                   /* center */
862         x = (window_width  - w) / 2;
863         y = (window_height + h) / 2 - ascent;
864         break;
865       case 1:                                   /* top */
866         x = ascent;
867         y = window_height - ascent*2;
868         break;
869       case 2:                                   /* bottom */
870         x = ascent;
871         y = h;
872         break;
873       default:
874         abort();
875       }
876
877       glTranslatef (x, y, 0);
878
879       /* draw the text five times, to give it a border. */
880       {
881         const XPoint offsets[] = {{ -1, -1 },
882                                   { -1,  1 },
883                                   {  1,  1 },
884                                   {  1, -1 },
885                                   {  0,  0 }};
886         int i;
887
888         glColor3f (0, 0, 0);
889         for (i = 0; i < countof(offsets); i++)
890           {
891             if (offsets[i].x == 0)
892               glColor4fv (color);
893             glPushMatrix();
894             glTranslatef (offsets[i].x, offsets[i].y, 0);
895             print_texture_string (data, string);
896             glPopMatrix();
897           }
898       }
899     }
900     glPopMatrix();
901   }
902   glMatrixMode(GL_PROJECTION);
903   glPopMatrix();
904
905   if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
906   if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
907   if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
908   if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
909   if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
910   if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
911
912   glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
913
914 # ifndef HAVE_JWZGLES
915   glPolygonMode (GL_FRONT, opoly[0]);
916 # endif
917
918   glMatrixMode(GL_MODELVIEW);
919 }
920
921
922 /* Releases the font and texture.
923  */
924 void
925 free_texture_font (texture_font_data *data)
926 {
927   while (data->cache)
928     {
929       texfont_cache *next = data->cache->next;
930       glDeleteTextures (1, &data->cache->texid);
931       free (data->cache);
932       data->cache = next;
933     }
934   if (data->xftfont)
935     XftFontClose (data->dpy, data->xftfont);
936   free (data);
937 }