From http://www.jwz.org/xscreensaver/xscreensaver-5.32.tar.gz
[xscreensaver] / hacks / glx / texfont.c
1 /* texfonts, Copyright (c) 2005-2014 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   int width, height;
69   int width2, height2;
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 # error XX
158   if (use_shm)
159     {
160       image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info,
161                                  ow, oh);
162       if (image)
163         XShmGetImage (dpy, p, image, 0, 0, ~0L);
164       else
165         use_shm = False;
166     }
167 # endif /* HAVE_XSHM_EXTENSION */
168
169   if (!image)
170     image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
171
172 # ifdef HAVE_JWZGLES
173   /* This would work, but it's wasteful for no benefit. */
174   mipmap_p = False;
175 # endif
176
177   for (y = 0; y < h2; y++)
178     for (x = 0; x < w2; x++) {
179       /* Might be better to average a scale x scale square of source pixels,
180          but at the resolutions we're dealing with, this is probably good
181          enough. */
182       int sx = x * scale;
183       int sy = y * scale;
184       unsigned long pixel = (sx >= ow || sy >= oh ? 0 :
185                              XGetPixel (image, sx, sy));
186       /* instead of averaging all three channels, let's just use red,
187          and assume it was already grayscale. */
188       unsigned long r = pixel & visual->red_mask;
189       /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
190       pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
191 # ifndef GL_INTENSITY
192       *out++ = 0xFF;  /* 2 bytes per pixel (luminance, alpha) */
193 # endif
194       *out++ = pixel;
195     }
196
197 # ifdef HAVE_XSHM_EXTENSION
198   if (use_shm)
199     destroy_xshm_image (dpy, image, &shm_info);
200   else
201 # endif /* HAVE_XSHM_EXTENSION */
202     XDestroyImage (image);
203
204   image = 0;
205
206   if (mipmap_p)
207     gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
208   else
209     glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
210
211   {
212     char msg[100];
213     sprintf (msg, "texture font %s (%d x %d)",
214              mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
215              w2, h2);
216     check_gl_error (msg);
217   }
218
219   free (data);
220
221   *wP = w2 * scale;
222   *hP = h2 * scale;
223 }
224
225
226 /* Loads the font named by the X resource "res" and returns
227    a texture-font object.
228 */
229 texture_font_data *
230 load_texture_font (Display *dpy, char *res)
231 {
232   int screen = DefaultScreen (dpy);
233   char *font = get_string_resource (dpy, res, "Font");
234   const char *def1 = "-*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*";
235   const char *def2 = "-*-helvetica-medium-r-normal-*-*-140-*-*-*-*-*-*";
236   const char *def3 = "fixed";
237   XftFont *f = 0;
238   texture_font_data *data;
239   int cache_size = get_integer_resource (dpy, "texFontCacheSize", "Integer");
240
241   /* Hacks that draw a lot of different strings on the screen simultaneously,
242      like Star Wars, should set this to a larger value for performance. */
243   if (cache_size <= 0)
244     cache_size = 30;
245
246   if (!res || !*res) abort();
247
248   if (!strcmp (res, "fpsFont")) {  /* Kludge. */
249     def1 = "-*-courier-bold-r-normal-*-*-140-*-*-*-*-*-*";
250     cache_size = 0;  /* No need for a cache on FPS: already throttled. */
251   }
252
253   if (!font) font = strdup(def1);
254
255   f = XftFontOpenXlfd (dpy, screen, font);
256   if (!f && !!strcmp (font, def1))
257     {
258       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
259                progname, font, def1);
260       free (font);
261       font = strdup (def1);
262       f = XftFontOpenXlfd (dpy, screen, font);
263     }
264
265   if (!f && !!strcmp (font, def2))
266     {
267       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
268                progname, font, def2);
269       free (font);
270       font = strdup (def2);
271       f = XftFontOpenXlfd (dpy, screen, font);
272     }
273
274   if (!f && !!strcmp (font, def3))
275     {
276       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
277                progname, font, def3);
278       free (font);
279       font = strdup (def3);
280       f = XftFontOpenXlfd (dpy, screen, font);
281     }
282
283   if (!f)
284     {
285       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
286                progname, font);
287       abort();
288     }
289
290   free (font);
291   font = 0;
292
293   data = (texture_font_data *) calloc (1, sizeof(*data));
294   data->dpy = dpy;
295   data->xftfont = f;
296   data->cache_size = cache_size;
297
298   return data;
299 }
300
301
302 /* Measure the string, or render it, depending on whether the XftDraw
303    is supplied.
304  */
305 static int
306 iterate_texture_string (texture_font_data *data,
307                         const char *s, 
308                         XftDraw *xftdraw, XftColor *xftcolor,
309                         int x, int y,
310                         int *height_ret)
311 {
312   int line_height = data->xftfont->ascent + data->xftfont->descent;
313   int left = x;
314   int max_x, ox, oy;
315   const char *os = s;
316   Bool sub_p = False, osub_p = False;
317   int cw = 0, tabs = 0;
318   XGlyphInfo extents;
319
320   y += line_height;
321   max_x = x;
322   ox = x;
323   oy = y;
324
325   while (1)
326     {
327       if (*s == 0 ||
328           *s == '\n' ||
329           *s == '\t' ||
330           (*s == '[' && isdigit(s[1])) ||
331           (*s == ']' && sub_p))
332         {
333           if (s == os)
334             extents.xOff = 0;
335           else
336             XftTextExtentsUtf8 (data->dpy, data->xftfont,
337                                 (FcChar8 *) os, (int) (s - os),
338                                 &extents);
339           x += extents.xOff;
340           if (x > max_x)
341             max_x = x;
342
343           if (*s == '\n')
344             {
345               x = left;
346               y += line_height;
347               sub_p = False;
348             }
349           else if (*s == '\t')
350             {
351               if (! cw)
352                 {
353                   /* Measure "m" to determine tab width. */
354                   XftTextExtentsUtf8 (data->dpy, data->xftfont,
355                                       (FcChar8 *) "m", 1, &extents);
356                   cw = extents.xOff;
357                   if (cw <= 0) cw = 1;
358                   tabs = cw * 7;
359                 }
360               x = ((x + tabs) / tabs) * tabs;
361             }
362           else if (*s == '[' && isdigit(s[1]))
363             sub_p = True;
364           else if (*s == ']' && sub_p)
365             sub_p = False;
366
367           if (xftdraw && s != os)
368             XftDrawStringUtf8 (xftdraw, xftcolor, data->xftfont,
369                                ox, 
370                                oy + (int) (osub_p ? line_height * 0.3 : 0),
371                                (FcChar8 *) os, (int) (s - os));
372           if (!*s) break;
373           os = s+1;
374           ox = x;
375           oy = y;
376           osub_p = sub_p;
377         }
378       s++;
379     }
380
381   if (height_ret)
382     *height_ret = y;
383   return max_x;
384 }
385
386
387 /* Bounding box of the multi-line string, in pixels.
388  */
389 int
390 texture_string_width (texture_font_data *data, const char *s, int *height_ret)
391 {
392   return iterate_texture_string (data, s, 0, 0, 0, 0, height_ret);
393 }
394
395
396 static struct texfont_cache *
397 get_cache (texture_font_data *data, const char *string)
398 {
399   int count = 0;
400   texfont_cache *prev = 0, *prev2 = 0, *curr = 0, *next = 0;
401
402   if (data->cache)
403     for (prev2 = 0, prev = 0, curr = data->cache, next = curr->next;
404          curr;
405          prev2 = prev, prev = curr, curr = next,
406            next = (curr ? curr->next : 0), count++)
407       {
408         if (!strcmp (string, curr->string))
409           {
410             if (prev)
411               prev->next = next;       /* Unlink from list */
412             if (curr != data->cache)
413               {
414                 curr->next = data->cache;  /* Move to front */
415                 data->cache = curr;
416               }
417             return curr;
418           }
419       }
420
421   /* Made it to the end of the list without a hit.
422      If the cache is full, empty out the last one on the list,
423      and move it to the front.  Keep the texid.
424    */
425   if (count > data->cache_size)
426     {
427       free (prev->string);
428       prev->string  = 0;
429       prev->width   = 0;
430       prev->height  = 0;
431       prev->width2  = 0;
432       prev->height2 = 0;
433       if (prev2)
434         prev2->next = 0;
435       if (prev != data->cache)
436         prev->next = data->cache;
437       data->cache = prev;
438       return prev;
439     }
440
441   /* Not cached, and cache not full.  Add a new entry at the front,
442      and allocate a new texture for it.
443    */
444   curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
445   glGenTextures (1, &curr->texid);
446   curr->string = 0;
447   curr->next = data->cache;
448   data->cache = curr;
449
450   return curr;
451 }
452
453
454 /* Draws the string in the scene at the origin.
455    Newlines and tab stops are honored.
456    Any numbers inside [] will be rendered as a subscript.
457    Assumes the font has been loaded as with load_texture_font().
458  */
459 void
460 print_texture_string (texture_font_data *data, const char *string)
461 {
462   int line_height = data->xftfont->ascent + data->xftfont->descent;
463   int margin = line_height * 0.35;
464   int width, height;
465   int width2, height2;
466   XWindowAttributes xgwa;
467   Pixmap p = 0;
468   texfont_cache *cache;
469
470   if (!*string) return;
471
472   cache = get_cache (data, string);
473
474   /* Measure the string and make a pixmap that will fit it,
475      unless it's cached.
476    */
477   if (cache->string)
478     {
479       width   = data->cache->width;
480       height  = data->cache->height;
481     }
482   else
483     {
484       Window window = RootWindow (data->dpy, 0);
485       XGCValues gcv;
486       GC gc;
487
488       XGetWindowAttributes (data->dpy, window, &xgwa);
489       width = iterate_texture_string (data, string, 0, 0, 0, 0, &height);
490       p = XCreatePixmap (data->dpy, window, 
491                          width  + margin*2,
492                          height + margin*2,
493                          xgwa.depth);
494       gcv.foreground = BlackPixelOfScreen (xgwa.screen);
495       gc = XCreateGC (data->dpy, p, GCForeground, &gcv);
496       XFillRectangle (data->dpy, p, gc, 0, 0, 
497                       width  + margin*2,
498                       height + margin*2);
499       XFreeGC (data->dpy, gc);
500     }
501
502   /* Draw the string into the pixmap, unless it's cached.
503    */
504   if (!cache->string)
505     {
506       XRenderColor rcolor;
507       XftColor xftcolor;
508       XftDraw *xftdraw;
509       rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF;
510       XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap,
511                           &rcolor, &xftcolor);
512       xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap);
513       iterate_texture_string (data, string, xftdraw, &xftcolor,
514                               margin, 0, 0);
515       XftDrawDestroy (xftdraw);
516       XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor);
517     }
518
519   {
520     GLint old_texture;
521     int ofront, oblend;
522     Bool alpha_p, blend_p;
523     GLfloat omatrix[16];
524     GLfloat qx0, qy0, qx1, qy1;
525     GLfloat tx0, ty0, tx1, ty1;
526
527     /* Save the prevailing texture environment, and set up ours.
528      */
529     glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
530     glGetIntegerv (GL_FRONT_FACE, &ofront);
531     glGetIntegerv (GL_BLEND_DST, &oblend);
532     glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
533     blend_p = glIsEnabled (GL_BLEND);
534     alpha_p = glIsEnabled (GL_ALPHA_TEST);
535
536     clear_gl_error ();
537
538     glPushMatrix();
539
540     glNormal3f (0, 0, 1);
541     glFrontFace (GL_CW);
542
543     glMatrixMode (GL_TEXTURE);
544     glLoadIdentity ();
545     glMatrixMode (GL_MODELVIEW);
546
547     glBindTexture (GL_TEXTURE_2D, cache->texid);
548     check_gl_error ("texture font binding");
549
550     glEnable(GL_TEXTURE_2D);
551
552     /* Copy the bits from the Pixmap into a texture, unless it's cached.
553      */
554     if (cache->string)
555       {
556         width2  = data->cache->width2;
557         height2 = data->cache->height2;
558         if (p) abort();
559       }
560     else
561       {
562         width2  = width  + margin*2;
563         height2 = height + margin*2;
564         bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth,
565                            &width2, &height2);
566         XFreePixmap (data->dpy, p);
567       }
568
569
570     /* Texture-rendering parameters to make font pixmaps tolerable to look at.
571      */
572
573     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
574     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
575                      GL_LINEAR_MIPMAP_LINEAR);
576
577     /* LOD bias is part of OpenGL 1.4.
578        GL_EXT_texture_lod_bias has been present since the original iPhone.
579      */
580 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
581 #   define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
582 # endif
583 # ifdef GL_TEXTURE_LOD_BIAS
584     glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
585 # endif
586     clear_gl_error();  /* invalid enum on iPad 3 */
587
588     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
589     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
590
591     /* Don't write the transparent parts of the quad into the depth buffer. */
592     glAlphaFunc (GL_GREATER, 0.01);
593     glEnable (GL_ALPHA_TEST);
594     glEnable (GL_BLEND);
595     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
596
597     /* Draw a quad with that texture on it, possibly using a cached texture.
598      */
599     qx0 = -margin;
600     qy0 = line_height + margin;
601     qx1 = width + margin;
602     qy1 = line_height - height - margin;
603
604     tx0 = 0;
605     ty0 = 0;
606     tx1 = (width  + margin*2) / (GLfloat) width2;
607     ty1 = (height + margin*2) / (GLfloat) height2;
608
609     glBegin (GL_QUADS);
610     glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
611     glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
612     glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
613     glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
614     glEnd();
615
616     glPopMatrix();
617
618     /* Reset to the caller's texture environment.
619      */
620     glBindTexture (GL_TEXTURE_2D, old_texture);
621     glFrontFace (ofront);
622     if (!alpha_p) glDisable (GL_ALPHA_TEST);
623     if (!blend_p) glDisable (GL_BLEND);
624     glBlendFunc (GL_SRC_ALPHA, oblend);
625   
626     glMatrixMode (GL_TEXTURE);
627     glMultMatrixf (omatrix);
628     glMatrixMode (GL_MODELVIEW);
629
630     check_gl_error ("texture font print");
631
632     /* Store this string into the cache, unless that's where it came from.
633      */
634     if (!cache->string)
635       {
636         cache->string  = strdup (string);
637         cache->width   = width;
638         cache->height  = height;
639         cache->width2  = width2;
640         cache->height2 = height2;
641       }
642   }
643 }
644
645
646 /* Draws the string on the window at the given pixel position.
647    Newlines and tab stops are honored.
648    Any numbers inside [] will be rendered as a subscript.
649    Assumes the font has been loaded as with load_texture_font().
650
651    Position is 0 for center, 1 for top left, 2 for bottom left.
652  */
653 void
654 print_texture_label (Display *dpy,
655                      texture_font_data *data,
656                      int window_width, int window_height,
657                      int position,
658                      const char *string)
659 {
660   GLfloat color[4];
661
662   Bool tex_p   = glIsEnabled (GL_TEXTURE_2D);
663   Bool texs_p  = glIsEnabled (GL_TEXTURE_GEN_S);
664   Bool text_p  = glIsEnabled (GL_TEXTURE_GEN_T);
665   Bool light_p = glIsEnabled (GL_LIGHTING);
666   Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
667   Bool cull_p  = glIsEnabled (GL_CULL_FACE);
668   Bool fog_p   = glIsEnabled (GL_FOG);
669   GLint ovp[4];
670
671 #  ifndef HAVE_JWZGLES
672   GLint opoly[2];
673   glGetIntegerv (GL_POLYGON_MODE, opoly);
674 #  endif
675
676   glGetIntegerv (GL_VIEWPORT, ovp);
677
678   glGetFloatv (GL_CURRENT_COLOR, color);
679
680   glEnable (GL_TEXTURE_2D);
681   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
682   glPolygonMode (GL_FRONT, GL_FILL);
683
684   glDisable (GL_TEXTURE_GEN_S);
685   glDisable (GL_TEXTURE_GEN_T);
686   glDisable (GL_LIGHTING);
687   glDisable (GL_CULL_FACE);
688   glDisable (GL_FOG);
689
690   glDisable (GL_DEPTH_TEST);
691
692   /* Each matrix mode has its own stack, so we need to push/pop
693      them separately.
694    */
695   glMatrixMode(GL_PROJECTION);
696   glPushMatrix();
697   {
698     glLoadIdentity();
699
700     glMatrixMode(GL_MODELVIEW);
701     glPushMatrix();
702     {
703       int x, y, w, h, lh, swap;
704       int rot = (int) current_device_rotation();
705
706       glLoadIdentity();
707       glViewport (0, 0, window_width, window_height);
708       glOrtho (0, window_width, 0, window_height, -1, 1);
709
710       while (rot <= -180) rot += 360;
711       while (rot >   180) rot -= 360;
712
713       lh = texture_string_width (data, "M", 0);
714       w = texture_string_width (data, string, &h);
715
716 # ifdef USE_IPHONE
717       /* Size of the font is in points, so scale iOS pixels to points. */
718       {
719         GLfloat scale = 1;
720         scale = window_width / 768.0;
721         if (scale < 1) scale = 1;
722
723         /* jwxyz-XLoadFont has already doubled the font size, to compensate
724            for physically smaller screens.  Undo that, since OpenGL hacks
725            use full-resolution framebuffers, unlike X11 hacks. */
726         scale /= 2;
727
728         window_width  /= scale;
729         window_height /= scale;
730         glScalef (scale, scale, scale);
731       }
732 # endif /* USE_IPHONE */
733
734       if (rot > 135 || rot < -135)              /* 180 */
735         {
736           glTranslatef (window_width, window_height, 0);
737           glRotatef (180, 0, 0, 1);
738         }
739       else if (rot > 45)                        /* 90 */
740         {
741           glTranslatef (window_width, 0, 0);
742           glRotatef (90, 0, 0, 1);
743           swap = window_width;
744           window_width = window_height;
745           window_height = swap;
746         }
747       else if (rot < -45)                       /* 270 */
748         {
749           glTranslatef(0, window_height, 0);
750           glRotatef (-90, 0, 0, 1);
751           swap = window_width;
752           window_width = window_height;
753           window_height = swap;
754         }
755
756       switch (position) {
757       case 0:                                   /* center */
758         x = (window_width  - w) / 2;
759         y = (window_height - h) / 2;
760         break;
761       case 1:                                   /* top */
762         x = lh;
763         y = window_height - lh * 2;
764         break;
765       case 2:                                   /* bottom */
766         x = lh;
767         y = h - lh;
768         break;
769       default:
770         abort();
771       }
772
773       glTranslatef (x, y, 0);
774
775       /* draw the text five times, to give it a border. */
776       {
777         const XPoint offsets[] = {{ -1, -1 },
778                                   { -1,  1 },
779                                   {  1,  1 },
780                                   {  1, -1 },
781                                   {  0,  0 }};
782         int i;
783
784         glColor3f (0, 0, 0);
785         for (i = 0; i < countof(offsets); i++)
786           {
787             if (offsets[i].x == 0)
788               glColor4fv (color);
789             glPushMatrix();
790             glTranslatef (offsets[i].x, offsets[i].y, 0);
791             print_texture_string (data, string);
792             glPopMatrix();
793           }
794       }
795     }
796     glPopMatrix();
797   }
798   glMatrixMode(GL_PROJECTION);
799   glPopMatrix();
800
801   if (tex_p)   glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
802   if (texs_p)  glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/
803   if (text_p)  glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/
804   if (light_p) glEnable (GL_LIGHTING); /*else glDisable (GL_LIGHTING);*/
805   if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
806   if (cull_p)  glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
807   if (fog_p)   glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
808
809   glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
810
811 # ifndef HAVE_JWZGLES
812   glPolygonMode (GL_FRONT, opoly[0]);
813 # endif
814
815   glMatrixMode(GL_MODELVIEW);
816 }
817
818
819 /* Releases the font and texture.
820  */
821 void
822 free_texture_font (texture_font_data *data)
823 {
824   while (data->cache)
825     {
826       texfont_cache *next = data->cache->next;
827       glDeleteTextures (1, &data->cache->texid);
828       free (data->cache);
829       data->cache = next;
830     }
831   if (data->xftfont)
832     XftFontClose (data->dpy, data->xftfont);
833   free (data);
834 }