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.)
62 /* Width (and optionally height) of the string in pixels.
65 string_width (XFontStruct *f, const char *c, int *height_ret)
69 int h = f->ascent + f->descent;
72 int cc = *((unsigned char *) c);
75 if (x > max_w) max_w = x;
77 h += f->ascent + f->descent;
81 ? f->per_char[cc-f->min_char_or_byte2].width
82 : f->min_bounds.rbearing);
85 if (x > max_w) max_w = x;
86 if (height_ret) *height_ret = h;
94 /* Mostly lifted from the Mesa implementation of glXUseXFont(), since
95 Mac OS 10.6 no longer supports aglUseFont() which was their analog
96 of that. This code could be in libjwxyz instead, but we might as
97 well use the same text-drawing code on both X11 and Cocoa.
100 fill_bitmap (Display *dpy, Window win, GC gc,
101 unsigned int width, unsigned int height,
102 int x0, int y0, char c, GLubyte *bitmap)
108 pixmap = XCreatePixmap (dpy, win, 8*width, height, 1);
109 XSetForeground(dpy, gc, 0);
110 XFillRectangle (dpy, pixmap, gc, 0, 0, 8*width, height);
111 XSetForeground(dpy, gc, 1);
112 XDrawString (dpy, pixmap, gc, x0, y0, &c, 1);
114 image = XGetImage (dpy, pixmap, 0, 0, 8*width, height, 1, XYPixmap);
116 /* Fill the bitmap (X11 and OpenGL are upside down wrt each other). */
117 for (y = 0; y < height; y++)
118 for (x = 0; x < 8*width; x++)
119 if (XGetPixel (image, x, y))
120 bitmap[width*(height - y - 1) + x/8] |= (1 << (7 - (x % 8)));
122 XFreePixmap (dpy, pixmap);
123 XDestroyImage (image);
129 dump_bitmap (unsigned int width, unsigned int height, GLubyte *bitmap)
134 for (x = 0; x < 8*width; x++)
135 printf ("%o", 7 - (x % 8));
137 for (y = 0; y < height; y++)
140 for (x = 0; x < 8*width; x++)
141 putchar ((bitmap[width*(height - y - 1) + x/8] & (1 << (7 - (x % 8))))
144 for (x = 0; x < width; x++)
145 printf ("0x%02x, ", bitmap[width*(height - y - 1) + x]);
153 xscreensaver_glXUseXFont (Display *dpy, Font font,
154 int first, int count, int listbase)
156 Window win = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
160 unsigned long valuemask;
164 GLint swapbytes, lsbfirst, rowlength;
165 GLint skiprows, skippixels, alignment;
167 unsigned int max_width, max_height, max_bm_width, max_bm_height;
174 fs = XQueryFont (dpy, font);
177 /*gl_error (CC->gl_ctx, GL_INVALID_VALUE,
178 "Couldn't get font structure information");*/
183 /* Allocate a bitmap that can fit all characters. */
184 max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
185 max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
186 max_bm_width = (max_width + 7) / 8;
187 max_bm_height = max_height;
189 bm = (GLubyte *) malloc ((max_bm_width * max_bm_height) * sizeof (GLubyte));
192 /*gl_error (CC->gl_ctx, GL_OUT_OF_MEMORY,
193 "Couldn't allocate bitmap in glXUseXFont()");*/
198 /* Save the current packing mode for bitmaps. */
199 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &swapbytes);
200 glGetIntegerv (GL_UNPACK_LSB_FIRST, &lsbfirst);
201 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowlength);
202 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skiprows);
203 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skippixels);
204 glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
206 /* Enforce a standard packing mode which is compatible with
207 fill_bitmap() from above. This is actually the default mode,
208 except for the (non)alignment. */
209 glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
210 glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
211 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
212 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
213 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
214 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
216 clear_gl_error(); /* WTF? sometimes "invalid op" from glPixelStorei! */
219 pixmap = XCreatePixmap (dpy, win, 10, 10, 1);
220 values.foreground = 0;
221 values.background = 1;
222 values.font = fs->fid;
223 valuemask = GCForeground | GCBackground | GCFont;
224 gc = XCreateGC (dpy, pixmap, valuemask, &values);
225 XFreePixmap (dpy, pixmap);
228 /* Anti-aliasing of fonts looks like crap with 1-bit bitmaps.
229 It would be nice if we were using full-depth bitmaps, so
230 that the text showed up anti-aliased on screen, but
231 glBitmap() doesn't work that way. */
232 jwxyz_XSetAntiAliasing (dpy, gc, False);
235 for (i = 0; i < count; i++)
237 unsigned int width, height, bm_width, bm_height;
238 GLfloat x0, y0, dx, dy;
242 int list = listbase + i;
245 && (c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2))
246 ch = &fs->per_char[c-fs->min_char_or_byte2];
248 ch = &fs->max_bounds;
250 /* I'm not entirely clear on why this is necessary on OSX, but
251 without it, the characters are clipped. And it does not hurt
252 under real X11. -- jwz. */
256 /* glBitmap()'s parameters:
257 "Bitmap parameters xorig, yorig, width, and height are
258 computed from font metrics as descent-1, -lbearing,
259 rbearing-lbearing, and ascent+descent, respectively.
260 xmove is taken from the glyph's width metric, and
261 ymove is set to zero. Finally, the glyph's image is
262 converted to the appropriate format for glBitmap."
264 width = ch->rbearing - ch->lbearing;
265 height = ch->ascent + ch->descent;
267 y0 = ch->descent - 1;
271 /* X11's starting point. */
275 /* Round the width to a multiple of eight. We will use this also
276 for the pixmap for capturing the X11 font. This is slightly
277 inefficient, but it makes the OpenGL part real easy. */
278 bm_width = (width + 7) / 8;
281 glNewList (list, GL_COMPILE);
282 if ((c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2)
283 && (bm_width > 0) && (bm_height > 0))
285 memset (bm, '\0', bm_width * bm_height);
286 fill_bitmap (dpy, win, gc, bm_width, bm_height, x, y, c, bm);
287 glBitmap (width, height, x0, y0, dx, dy, bm);
289 printf ("width/height = %d/%d\n", width, height);
290 printf ("bm_width/bm_height = %d/%d\n", bm_width, bm_height);
291 dump_bitmap (bm_width, bm_height, bm);
295 glBitmap (0, 0, 0.0, 0.0, dx, dy, NULL);
300 XFreeFontInfo( NULL, fs, 0 );
303 /* Restore saved packing modes. */
304 glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
305 glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
306 glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
307 glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
308 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
309 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
311 check_gl_error ("xscreensaver_glXUseXFont");
316 /* Loads the font named by the X resource "res".
317 Returns an XFontStruct.
318 Also converts the font to a set of GL lists and returns the first list.
321 load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
325 const char *font = get_string_resource (dpy, res, "Font");
326 const char *def1 = "-*-helvetica-medium-r-normal-*-180-*";
327 const char *def2 = "fixed";
331 if (!res || !*res) abort();
332 if (!font_ret && !dlist_ret) abort();
334 if (!font) font = def1;
336 f = XLoadQueryFont(dpy, font);
337 if (!f && !!strcmp (font, def1))
339 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
340 progname, font, def1);
342 f = XLoadQueryFont(dpy, font);
345 if (!f && !!strcmp (font, def2))
347 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
348 progname, font, def2);
350 f = XLoadQueryFont(dpy, font);
355 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
361 first = f->min_char_or_byte2;
362 last = f->max_char_or_byte2;
368 *dlist_ret = glGenLists ((GLuint) last+1);
369 check_gl_error ("glGenLists");
370 xscreensaver_glXUseXFont (dpy, id, first, last-first+1,
378 #endif /* HAVE_GLBITMAP */
381 /* Draws the string on the window at the given pixel position.
382 Newlines and tab stops are honored.
383 Any text inside [] will be rendered as a subscript.
384 Assumes the font has been loaded as with load_font().
387 print_gl_string (Display *dpy,
388 # ifdef HAVE_GLBITMAP
389 XFontStruct *font, GLuint font_dlist,
391 texture_font_data *font_data,
393 int window_width, int window_height,
394 GLfloat x, GLfloat y,
396 Bool clear_background_p)
399 /* If window_width was specified, we're drawing ortho in pixel coordinates.
400 Otherwise, we're just dropping the text at the current position in the
401 scene, billboarded. */
402 Bool in_scene_p = (window_width == 0);
404 # ifdef HAVE_GLBITMAP
405 GLfloat line_height = font->ascent + font->descent;
406 int cw = string_width (font, "m", 0);
407 # else /* !HAVE_GLBITMAP */
409 int cw = texture_string_width (font_data, "m", &line_height);
410 # endif /* !HAVE_GLBITMAP */
412 GLfloat sub_shift = (line_height * 0.3);
417 # ifdef HAVE_GLBITMAP
418 /* Sadly, this causes a stall of the graphics pipeline (as would the
419 equivalent calls to glGet*.) But there's no way around this, short
420 of having each caller set up the specific display matrix we need
421 here, which would kind of defeat the purpose of centralizing this
424 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
425 GL_ENABLE_BIT | /* for various glDisable calls */
426 GL_CURRENT_BIT | /* for glColor3f() */
427 GL_LIST_BIT); /* for glListBase() */
429 check_gl_error ("glPushAttrib");
431 # else /* !HAVE_GLBITMAP */
432 Bool tex_p = glIsEnabled (GL_TEXTURE_2D);
433 Bool texs_p = glIsEnabled (GL_TEXTURE_GEN_S);
434 Bool text_p = glIsEnabled (GL_TEXTURE_GEN_T);
435 Bool light_p = glIsEnabled (GL_LIGHTING);
436 Bool blend_p = glIsEnabled (GL_BLEND);
437 Bool depth_p = glIsEnabled (GL_DEPTH_TEST);
438 Bool cull_p = glIsEnabled (GL_CULL_FACE);
439 Bool fog_p = glIsEnabled (GL_FOG);
441 # ifndef HAVE_JWZGLES
443 glGetIntegerv (GL_POLYGON_MODE, opoly);
445 glGetIntegerv (GL_BLEND_DST, &oblend);
446 # endif /* !HAVE_GLBITMAP */
448 for (c = string; *c; c++)
449 if (*c == '\n') lines++;
455 /* disable lighting and texturing when drawing bitmaps!
456 (glPopAttrib() restores these.)
458 # ifdef HAVE_GLBITMAP
459 glDisable (GL_TEXTURE_2D);
460 # else /* !HAVE_GLBITMAP */
461 glEnable (GL_TEXTURE_2D);
462 glDisable (GL_TEXTURE_GEN_S);
463 glDisable (GL_TEXTURE_GEN_T);
464 glPolygonMode (GL_FRONT, GL_FILL);
466 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
467 # endif /* !HAVE_GLBITMAP */
469 glDisable (GL_LIGHTING);
472 glDisable (GL_DEPTH_TEST);
473 glDisable (GL_CULL_FACE);
476 /* glPopAttrib() does not restore matrix changes, so we must
477 push/pop the matrix stacks to be non-intrusive there.
479 glMatrixMode(GL_PROJECTION);
483 check_gl_error ("glPushMatrix");
488 /* Each matrix mode has its own stack, so we need to push/pop
490 glMatrixMode(GL_MODELVIEW);
498 check_gl_error ("glPushMatrix");
503 double rot = current_device_rotation();
506 glOrtho (0, window_width, 0, window_height, -1, 1);
508 if (rot > 135 || rot < -135)
510 glTranslatef (window_width, window_height, 0);
511 glRotatef (180, 0, 0, 1);
515 glTranslatef (window_width, 0, 0);
516 glRotatef (90, 0, 0, 1);
517 y -= (window_height - window_width);
518 if (y < line_height * lines + 10)
519 y = line_height * lines + 10;
523 glTranslatef(0, window_height, 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 check_gl_error ("glOrtho");
534 if (clear_background_p)
538 # ifdef HAVE_GLBITMAP
539 w = string_width (font, string, &h);
540 glRecti (x - font->descent,
542 x + w + 2*font->descent,
543 y + line_height - h - font->descent);
544 # else /* !HAVE_GLBITMAP */
546 int descent = line_height * 0.2;
547 if (descent < 2) descent = 2;
548 w = texture_string_width (font_data, string, &h);
549 glRecti (x - descent,
552 y + line_height - h - descent);
554 # endif /* !HAVE_GLBITMAP */
560 for (i = 0; i < strlen(string); i++)
562 unsigned char c = (unsigned char) string[i];
571 x2 = ((x2 + tabs) / tabs) * tabs; /* tab to tab stop */
574 else if (c == '[' && (isdigit (string[i+1])))
579 else if (c == ']' && sub_p)
586 # ifdef HAVE_GLBITMAP
587 glRasterPos2f (x2, y);
588 glCallList (font_dlist + (int)(c));
589 x2 += (font->per_char
590 ? font->per_char[c - font->min_char_or_byte2].width
591 : font->min_bounds.width);
592 # else /* !HAVE_GLBITMAP */
594 glTranslatef (x2, y, 0);
599 print_texture_string (font_data, s);
600 x2 += texture_string_width (font_data, s, 0);
603 # endif /* !HAVE_GLBITMAP */
607 check_gl_error ("print_gl_string");
612 glMatrixMode(GL_PROJECTION);
615 # ifdef HAVE_GLBITMAP
618 check_gl_error ("glPopAttrib");
620 # else /* !HAVE_GLBITMAP */
621 if (tex_p) glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
622 if (texs_p) glEnable (GL_TEXTURE_GEN_S);/*else glDisable (GL_TEXTURE_GEN_S);*/
623 if (text_p) glEnable (GL_TEXTURE_GEN_T);/*else glDisable (GL_TEXTURE_GEN_T);*/
624 if (blend_p) glEnable (GL_BLEND); else glDisable (GL_BLEND);
625 if (light_p) glEnable (GL_LIGHTING); /*else glDisable (GL_LIGHTING);*/
626 if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
627 if (cull_p) glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
628 if (fog_p) glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
629 # ifndef HAVE_JWZGLES
630 glPolygonMode (GL_FRONT, opoly[0]);
632 glBlendFunc (GL_SRC_ALPHA, oblend);
633 # endif /* !HAVE_GLBITMAP */
635 glMatrixMode(GL_MODELVIEW);