1 /* glxfonts, Copyright (c) 2001-2012 Jamie Zawinski <jwz@jwz.org>
2 * Loads X11 fonts for use with OpenGL.
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
12 * Draws 2D text over the GL scene, e.g., the FPS displays.
14 * There are two implementations here: one using glBitmap (the OpenGL 1.1 way)
15 * and one using textures via texfont.c (since OpenGLES doesn't have glBitmap).
29 /*# include <AGL/agl.h>*/
37 #endif /* HAVE_JWZGLES */
39 #include "resources.h"
45 #endif /* HAVE_GLBITMAP */
48 /* These are in xlock-gl.c */
49 extern void clear_gl_error (void);
50 extern void check_gl_error (const char *type);
53 extern char *progname;
56 #undef DEBUG /* Defining this causes check_gl_error() to be called inside
57 time-critical sections, which could slow things down (since
58 it might result in a round-trip, and stall of the pipeline.)
63 #define countof(x) (sizeof((x))/sizeof((*x)))
66 /* Width (and optionally height) of the string in pixels.
69 string_width (XFontStruct *f, const char *c, int *height_ret)
73 int h = f->ascent + f->descent;
76 int cc = *((unsigned char *) c);
79 if (x > max_w) max_w = x;
81 h += f->ascent + f->descent;
85 ? f->per_char[cc-f->min_char_or_byte2].width
86 : f->min_bounds.rbearing);
89 if (x > max_w) max_w = x;
90 if (height_ret) *height_ret = h;
98 /* Mostly lifted from the Mesa implementation of glXUseXFont(), since
99 Mac OS 10.6 no longer supports aglUseFont() which was their analog
100 of that. This code could be in libjwxyz instead, but we might as
101 well use the same text-drawing code on both X11 and Cocoa.
104 fill_bitmap (Display *dpy, Window win, GC gc,
105 unsigned int width, unsigned int height,
106 int x0, int y0, char c, GLubyte *bitmap)
112 pixmap = XCreatePixmap (dpy, win, 8*width, height, 1);
113 XSetForeground(dpy, gc, 0);
114 XFillRectangle (dpy, pixmap, gc, 0, 0, 8*width, height);
115 XSetForeground(dpy, gc, 1);
116 XDrawString (dpy, pixmap, gc, x0, y0, &c, 1);
118 image = XGetImage (dpy, pixmap, 0, 0, 8*width, height, 1, XYPixmap);
120 /* Fill the bitmap (X11 and OpenGL are upside down wrt each other). */
121 for (y = 0; y < height; y++)
122 for (x = 0; x < 8*width; x++)
123 if (XGetPixel (image, x, y))
124 bitmap[width*(height - y - 1) + x/8] |= (1 << (7 - (x % 8)));
126 XFreePixmap (dpy, pixmap);
127 XDestroyImage (image);
133 dump_bitmap (unsigned int width, unsigned int height, GLubyte *bitmap)
138 for (x = 0; x < 8*width; x++)
139 printf ("%o", 7 - (x % 8));
141 for (y = 0; y < height; y++)
144 for (x = 0; x < 8*width; x++)
145 putchar ((bitmap[width*(height - y - 1) + x/8] & (1 << (7 - (x % 8))))
148 for (x = 0; x < width; x++)
149 printf ("0x%02x, ", bitmap[width*(height - y - 1) + x]);
157 xscreensaver_glXUseXFont (Display *dpy, Font font,
158 int first, int count, int listbase)
160 Window win = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
164 unsigned long valuemask;
168 GLint swapbytes, lsbfirst, rowlength;
169 GLint skiprows, skippixels, alignment;
171 unsigned int max_width, max_height, max_bm_width, max_bm_height;
178 fs = XQueryFont (dpy, font);
181 /*gl_error (CC->gl_ctx, GL_INVALID_VALUE,
182 "Couldn't get font structure information");*/
187 /* Allocate a bitmap that can fit all characters. */
188 max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
189 max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
190 max_bm_width = (max_width + 7) / 8;
191 max_bm_height = max_height;
193 bm = (GLubyte *) malloc ((max_bm_width * max_bm_height) * sizeof (GLubyte));
196 /*gl_error (CC->gl_ctx, GL_OUT_OF_MEMORY,
197 "Couldn't allocate bitmap in glXUseXFont()");*/
202 /* Save the current packing mode for bitmaps. */
203 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &swapbytes);
204 glGetIntegerv (GL_UNPACK_LSB_FIRST, &lsbfirst);
205 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowlength);
206 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skiprows);
207 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skippixels);
208 glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
210 /* Enforce a standard packing mode which is compatible with
211 fill_bitmap() from above. This is actually the default mode,
212 except for the (non)alignment. */
213 glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
214 glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
215 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
216 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
217 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
218 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
220 clear_gl_error(); /* WTF? sometimes "invalid op" from glPixelStorei! */
223 pixmap = XCreatePixmap (dpy, win, 10, 10, 1);
224 values.foreground = 0;
225 values.background = 1;
226 values.font = fs->fid;
227 valuemask = GCForeground | GCBackground | GCFont;
228 gc = XCreateGC (dpy, pixmap, valuemask, &values);
229 XFreePixmap (dpy, pixmap);
232 /* Anti-aliasing of fonts looks like crap with 1-bit bitmaps.
233 It would be nice if we were using full-depth bitmaps, so
234 that the text showed up anti-aliased on screen, but
235 glBitmap() doesn't work that way. */
236 jwxyz_XSetAntiAliasing (dpy, gc, False);
239 for (i = 0; i < count; i++)
241 unsigned int width, height, bm_width, bm_height;
242 GLfloat x0, y0, dx, dy;
246 int list = listbase + i;
249 && (c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2))
250 ch = &fs->per_char[c-fs->min_char_or_byte2];
252 ch = &fs->max_bounds;
254 /* I'm not entirely clear on why this is necessary on OSX, but
255 without it, the characters are clipped. And it does not hurt
256 under real X11. -- jwz. */
260 /* glBitmap()'s parameters:
261 "Bitmap parameters xorig, yorig, width, and height are
262 computed from font metrics as descent-1, -lbearing,
263 rbearing-lbearing, and ascent+descent, respectively.
264 xmove is taken from the glyph's width metric, and
265 ymove is set to zero. Finally, the glyph's image is
266 converted to the appropriate format for glBitmap."
268 width = ch->rbearing - ch->lbearing;
269 height = ch->ascent + ch->descent;
271 y0 = ch->descent - 1;
275 /* X11's starting point. */
279 /* Round the width to a multiple of eight. We will use this also
280 for the pixmap for capturing the X11 font. This is slightly
281 inefficient, but it makes the OpenGL part real easy. */
282 bm_width = (width + 7) / 8;
285 glNewList (list, GL_COMPILE);
286 if ((c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2)
287 && (bm_width > 0) && (bm_height > 0))
289 memset (bm, '\0', bm_width * bm_height);
290 fill_bitmap (dpy, win, gc, bm_width, bm_height, x, y, c, bm);
291 glBitmap (width, height, x0, y0, dx, dy, bm);
293 printf ("width/height = %d/%d\n", width, height);
294 printf ("bm_width/bm_height = %d/%d\n", bm_width, bm_height);
295 dump_bitmap (bm_width, bm_height, bm);
299 glBitmap (0, 0, 0.0, 0.0, dx, dy, NULL);
304 XFreeFontInfo( NULL, fs, 0 );
307 /* Restore saved packing modes. */
308 glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
309 glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
310 glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
311 glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
312 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
313 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
315 check_gl_error ("xscreensaver_glXUseXFont");
320 /* Loads the font named by the X resource "res".
321 Returns an XFontStruct.
322 Also converts the font to a set of GL lists and returns the first list.
325 load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
329 const char *font = get_string_resource (dpy, res, "Font");
330 const char *def1 = "-*-helvetica-medium-r-normal-*-180-*";
331 const char *def2 = "fixed";
335 if (!res || !*res) abort();
336 if (!font_ret && !dlist_ret) abort();
338 if (!font) font = def1;
340 f = XLoadQueryFont(dpy, font);
341 if (!f && !!strcmp (font, def1))
343 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
344 progname, font, def1);
346 f = XLoadQueryFont(dpy, font);
349 if (!f && !!strcmp (font, def2))
351 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
352 progname, font, def2);
354 f = XLoadQueryFont(dpy, font);
359 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
365 first = f->min_char_or_byte2;
366 last = f->max_char_or_byte2;
372 *dlist_ret = glGenLists ((GLuint) last+1);
373 check_gl_error ("glGenLists");
374 xscreensaver_glXUseXFont (dpy, id, first, last-first+1,
382 #endif /* HAVE_GLBITMAP */
385 /* Draws the string on the window at the given pixel position.
386 Newlines and tab stops are honored.
387 Any numbers inside [] will be rendered as a subscript.
388 Assumes the font has been loaded as with load_font().
391 print_gl_string (Display *dpy,
392 # ifdef HAVE_GLBITMAP
393 XFontStruct *font, GLuint font_dlist,
395 texture_font_data *font_data,
397 int window_width, int window_height,
398 GLfloat x, GLfloat y,
400 Bool clear_background_p)
403 /* If window_width was specified, we're drawing ortho in pixel coordinates.
404 Otherwise, we're just dropping the text at the current position in the
405 scene, billboarded. */
406 Bool in_scene_p = (window_width == 0);
408 # ifdef HAVE_GLBITMAP
409 GLfloat line_height = font->ascent + font->descent;
410 int cw = string_width (font, "m", 0);
411 # else /* !HAVE_GLBITMAP */
413 int cw = texture_string_width (font_data, "m", &line_height);
414 # endif /* !HAVE_GLBITMAP */
416 GLfloat sub_shift = (line_height * 0.3);
422 # ifdef HAVE_GLBITMAP
423 /* Sadly, this causes a stall of the graphics pipeline (as would the
424 equivalent calls to glGet*.) But there's no way around this, short
425 of having each caller set up the specific display matrix we need
426 here, which would kind of defeat the purpose of centralizing this
429 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
430 GL_ENABLE_BIT | /* for various glDisable calls */
431 GL_CURRENT_BIT | /* for glColor3f() */
432 GL_LIST_BIT); /* for glListBase() */
434 check_gl_error ("glPushAttrib");
436 # else /* !HAVE_GLBITMAP */
437 Bool tex_p = glIsEnabled (GL_TEXTURE_2D);
438 Bool texs_p = glIsEnabled (GL_TEXTURE_GEN_S);
439 Bool text_p = glIsEnabled (GL_TEXTURE_GEN_T);
440 Bool light_p = glIsEnabled (GL_LIGHTING);
441 Bool blend_p = glIsEnabled (GL_BLEND);
442 Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
443 Bool cull_p = glIsEnabled (GL_CULL_FACE);
444 Bool fog_p = glIsEnabled (GL_FOG);
446 # ifndef HAVE_JWZGLES
448 glGetIntegerv (GL_POLYGON_MODE, opoly);
450 glGetIntegerv (GL_BLEND_DST, &oblend);
451 # endif /* !HAVE_GLBITMAP */
453 glGetFloatv (GL_CURRENT_COLOR, color);
455 for (c = string; *c; c++)
456 if (*c == '\n') lines++;
462 /* disable lighting and texturing when drawing bitmaps!
463 (glPopAttrib() restores these.)
465 # ifdef HAVE_GLBITMAP
466 glDisable (GL_TEXTURE_2D);
467 # else /* !HAVE_GLBITMAP */
468 glEnable (GL_TEXTURE_2D);
469 glDisable (GL_TEXTURE_GEN_S);
470 glDisable (GL_TEXTURE_GEN_T);
471 glPolygonMode (GL_FRONT, GL_FILL);
473 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
474 # endif /* !HAVE_GLBITMAP */
476 glDisable (GL_LIGHTING);
480 glDisable (GL_DEPTH_TEST);
481 glDisable (GL_CULL_FACE);
484 /* glPopAttrib() does not restore matrix changes, so we must
485 push/pop the matrix stacks to be non-intrusive there.
487 glMatrixMode(GL_PROJECTION);
491 check_gl_error ("glPushMatrix");
496 /* Each matrix mode has its own stack, so we need to push/pop
498 glMatrixMode(GL_MODELVIEW);
506 check_gl_error ("glPushMatrix");
511 double rot = current_device_rotation();
514 glOrtho (0, window_width, 0, window_height, -1, 1);
516 if (rot > 135 || rot < -135)
518 glTranslatef (window_width, window_height, 0);
519 glRotatef (180, 0, 0, 1);
523 glTranslatef (window_width, 0, 0);
524 glRotatef (90, 0, 0, 1);
525 y -= (window_height - window_width);
526 if (y < line_height * lines + 10)
527 y = line_height * lines + 10;
531 glTranslatef(0, window_height, 0);
532 glRotatef (-90, 0, 0, 1);
533 y -= (window_height - window_width);
534 if (y < line_height * lines + 10)
535 y = line_height * lines + 10;
539 check_gl_error ("glOrtho");
542 /* Let's always dropshadow the FPS and Title text. */
544 clear_background_p = True;
547 if (clear_background_p)
551 # ifdef HAVE_GLBITMAP
552 w = string_width (font, string, &h);
553 glRecti (x - font->descent,
555 x + w + 2*font->descent,
556 y + line_height - h - font->descent);
557 # else /* !HAVE_GLBITMAP */
559 int descent = line_height * 0.2;
560 if (descent < 2) descent = 2;
561 w = texture_string_width (font_data, string, &h);
562 glRecti (x - descent,
565 y + line_height - h - descent);
567 # endif /* !HAVE_GLBITMAP */
575 for (i = 0; i < strlen(string); i++)
577 unsigned char c = (unsigned char) string[i];
586 x2 = ((x2 + tabs) / tabs) * tabs; /* tab to tab stop */
589 else if (c == '[' && (isdigit (string[i+1])))
594 else if (c == ']' && sub_p)
601 /* outline font in black */
602 const XPoint offsets[] = {{ -1, -1 },
608 # ifndef HAVE_GLBITMAP
612 # endif /* !HAVE_GLBITMAP */
615 for (j = 0; j < countof(offsets); j++)
617 if (! clear_background_p)
618 j = countof(offsets)-1;
619 if (offsets[j].x == 0)
622 # ifdef HAVE_GLBITMAP
623 glRasterPos2f (x2 + offsets[j].x, y + offsets[j].y);
624 glCallList (font_dlist + (int)(c));
625 # else /* !HAVE_GLBITMAP */
627 glTranslatef (x2 + offsets[j].x, y + offsets[j].y, 0);
628 print_texture_string (font_data, s);
630 # endif /* !HAVE_GLBITMAP */
632 # ifdef HAVE_GLBITMAP
633 x2 += (font->per_char
634 ? font->per_char[c - font->min_char_or_byte2].width
635 : font->min_bounds.width);
636 # else /* !HAVE_GLBITMAP */
637 x2 += texture_string_width (font_data, s, 0);
638 # endif /* !HAVE_GLBITMAP */
642 check_gl_error ("print_gl_string");
647 glMatrixMode(GL_PROJECTION);
650 # ifdef HAVE_GLBITMAP
653 check_gl_error ("glPopAttrib");
655 # else /* !HAVE_GLBITMAP */
656 if (tex_p) glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
657 if (texs_p) glEnable (GL_TEXTURE_GEN_S);/*else glDisable (GL_TEXTURE_GEN_S);*/
658 if (text_p) glEnable (GL_TEXTURE_GEN_T);/*else glDisable (GL_TEXTURE_GEN_T);*/
659 if (blend_p) glEnable (GL_BLEND); else glDisable (GL_BLEND);
660 if (light_p) glEnable (GL_LIGHTING); /*else glDisable (GL_LIGHTING);*/
661 if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
662 if (cull_p) glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
663 if (fog_p) glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
664 # ifndef HAVE_JWZGLES
665 glPolygonMode (GL_FRONT, opoly[0]);
667 glBlendFunc (GL_SRC_ALPHA, oblend);
668 # endif /* !HAVE_GLBITMAP */
670 glMatrixMode(GL_MODELVIEW);