1 /* texfonts, Copyright (c) 2005-2007 Jamie Zawinski <jwz@jwz.org>
2 * Loads X11 fonts into textures 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/glu.h>
30 #include "resources.h"
33 /* These are in xlock-gl.c */
34 extern void clear_gl_error (void);
35 extern void check_gl_error (const char *type);
38 extern char *progname;
40 struct texture_font_data {
43 int cell_width, cell_height; /* maximal charcell */
44 int tex_width, tex_height; /* size of each texture */
46 int grid_mag; /* 1, 2, 4, or 8 */
47 int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */
49 GLuint texid[64]; /* must hold ntextures */
53 /* return the next larger power of 2. */
57 static const unsigned int pow2[] = {
58 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
59 2048, 4096, 8192, 16384, 32768, 65536 };
61 for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
62 if (pow2[j] >= i) return pow2[j];
63 abort(); /* too big! */
67 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
68 RGB are averaged to grayscale, and the resulting value is treated as alpha.
69 Pass in the size of the pixmap; the size of the texture is returned
70 (it may be larger, since GL like powers of 2.)
72 We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
73 were drawn with antialiasing, that is preserved.
76 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
81 int w2 = to_pow2 (ow);
82 int h2 = to_pow2 (oh);
84 XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
85 unsigned char *data = (unsigned char *) calloc (w2, (h2 + 1));
86 unsigned char *out = data;
87 GLuint iformat = GL_INTENSITY;
88 GLuint format = GL_LUMINANCE;
89 GLuint type = GL_UNSIGNED_BYTE;
91 for (y = 0; y < h2; y++)
92 for (x = 0; x < w2; x++) {
93 unsigned long pixel = (x >= ow || y >= oh ? 0 : XGetPixel (image, x, y));
94 /* instead of averaging all three channels, let's just use red,
95 and assume it was already grayscale. */
96 unsigned long r = pixel & visual->red_mask;
97 pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
100 XDestroyImage (image);
104 gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
106 glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
110 sprintf (msg, "texture font %s (%d x %d)",
111 mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
113 check_gl_error (msg);
117 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
118 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
119 mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
121 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
122 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
131 /* Loads the font named by the X resource "res" and returns
132 a texture-font object.
135 load_texture_font (Display *dpy, char *res)
137 Screen *screen = DefaultScreenOfDisplay (dpy);
138 Window root = RootWindowOfScreen (screen);
139 XWindowAttributes xgwa;
141 texture_font_data *data = 0;
142 char *font = get_string_resource (dpy, res, "Font");
143 const char *def1 = "-*-times-bold-r-normal-*-240-*";
144 const char *def2 = "-*-times-bold-r-normal-*-180-*";
145 const char *def3 = "fixed";
149 check_gl_error ("stale texture font");
151 XGetWindowAttributes (dpy, root, &xgwa);
153 if (!res || !*res) abort();
154 if (!font) font = strdup(def1);
156 f = XLoadQueryFont(dpy, font);
157 if (!f && !!strcmp (font, def1))
159 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
160 progname, font, def1);
162 font = strdup (def1);
163 f = XLoadQueryFont(dpy, font);
166 if (!f && !!strcmp (font, def2))
168 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
169 progname, font, def2);
171 font = strdup (def2);
172 f = XLoadQueryFont(dpy, font);
175 if (!f && !!strcmp (font, def3))
177 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
178 progname, font, def3);
180 font = strdup (def3);
181 f = XLoadQueryFont(dpy, font);
186 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
194 data = (texture_font_data *) calloc (1, sizeof(*data));
198 /* Figure out how many textures to use.
199 E.g., if we need 1024x1024 bits, use four 512x512 textures,
200 to be gentle to machines with low texture size limits.
203 int w = to_pow2 (16 * (f->max_bounds.rbearing - f->min_bounds.lbearing));
204 int h = to_pow2 (16 * (f->max_bounds.ascent + f->max_bounds.descent));
205 int i = (w > h ? w : h);
207 if (i <= 512) data->grid_mag = 1; /* 1 tex of 16x16 chars */
208 else if (i <= 1024) data->grid_mag = 2; /* 4 tex of 8x8 chars */
209 else if (i <= 2048) data->grid_mag = 4; /* 16 tex of 4x4 chars */
210 else data->grid_mag = 8; /* 32 tex of 2x2 chars */
212 data->ntextures = data->grid_mag * data->grid_mag;
216 "%s: %dx%d grid of %d textures of %dx%d chars (%dx%d bits)\n",
218 data->grid_mag, data->grid_mag,
220 16 / data->grid_mag, 16 / data->grid_mag,
225 for (which = 0; which < data->ntextures; which++)
227 /* Create a pixmap big enough to fit every character in the font.
228 (modulo the "ntextures" scaling.)
229 Make it square-ish, since GL likes dimensions to be powers of 2.
234 int cw = f->max_bounds.rbearing - f->min_bounds.lbearing;
235 int ch = f->max_bounds.ascent + f->max_bounds.descent;
236 int grid_size = (16 / data->grid_mag);
237 int w = cw * grid_size;
238 int h = ch * grid_size;
241 data->cell_width = cw;
242 data->cell_height = ch;
244 p = XCreatePixmap (dpy, root, w, h, xgwa.depth);
246 gcv.foreground = BlackPixelOfScreen (xgwa.screen);
247 gcv.background = BlackPixelOfScreen (xgwa.screen);
248 gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
249 XFillRectangle (dpy, p, gc, 0, 0, w, h);
250 XSetForeground (dpy, gc, WhitePixelOfScreen (xgwa.screen));
251 for (i = 0; i < 256 / data->ntextures; i++)
253 int ii = (i + (which * 256 / data->ntextures));
255 int x = (i % grid_size) * cw;
256 int y = (i / grid_size) * ch;
258 /* See comment in print_texture_string for bit layout explanation.
260 int lbearing = (f->per_char
261 ? f->per_char[ii - f->min_char_or_byte2].lbearing
262 : f->min_bounds.lbearing);
263 int ascent = (f->per_char
264 ? f->per_char[ii - f->min_char_or_byte2].ascent
265 : f->max_bounds.ascent);
266 int width = (f->per_char
267 ? f->per_char[ii - f->min_char_or_byte2].width
268 : f->max_bounds.width);
270 if (width == 0) continue;
271 XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1);
275 glGenTextures (1, &data->texid[which]);
276 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
277 check_gl_error ("texture font load");
279 data->tex_height = h;
281 #if 0 /* debugging: splat the bitmap onto the desktop root window */
283 Window win = RootWindow (dpy, 0);
284 GC gc2 = XCreateGC (dpy, win, 0, &gcv);
285 XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
286 XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
287 XCopyArea (dpy, p, win, gc2, 0, 0, w, h, 0, 0);
294 #if 0 /* debugging: write the bitmap to a pgm file */
300 sprintf (file, "/tmp/%02d.pgm", which);
301 image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
302 f = fopen (file, "w");
303 fprintf (f, "P5\n%d %d\n255\n", w, h);
304 for (y = 0; y < h; y++)
305 for (x = 0; x < w; x++) {
306 unsigned long pix = XGetPixel (image, x, y);
307 unsigned long r = (pix & xgwa.visual->red_mask);
308 r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
309 fprintf (f, "%c", (char) r);
312 XDestroyImage (image);
313 fprintf (stderr, "%s: wrote %s\n", progname, file);
317 bitmap_to_texture (dpy, p, xgwa.visual,
318 &data->tex_width, &data->tex_height);
319 XFreePixmap (dpy, p);
326 /* Width of the string in pixels.
329 texture_string_width (texture_font_data *data, const char *c,
330 int *line_height_ret)
333 XFontStruct *f = data->font;
336 int cc = *((unsigned char *) c);
338 ? f->per_char[cc-f->min_char_or_byte2].width
339 : f->max_bounds.width);
343 *line_height_ret = f->ascent + f->descent;
348 /* Draws the string in the scene at the origin.
349 Newlines and tab stops are honored.
352 print_texture_string (texture_font_data *data, const char *string)
354 XFontStruct *f = data->font;
355 GLfloat line_height = f->ascent + f->descent;
356 # ifdef DO_SUBSCRIPTS
357 GLfloat sub_shift = (line_height * 0.3);
359 # endif /* DO_SUBSCRIPTS */
360 int cw = texture_string_width (data, "m", 0);
369 glNormal3f (0, 0, 1);
373 for (i = 0; i < strlen(string); i++)
375 unsigned char c = string[i];
383 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
385 # ifdef DO_SUBSCRIPTS
386 else if (c == '[' && (isdigit (string[i+1])))
391 else if (c == ']' && sub_p)
396 # endif /* DO_SUBSCRIPTS */
399 /* For a small font, the texture is divided into 16x16 rectangles
400 whose size are the max_bounds charcell of the font. Within each
401 rectangle, the individual characters' charcells sit in the upper
404 For a larger font, the texture will itself be subdivided, to
405 keep the texture sizes small (in that case we deal with, e.g.,
406 4 grids of 8x8 characters instead of 1 grid of 16x16.)
410 [A]----------------------------
421 |----[B]----------|---| |
425 |--------------------[C] |
428 ------------------------------ cell_height
430 We want to make a quad from point A to point C.
431 We want to position that quad so that point B lies at x,y.
433 int lbearing = (f->per_char
434 ? f->per_char[c - f->min_char_or_byte2].lbearing
435 : f->min_bounds.lbearing);
436 int rbearing = (f->per_char
437 ? f->per_char[c - f->min_char_or_byte2].rbearing
438 : f->max_bounds.rbearing);
439 int ascent = (f->per_char
440 ? f->per_char[c - f->min_char_or_byte2].ascent
441 : f->max_bounds.ascent);
442 int descent = (f->per_char
443 ? f->per_char[c - f->min_char_or_byte2].descent
444 : f->max_bounds.descent);
445 int cwidth = (f->per_char
446 ? f->per_char[c - f->min_char_or_byte2].width
447 : f->max_bounds.width);
449 char cc = c % (256 / data->ntextures);
451 int gs = (16 / data->grid_mag); /* grid size */
453 int ax = ((int) cc % gs) * data->cell_width; /* point A */
454 int ay = ((int) cc / gs) * data->cell_height;
456 int bx = ax - lbearing; /* point B */
457 int by = ay + ascent;
459 int cx = bx + rbearing; /* point C */
460 int cy = by + descent;
462 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
463 GLfloat tay = (GLfloat) ay / data->tex_height;
465 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
466 GLfloat tcy = (GLfloat) cy / data->tex_height;
468 GLfloat qx0 = x + lbearing; /* quad top left */
469 GLfloat qy0 = y + ascent;
470 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
471 GLfloat qy1 = qy0 - (ascent + descent);
473 if (cwidth > 0 && c != ' ')
475 int which = c / (256 / data->ntextures);
476 if (which >= data->ntextures) abort();
477 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
480 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
481 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
482 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
483 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
486 glDisable(GL_TEXTURE_2D);
487 glBegin (GL_LINE_LOOP);
488 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
489 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
490 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
491 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
493 glEnable(GL_TEXTURE_2D);
502 check_gl_error ("texture font print");
505 /* Releases the font and texture.
508 free_texture_font (texture_font_data *data)
512 XFreeFont (data->dpy, data->font);
513 for (i = 0; i < data->ntextures; i++)
515 glDeleteTextures (1, &data->texid[i]);