1 /* texfonts, Copyright (c) 2005 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
21 #include "resources.h"
24 /* These are in xlock-gl.c */
25 extern void clear_gl_error (void);
26 extern void check_gl_error (const char *type);
29 extern char *progname;
31 struct texture_font_data {
34 int cell_width, cell_height; /* maximal charcell */
35 int tex_width, tex_height; /* size of each texture */
37 int grid_mag; /* 1, 2, 4, or 8 */
38 int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */
44 /* return the next larger power of 2. */
48 static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
49 2048, 4096, 8192, 16384, 32768, 65536 };
51 for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
52 if (pow2[j] >= i) return pow2[j];
53 abort(); /* too big! */
57 /* Given a Pixmap of depth 1, converts it to an OpenGL luminance mipmap.
58 The 1 bits are drawn, the 0 bits are alpha.
59 Pass in the size of the pixmap; the size of the texture is returned
60 (it may be larger, since GL like powers of 2.)
63 bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP)
68 int w2 = to_pow2 (ow);
69 int h2 = to_pow2 (oh);
71 XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, XYPixmap);
72 unsigned char *data = (unsigned char *) calloc (w2, (h2 + 1));
73 unsigned char *out = data;
74 GLuint iformat = GL_INTENSITY;
75 GLuint format = GL_LUMINANCE;
76 GLuint type = GL_UNSIGNED_BYTE;
78 for (y = 0; y < h2; y++)
79 for (x = 0; x < w2; x++)
80 *out++ = (x >= ow || y >= oh ? 0 :
81 XGetPixel (image, x, y) ? 255 : 0);
82 XDestroyImage (image);
86 gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
88 glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
92 sprintf (msg, "texture font %s (%d x %d)",
93 mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
99 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
100 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
101 mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
103 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
104 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
113 /* Loads the font named by the X resource "res" and returns
114 a texture-font object.
117 load_texture_font (Display *dpy, char *res)
119 texture_font_data *data = 0;
120 const char *font = get_string_resource (res, "Font");
121 const char *def1 = "-*-times-bold-r-normal-*-240-*";
122 const char *def2 = "-*-times-bold-r-normal-*-180-*";
123 const char *def3 = "fixed";
127 check_gl_error ("stale texture font");
129 if (!res || !*res) abort();
130 if (!font) font = def1;
132 f = XLoadQueryFont(dpy, font);
133 if (!f && !!strcmp (font, def1))
135 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
136 progname, font, def1);
138 f = XLoadQueryFont(dpy, font);
141 if (!f && !!strcmp (font, def2))
143 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
144 progname, font, def2);
146 f = XLoadQueryFont(dpy, font);
149 if (!f && !!strcmp (font, def3))
151 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
152 progname, font, def3);
154 f = XLoadQueryFont(dpy, font);
159 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
164 data = (texture_font_data *) calloc (1, sizeof(*data));
168 /* Figure out how many textures to use.
169 E.g., if we need 1024x1024 bits, use four 512x512 textures,
170 to be gentle to machines with low texture size limits.
173 int w = to_pow2 (16 * (f->max_bounds.rbearing - f->min_bounds.lbearing));
174 int h = to_pow2 (16 * (f->max_bounds.ascent + f->max_bounds.descent));
175 int i = (w > h ? w : h);
177 if (i <= 512) data->grid_mag = 1; /* 1 tex of 16x16 chars */
178 else if (i <= 1024) data->grid_mag = 2; /* 4 tex of 8x8 chars */
179 else if (i <= 2048) data->grid_mag = 4; /* 16 tex of 4x4 chars */
180 else data->grid_mag = 8; /* 32 tex of 2x2 chars */
182 data->ntextures = data->grid_mag * data->grid_mag;
186 "%s: %dx%d grid of %d textures of %dx%d chars (%dx%d bits)\n",
188 data->grid_mag, data->grid_mag,
190 16 / data->grid_mag, 16 / data->grid_mag,
195 for (which = 0; which < data->ntextures; which++)
197 /* Create a pixmap big enough to fit every character in the font.
198 (modulo the "ntextures" scaling.)
199 Make it square-ish, since GL likes dimensions to be powers of 2.
201 Screen *screen = DefaultScreenOfDisplay (dpy);
202 Window root = RootWindowOfScreen (screen);
206 int cw = f->max_bounds.rbearing - f->min_bounds.lbearing;
207 int ch = f->max_bounds.ascent + f->max_bounds.descent;
208 int grid_size = (16 / data->grid_mag);
209 int w = cw * grid_size;
210 int h = ch * grid_size;
213 data->cell_width = cw;
214 data->cell_height = ch;
216 p = XCreatePixmap (dpy, root, w, h, 1);
220 gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
221 XFillRectangle (dpy, p, gc, 0, 0, w, h);
222 XSetForeground (dpy, gc, 1);
223 for (i = 0; i < 256 / data->ntextures; i++)
225 int ii = (i + (which * 256 / data->ntextures));
227 int x = (i % grid_size) * cw;
228 int y = (i / grid_size) * ch;
230 /* See comment in print_texture_string for bit layout explanation.
232 int lbearing = (f->per_char
233 ? f->per_char[ii - f->min_char_or_byte2].lbearing
234 : f->min_bounds.lbearing);
235 int ascent = (f->per_char
236 ? f->per_char[ii - f->min_char_or_byte2].ascent
237 : f->max_bounds.ascent);
238 int width = (f->per_char
239 ? f->per_char[ii - f->min_char_or_byte2].width
240 : f->max_bounds.width);
242 if (width == 0) continue;
243 XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1);
247 glGenTextures (1, &data->texid[which]);
248 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
249 check_gl_error ("texture font load");
251 data->tex_height = h;
253 #if 0 /* debugging: splat the bitmap onto the desktop root window */
255 Window win = RootWindow (dpy, 0);
256 GC gc2 = XCreateGC (dpy, win, 0, &gcv);
257 XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
258 XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
259 XCopyPlane (dpy, p, win, gc2, 0, 0, w, h, 0, 0, 1);
266 bitmap_to_texture (dpy, p, &data->tex_width, &data->tex_height);
267 XFreePixmap (dpy, p);
274 /* Width of the string in pixels.
277 texture_string_width (texture_font_data *data, const char *c,
278 int *line_height_ret)
281 XFontStruct *f = data->font;
284 int cc = *((unsigned char *) c);
286 ? f->per_char[cc-f->min_char_or_byte2].width
287 : f->max_bounds.width);
291 *line_height_ret = f->ascent + f->descent;
296 /* Draws the string in the scene at the origin.
297 Newlines and tab stops are honored.
300 print_texture_string (texture_font_data *data, const char *string)
302 XFontStruct *f = data->font;
303 GLfloat line_height = f->ascent + f->descent;
304 # ifdef DO_SUBSCRIPTS
305 GLfloat sub_shift = (line_height * 0.3);
307 # endif /* DO_SUBSCRIPTS */
308 int cw = texture_string_width (data, "m", 0);
317 glNormal3f (0, 0, 1);
321 for (i = 0; i < strlen(string); i++)
323 unsigned char c = string[i];
331 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
333 # ifdef DO_SUBSCRIPTS
334 else if (c == '[' && (isdigit (string[i+1])))
339 else if (c == ']' && sub_p)
344 # endif /* DO_SUBSCRIPTS */
347 /* For a small font, the texture is divided into 16x16 rectangles
348 whose size are the max_bounds charcell of the font. Within each
349 rectangle, the individual characters' charcells sit in the upper
352 For a larger font, the texture will itself be subdivided, to
353 keep the texture sizes small (in that case we deal with, e.g.,
354 4 grids of 8x8 characters instead of 1 grid of 16x16.)
358 [A]----------------------------
369 |----[B]----------|---| |
373 |--------------------[C] |
376 ------------------------------ cell_height
378 We want to make a quad from point A to point C.
379 We want to position that quad so that point B lies at x,y.
381 int lbearing = (f->per_char
382 ? f->per_char[c - f->min_char_or_byte2].lbearing
383 : f->min_bounds.lbearing);
384 int rbearing = (f->per_char
385 ? f->per_char[c - f->min_char_or_byte2].rbearing
386 : f->max_bounds.rbearing);
387 int ascent = (f->per_char
388 ? f->per_char[c - f->min_char_or_byte2].ascent
389 : f->max_bounds.ascent);
390 int descent = (f->per_char
391 ? f->per_char[c - f->min_char_or_byte2].descent
392 : f->max_bounds.descent);
393 int cwidth = (f->per_char
394 ? f->per_char[c - f->min_char_or_byte2].width
395 : f->max_bounds.width);
397 char cc = c % (256 / data->ntextures);
399 int gs = (16 / data->grid_mag); /* grid size */
401 int ax = ((int) cc % gs) * data->cell_width; /* point A */
402 int ay = ((int) cc / gs) * data->cell_height;
404 int bx = ax - lbearing; /* point B */
405 int by = ay + ascent;
407 int cx = bx + rbearing; /* point C */
408 int cy = by + descent;
410 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
411 GLfloat tay = (GLfloat) ay / data->tex_height;
413 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
414 GLfloat tcy = (GLfloat) cy / data->tex_height;
416 GLfloat qx0 = x + lbearing; /* quad top left */
417 GLfloat qy0 = y + ascent;
418 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
419 GLfloat qy1 = qy0 - (ascent + descent);
421 if (cwidth > 0 && c != ' ')
423 int which = c / (256 / data->ntextures);
424 if (which >= data->ntextures) abort();
425 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
428 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
429 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
430 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
431 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
434 glDisable(GL_TEXTURE_2D);
435 glBegin (GL_LINE_LOOP);
436 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
437 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
438 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
439 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
441 glEnable(GL_TEXTURE_2D);
450 check_gl_error ("texture font print");
453 /* Releases the font and texture.
456 free_texture_font (texture_font_data *data)
460 XFreeFont (data->dpy, data->font);
461 for (i = 0; i < data->ntextures; i++)
463 glDeleteTextures (1, &data->texid[i]);