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