1 /* glxfonts, Copyright (c) 2001-2009 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
24 # include <OpenGL/gl.h>
25 # include <OpenGL/glu.h>
32 #include "resources.h"
35 /* These are in xlock-gl.c */
36 extern void clear_gl_error (void);
37 extern void check_gl_error (const char *type);
40 extern char *progname;
43 #undef DEBUG /* Defining this causes check_gl_error() to be called inside
44 time-critical sections, which could slow things down (since
45 it might result in a round-trip, and stall of the pipeline.)
49 /* Mostly lifted from the Mesa implementation of glXUseXFont(), since
50 Mac OS 10.6 no longer supports aglUseFont() which was their analog
51 of that. This code could be in libjwxyz instead, but we might as
52 well use the same text-drawing code on both X11 and Cocoa.
55 fill_bitmap (Display *dpy, Window win, GC gc,
56 unsigned int width, unsigned int height,
57 int x0, int y0, char c, GLubyte *bitmap)
63 pixmap = XCreatePixmap (dpy, win, 8*width, height, 1);
64 XSetForeground(dpy, gc, 0);
65 XFillRectangle (dpy, pixmap, gc, 0, 0, 8*width, height);
66 XSetForeground(dpy, gc, 1);
67 XDrawString (dpy, pixmap, gc, x0, y0, &c, 1);
69 image = XGetImage (dpy, pixmap, 0, 0, 8*width, height, 1, XYPixmap);
71 /* Fill the bitmap (X11 and OpenGL are upside down wrt each other). */
72 for (y = 0; y < height; y++)
73 for (x = 0; x < 8*width; x++)
74 if (XGetPixel (image, x, y))
75 bitmap[width*(height - y - 1) + x/8] |= (1 << (7 - (x % 8)));
77 XFreePixmap (dpy, pixmap);
78 XDestroyImage (image);
84 dump_bitmap (unsigned int width, unsigned int height, GLubyte *bitmap)
89 for (x = 0; x < 8*width; x++)
90 printf ("%o", 7 - (x % 8));
92 for (y = 0; y < height; y++)
95 for (x = 0; x < 8*width; x++)
96 putchar ((bitmap[width*(height - y - 1) + x/8] & (1 << (7 - (x % 8))))
99 for (x = 0; x < width; x++)
100 printf ("0x%02x, ", bitmap[width*(height - y - 1) + x]);
108 xscreensaver_glXUseXFont (Display *dpy, Font font,
109 int first, int count, int listbase)
111 Window win = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
115 unsigned long valuemask;
119 GLint swapbytes, lsbfirst, rowlength;
120 GLint skiprows, skippixels, alignment;
122 unsigned int max_width, max_height, max_bm_width, max_bm_height;
127 fs = XQueryFont (dpy, font);
130 /*gl_error (CC->gl_ctx, GL_INVALID_VALUE,
131 "Couldn't get font structure information");*/
136 /* Allocate a bitmap that can fit all characters. */
137 max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
138 max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
139 max_bm_width = (max_width + 7) / 8;
140 max_bm_height = max_height;
142 bm = (GLubyte *) malloc ((max_bm_width * max_bm_height) * sizeof (GLubyte));
145 /*gl_error (CC->gl_ctx, GL_OUT_OF_MEMORY,
146 "Couldn't allocate bitmap in glXUseXFont()");*/
151 /* Save the current packing mode for bitmaps. */
152 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &swapbytes);
153 glGetIntegerv (GL_UNPACK_LSB_FIRST, &lsbfirst);
154 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowlength);
155 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skiprows);
156 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skippixels);
157 glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
159 /* Enforce a standard packing mode which is compatible with
160 fill_bitmap() from above. This is actually the default mode,
161 except for the (non)alignment. */
162 glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
163 glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
164 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
165 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
166 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
167 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
169 pixmap = XCreatePixmap (dpy, win, 10, 10, 1);
170 values.foreground = 0;
171 values.background = 1;
172 values.font = fs->fid;
173 valuemask = GCForeground | GCBackground | GCFont;
174 gc = XCreateGC (dpy, pixmap, valuemask, &values);
175 XFreePixmap (dpy, pixmap);
178 /* Anti-aliasing of fonts looks like crap with 1-bit bitmaps.
179 It would be nice if we were using full-depth bitmaps, so
180 that the text showed up anti-aliased on screen, but
181 glBitmap() doesn't work that way. */
182 jwxyz_XSetAntiAliasing (dpy, gc, False);
185 for (i = 0; i < count; i++)
187 unsigned int width, height, bm_width, bm_height;
188 GLfloat x0, y0, dx, dy;
192 int list = listbase + i;
195 && (c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2))
196 ch = &fs->per_char[c-fs->min_char_or_byte2];
198 ch = &fs->max_bounds;
200 /* I'm not entirely clear on why this is necessary on OSX, but
201 without it, the characters are clipped. And it does not hurt
202 under real X11. -- jwz. */
206 /* glBitmap()'s parameters:
207 "Bitmap parameters xorig, yorig, width, and height are
208 computed from font metrics as descent-1, -lbearing,
209 rbearing-lbearing, and ascent+descent, respectively.
210 xmove is taken from the glyph's width metric, and
211 ymove is set to zero. Finally, the glyph's image is
212 converted to the appropriate format for glBitmap."
214 width = ch->rbearing - ch->lbearing;
215 height = ch->ascent + ch->descent;
217 y0 = ch->descent - 1;
221 /* X11's starting point. */
225 /* Round the width to a multiple of eight. We will use this also
226 for the pixmap for capturing the X11 font. This is slightly
227 inefficient, but it makes the OpenGL part real easy. */
228 bm_width = (width + 7) / 8;
231 glNewList (list, GL_COMPILE);
232 if ((c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2)
233 && (bm_width > 0) && (bm_height > 0))
235 memset (bm, '\0', bm_width * bm_height);
236 fill_bitmap (dpy, win, gc, bm_width, bm_height, x, y, c, bm);
237 glBitmap (width, height, x0, y0, dx, dy, bm);
239 printf ("width/height = %d/%d\n", width, height);
240 printf ("bm_width/bm_height = %d/%d\n", bm_width, bm_height);
241 dump_bitmap (bm_width, bm_height, bm);
245 glBitmap (0, 0, 0.0, 0.0, dx, dy, NULL);
250 XFreeFontInfo( NULL, fs, 0 );
253 /* Restore saved packing modes. */
254 glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
255 glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
256 glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
257 glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
258 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
259 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
264 /* Loads the font named by the X resource "res".
265 Returns an XFontStruct.
266 Also converts the font to a set of GL lists and returns the first list.
269 load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
273 const char *font = get_string_resource (dpy, res, "Font");
274 const char *def1 = "-*-helvetica-medium-r-normal-*-180-*";
275 const char *def2 = "fixed";
279 if (!res || !*res) abort();
280 if (!font_ret && !dlist_ret) abort();
282 if (!font) font = def1;
284 f = XLoadQueryFont(dpy, font);
285 if (!f && !!strcmp (font, def1))
287 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
288 progname, font, def1);
290 f = XLoadQueryFont(dpy, font);
293 if (!f && !!strcmp (font, def2))
295 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
296 progname, font, def2);
298 f = XLoadQueryFont(dpy, font);
303 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
309 first = f->min_char_or_byte2;
310 last = f->max_char_or_byte2;
316 *dlist_ret = glGenLists ((GLuint) last+1);
317 check_gl_error ("glGenLists");
318 xscreensaver_glXUseXFont(dpy, id, first, last-first+1,
320 check_gl_error ("xscreensaver_glXUseXFont");
328 /* Width (and optionally height) of the string in pixels.
331 string_width (XFontStruct *f, const char *c, int *height_ret)
335 int h = f->ascent + f->descent;
338 int cc = *((unsigned char *) c);
341 if (x > max_w) max_w = x;
343 h += f->ascent + f->descent;
347 ? f->per_char[cc-f->min_char_or_byte2].width
348 : f->min_bounds.rbearing);
351 if (x > max_w) max_w = x;
352 if (height_ret) *height_ret = h;
358 /* Draws the string on the window at the given pixel position.
359 Newlines and tab stops are honored.
360 Any text inside [] will be rendered as a subscript.
361 Assumes the font has been loaded as with load_font().
364 print_gl_string (Display *dpy,
367 int window_width, int window_height,
368 GLfloat x, GLfloat y,
370 Bool clear_background_p)
372 GLfloat line_height = font->ascent + font->descent;
373 GLfloat sub_shift = (line_height * 0.3);
374 int cw = string_width (font, "m", 0);
379 /* Sadly, this causes a stall of the graphics pipeline (as would the
380 equivalent calls to glGet*.) But there's no way around this, short
381 of having each caller set up the specific display matrix we need
382 here, which would kind of defeat the purpose of centralizing this
385 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
386 GL_ENABLE_BIT | /* for various glDisable calls */
387 GL_CURRENT_BIT | /* for glColor3f() */
388 GL_LIST_BIT); /* for glListBase() */
391 check_gl_error ("glPushAttrib");
394 /* disable lighting and texturing when drawing bitmaps!
395 (glPopAttrib() restores these.)
397 glDisable (GL_TEXTURE_2D);
398 glDisable (GL_LIGHTING);
399 glDisable (GL_BLEND);
400 glDisable (GL_DEPTH_TEST);
401 glDisable (GL_CULL_FACE);
403 /* glPopAttrib() does not restore matrix changes, so we must
404 push/pop the matrix stacks to be non-intrusive there.
406 glMatrixMode(GL_PROJECTION);
410 check_gl_error ("glPushMatrix");
414 /* Each matrix mode has its own stack, so we need to push/pop
416 glMatrixMode(GL_MODELVIEW);
424 check_gl_error ("glPushMatrix");
428 gluOrtho2D (0, window_width, 0, window_height);
430 check_gl_error ("gluOrtho2D");
433 if (clear_background_p)
436 int lh = font->ascent + font->descent;
437 w = string_width (font, string, &h);
439 glRecti (x - font->descent,
441 x + w + 2*font->descent,
442 y + lh - h - font->descent);
447 glRasterPos2f (x, y);
448 /* glListBase (font_dlist);*/
449 for (i = 0; i < strlen(string); i++)
451 unsigned char c = (unsigned char) string[i];
454 glRasterPos2f (x, (y -= line_height));
460 x2 = ((x2 + tabs) / tabs) * tabs; /* tab to tab stop */
462 glRasterPos2f (x2, y);
464 else if (c == '[' && (isdigit (string[i+1])))
467 glRasterPos2f (x2, (y -= sub_shift));
469 else if (c == ']' && sub_p)
472 glRasterPos2f (x2, (y += sub_shift));
476 /* glCallLists (s - string, GL_UNSIGNED_BYTE, string);*/
477 glCallList (font_dlist + (int)(c));
478 x2 += (font->per_char
479 ? font->per_char[c - font->min_char_or_byte2].width
480 : font->min_bounds.width);
484 check_gl_error ("print_gl_string");
489 glMatrixMode(GL_PROJECTION);
494 check_gl_error ("glPopAttrib");
497 glMatrixMode(GL_MODELVIEW);