1 /* glxfonts, Copyright (c) 2001-2011 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;
129 fs = XQueryFont (dpy, font);
132 /*gl_error (CC->gl_ctx, GL_INVALID_VALUE,
133 "Couldn't get font structure information");*/
138 /* Allocate a bitmap that can fit all characters. */
139 max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
140 max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
141 max_bm_width = (max_width + 7) / 8;
142 max_bm_height = max_height;
144 bm = (GLubyte *) malloc ((max_bm_width * max_bm_height) * sizeof (GLubyte));
147 /*gl_error (CC->gl_ctx, GL_OUT_OF_MEMORY,
148 "Couldn't allocate bitmap in glXUseXFont()");*/
153 /* Save the current packing mode for bitmaps. */
154 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &swapbytes);
155 glGetIntegerv (GL_UNPACK_LSB_FIRST, &lsbfirst);
156 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowlength);
157 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skiprows);
158 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skippixels);
159 glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
161 /* Enforce a standard packing mode which is compatible with
162 fill_bitmap() from above. This is actually the default mode,
163 except for the (non)alignment. */
164 glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
165 glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
166 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
167 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
168 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
169 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
171 clear_gl_error(); /* WTF? sometimes "invalid op" from glPixelStorei! */
174 pixmap = XCreatePixmap (dpy, win, 10, 10, 1);
175 values.foreground = 0;
176 values.background = 1;
177 values.font = fs->fid;
178 valuemask = GCForeground | GCBackground | GCFont;
179 gc = XCreateGC (dpy, pixmap, valuemask, &values);
180 XFreePixmap (dpy, pixmap);
183 /* Anti-aliasing of fonts looks like crap with 1-bit bitmaps.
184 It would be nice if we were using full-depth bitmaps, so
185 that the text showed up anti-aliased on screen, but
186 glBitmap() doesn't work that way. */
187 jwxyz_XSetAntiAliasing (dpy, gc, False);
190 for (i = 0; i < count; i++)
192 unsigned int width, height, bm_width, bm_height;
193 GLfloat x0, y0, dx, dy;
197 int list = listbase + i;
200 && (c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2))
201 ch = &fs->per_char[c-fs->min_char_or_byte2];
203 ch = &fs->max_bounds;
205 /* I'm not entirely clear on why this is necessary on OSX, but
206 without it, the characters are clipped. And it does not hurt
207 under real X11. -- jwz. */
211 /* glBitmap()'s parameters:
212 "Bitmap parameters xorig, yorig, width, and height are
213 computed from font metrics as descent-1, -lbearing,
214 rbearing-lbearing, and ascent+descent, respectively.
215 xmove is taken from the glyph's width metric, and
216 ymove is set to zero. Finally, the glyph's image is
217 converted to the appropriate format for glBitmap."
219 width = ch->rbearing - ch->lbearing;
220 height = ch->ascent + ch->descent;
222 y0 = ch->descent - 1;
226 /* X11's starting point. */
230 /* Round the width to a multiple of eight. We will use this also
231 for the pixmap for capturing the X11 font. This is slightly
232 inefficient, but it makes the OpenGL part real easy. */
233 bm_width = (width + 7) / 8;
236 glNewList (list, GL_COMPILE);
237 if ((c >= fs->min_char_or_byte2) && (c <= fs->max_char_or_byte2)
238 && (bm_width > 0) && (bm_height > 0))
240 memset (bm, '\0', bm_width * bm_height);
241 fill_bitmap (dpy, win, gc, bm_width, bm_height, x, y, c, bm);
242 glBitmap (width, height, x0, y0, dx, dy, bm);
244 printf ("width/height = %d/%d\n", width, height);
245 printf ("bm_width/bm_height = %d/%d\n", bm_width, bm_height);
246 dump_bitmap (bm_width, bm_height, bm);
250 glBitmap (0, 0, 0.0, 0.0, dx, dy, NULL);
255 XFreeFontInfo( NULL, fs, 0 );
258 /* Restore saved packing modes. */
259 glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
260 glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
261 glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
262 glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
263 glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
264 glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
266 check_gl_error ("xscreensaver_glXUseXFont");
271 /* Loads the font named by the X resource "res".
272 Returns an XFontStruct.
273 Also converts the font to a set of GL lists and returns the first list.
276 load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
280 const char *font = get_string_resource (dpy, res, "Font");
281 const char *def1 = "-*-helvetica-medium-r-normal-*-180-*";
282 const char *def2 = "fixed";
286 if (!res || !*res) abort();
287 if (!font_ret && !dlist_ret) abort();
289 if (!font) font = def1;
291 f = XLoadQueryFont(dpy, font);
292 if (!f && !!strcmp (font, def1))
294 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
295 progname, font, def1);
297 f = XLoadQueryFont(dpy, font);
300 if (!f && !!strcmp (font, def2))
302 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
303 progname, font, def2);
305 f = XLoadQueryFont(dpy, font);
310 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
316 first = f->min_char_or_byte2;
317 last = f->max_char_or_byte2;
323 *dlist_ret = glGenLists ((GLuint) last+1);
324 check_gl_error ("glGenLists");
325 xscreensaver_glXUseXFont (dpy, id, first, last-first+1,
334 /* Width (and optionally height) of the string in pixels.
337 string_width (XFontStruct *f, const char *c, int *height_ret)
341 int h = f->ascent + f->descent;
344 int cc = *((unsigned char *) c);
347 if (x > max_w) max_w = x;
349 h += f->ascent + f->descent;
353 ? f->per_char[cc-f->min_char_or_byte2].width
354 : f->min_bounds.rbearing);
357 if (x > max_w) max_w = x;
358 if (height_ret) *height_ret = h;
364 /* Draws the string on the window at the given pixel position.
365 Newlines and tab stops are honored.
366 Any text inside [] will be rendered as a subscript.
367 Assumes the font has been loaded as with load_font().
370 print_gl_string (Display *dpy,
373 int window_width, int window_height,
374 GLfloat x, GLfloat y,
376 Bool clear_background_p)
378 GLfloat line_height = font->ascent + font->descent;
379 GLfloat sub_shift = (line_height * 0.3);
380 int cw = string_width (font, "m", 0);
385 /* Sadly, this causes a stall of the graphics pipeline (as would the
386 equivalent calls to glGet*.) But there's no way around this, short
387 of having each caller set up the specific display matrix we need
388 here, which would kind of defeat the purpose of centralizing this
391 glPushAttrib (GL_TRANSFORM_BIT | /* for matrix contents */
392 GL_ENABLE_BIT | /* for various glDisable calls */
393 GL_CURRENT_BIT | /* for glColor3f() */
394 GL_LIST_BIT); /* for glListBase() */
397 check_gl_error ("glPushAttrib");
400 /* disable lighting and texturing when drawing bitmaps!
401 (glPopAttrib() restores these.)
403 glDisable (GL_TEXTURE_2D);
404 glDisable (GL_LIGHTING);
405 glDisable (GL_BLEND);
406 glDisable (GL_DEPTH_TEST);
407 glDisable (GL_CULL_FACE);
409 /* glPopAttrib() does not restore matrix changes, so we must
410 push/pop the matrix stacks to be non-intrusive there.
412 glMatrixMode(GL_PROJECTION);
416 check_gl_error ("glPushMatrix");
420 /* Each matrix mode has its own stack, so we need to push/pop
422 glMatrixMode(GL_MODELVIEW);
430 check_gl_error ("glPushMatrix");
434 gluOrtho2D (0, window_width, 0, window_height);
436 check_gl_error ("gluOrtho2D");
439 if (clear_background_p)
442 int lh = font->ascent + font->descent;
443 w = string_width (font, string, &h);
445 glRecti (x - font->descent,
447 x + w + 2*font->descent,
448 y + lh - h - font->descent);
453 glRasterPos2f (x, y);
454 /* glListBase (font_dlist);*/
455 for (i = 0; i < strlen(string); i++)
457 unsigned char c = (unsigned char) string[i];
460 glRasterPos2f (x, (y -= line_height));
466 x2 = ((x2 + tabs) / tabs) * tabs; /* tab to tab stop */
468 glRasterPos2f (x2, y);
470 else if (c == '[' && (isdigit (string[i+1])))
473 glRasterPos2f (x2, (y -= sub_shift));
475 else if (c == ']' && sub_p)
478 glRasterPos2f (x2, (y += sub_shift));
482 /* glCallLists (s - string, GL_UNSIGNED_BYTE, string);*/
483 glCallList (font_dlist + (int)(c));
484 x2 += (font->per_char
485 ? font->per_char[c - font->min_char_or_byte2].width
486 : font->min_bounds.width);
490 check_gl_error ("print_gl_string");
495 glMatrixMode(GL_PROJECTION);
500 check_gl_error ("glPopAttrib");
503 glMatrixMode(GL_MODELVIEW);