1 /* texfonts, Copyright (c) 2005-2012 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
27 # include <OpenGL/glu.h>
36 #endif /* HAVE_JWZGLES */
38 #include "resources.h"
41 /* These are in xlock-gl.c */
42 extern void clear_gl_error (void);
43 extern void check_gl_error (const char *type);
46 extern char *progname;
48 struct texture_font_data {
51 int cell_width, cell_height; /* maximal charcell */
52 int tex_width, tex_height; /* size of each texture */
54 int grid_mag; /* 1, 2, 4, or 8 */
55 int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */
57 GLuint texid[64]; /* must hold ntextures */
61 /* return the next larger power of 2. */
65 static const unsigned int pow2[] = {
66 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
67 2048, 4096, 8192, 16384, 32768, 65536 };
69 for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
70 if (pow2[j] >= i) return pow2[j];
71 abort(); /* too big! */
75 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
76 RGB are averaged to grayscale, and the resulting value is treated as alpha.
77 Pass in the size of the pixmap; the size of the texture is returned
78 (it may be larger, since GL like powers of 2.)
80 We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
81 were drawn with antialiasing, that is preserved.
84 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
89 int w2 = to_pow2 (ow);
90 int h2 = to_pow2 (oh);
92 XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
93 unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
94 unsigned char *out = data;
96 /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
97 texture with 1 byte per pixel, the intensity value, we have
98 to use 2 bytes per pixel: solid white, and an alpha value.
105 GLuint iformat = GL_INTENSITY;
106 GLuint format = GL_LUMINANCE;
108 GLuint iformat = GL_LUMINANCE_ALPHA;
109 GLuint format = GL_LUMINANCE_ALPHA;
111 GLuint type = GL_UNSIGNED_BYTE;
114 /* This would work, but it's wasteful for no benefit. */
118 for (y = 0; y < h2; y++)
119 for (x = 0; x < w2; x++) {
120 unsigned long pixel = (x >= ow || y >= oh ? 0 : XGetPixel (image, x, y));
121 /* instead of averaging all three channels, let's just use red,
122 and assume it was already grayscale. */
123 unsigned long r = pixel & visual->red_mask;
124 /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
125 pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
126 # ifndef GL_INTENSITY
127 *out++ = 0xFF; /* 2 bytes per pixel */
131 XDestroyImage (image);
135 gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
137 glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
141 sprintf (msg, "texture font %s (%d x %d)",
142 mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
144 check_gl_error (msg);
148 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
149 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
150 mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
152 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
153 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
162 /* Loads the font named by the X resource "res" and returns
163 a texture-font object.
166 load_texture_font (Display *dpy, char *res)
168 Screen *screen = DefaultScreenOfDisplay (dpy);
169 Window root = RootWindowOfScreen (screen);
170 XWindowAttributes xgwa;
172 texture_font_data *data = 0;
173 char *font = get_string_resource (dpy, res, "Font");
174 const char *def1 = "-*-helvetica-medium-r-normal-*-240-*";
175 const char *def2 = "-*-helvetica-medium-r-normal-*-180-*";
176 const char *def3 = "fixed";
179 GLint old_texture = 0;
181 glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
183 if (!strcmp (res, "fpsFont"))
184 def1 = "-*-courier-bold-r-normal-*-180-*"; /* Kludge. Sue me. */
186 XGetWindowAttributes (dpy, root, &xgwa);
188 if (!res || !*res) abort();
189 if (!font) font = strdup(def1);
191 f = XLoadQueryFont(dpy, font);
192 if (!f && !!strcmp (font, def1))
194 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
195 progname, font, def1);
197 font = strdup (def1);
198 f = XLoadQueryFont(dpy, font);
201 if (!f && !!strcmp (font, def2))
203 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
204 progname, font, def2);
206 font = strdup (def2);
207 f = XLoadQueryFont(dpy, font);
210 if (!f && !!strcmp (font, def3))
212 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
213 progname, font, def3);
215 font = strdup (def3);
216 f = XLoadQueryFont(dpy, font);
221 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
229 data = (texture_font_data *) calloc (1, sizeof(*data));
233 /* Figure out how many textures to use.
234 E.g., if we need 1024x1024 bits, use four 512x512 textures,
235 to be gentle to machines with low texture size limits.
238 int w = to_pow2 (16 * (f->max_bounds.rbearing - f->min_bounds.lbearing));
239 int h = to_pow2 (16 * (f->max_bounds.ascent + f->max_bounds.descent));
240 int i = (w > h ? w : h);
242 if (i <= 512) data->grid_mag = 1; /* 1 tex of 16x16 chars */
243 else if (i <= 1024) data->grid_mag = 2; /* 4 tex of 8x8 chars */
244 else if (i <= 2048) data->grid_mag = 4; /* 16 tex of 4x4 chars */
245 else data->grid_mag = 8; /* 32 tex of 2x2 chars */
247 data->ntextures = data->grid_mag * data->grid_mag;
251 "%s: %dx%d grid of %d textures of %dx%d chars (%dx%d bits)\n",
253 data->grid_mag, data->grid_mag,
255 16 / data->grid_mag, 16 / data->grid_mag,
260 for (which = 0; which < data->ntextures; which++)
262 /* Create a pixmap big enough to fit every character in the font.
263 (modulo the "ntextures" scaling.)
264 Make it square-ish, since GL likes dimensions to be powers of 2.
269 int cw = f->max_bounds.rbearing - f->min_bounds.lbearing;
270 int ch = f->max_bounds.ascent + f->max_bounds.descent;
271 int grid_size = (16 / data->grid_mag);
272 int w = cw * grid_size;
273 int h = ch * grid_size;
276 data->cell_width = cw;
277 data->cell_height = ch;
279 p = XCreatePixmap (dpy, root, w, h, xgwa.depth);
281 gcv.foreground = BlackPixelOfScreen (xgwa.screen);
282 gcv.background = BlackPixelOfScreen (xgwa.screen);
283 gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
284 XFillRectangle (dpy, p, gc, 0, 0, w, h);
285 XSetForeground (dpy, gc, WhitePixelOfScreen (xgwa.screen));
286 for (i = 0; i < 256 / data->ntextures; i++)
288 int ii = (i + (which * 256 / data->ntextures));
290 int x = (i % grid_size) * cw;
291 int y = (i / grid_size) * ch;
293 /* See comment in print_texture_string for bit layout explanation.
295 int lbearing = (f->per_char && ii >= f->min_char_or_byte2
296 ? f->per_char[ii - f->min_char_or_byte2].lbearing
297 : f->min_bounds.lbearing);
298 int ascent = (f->per_char && ii >= f->min_char_or_byte2
299 ? f->per_char[ii - f->min_char_or_byte2].ascent
300 : f->max_bounds.ascent);
301 int width = (f->per_char && ii >= f->min_char_or_byte2
302 ? f->per_char[ii - f->min_char_or_byte2].width
303 : f->max_bounds.width);
305 if (width == 0) continue;
306 XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1);
310 glGenTextures (1, &data->texid[which]);
311 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
312 check_gl_error ("texture font load");
314 data->tex_height = h;
316 #if 0 /* debugging: splat the bitmap onto the desktop root window */
318 Window win = RootWindow (dpy, 0);
319 GC gc2 = XCreateGC (dpy, win, 0, &gcv);
320 XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
321 XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
322 XCopyArea (dpy, p, win, gc2, 0, 0, w, h, 0, 0);
329 #if 0 /* debugging: write the bitmap to a pgm file */
335 sprintf (file, "/tmp/%02d.pgm", which);
336 image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
337 f = fopen (file, "w");
338 fprintf (f, "P5\n%d %d\n255\n", w, h);
339 for (y = 0; y < h; y++)
340 for (x = 0; x < w; x++) {
341 unsigned long pix = XGetPixel (image, x, y);
342 unsigned long r = (pix & xgwa.visual->red_mask);
343 r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
344 fprintf (f, "%c", (char) r);
347 XDestroyImage (image);
348 fprintf (stderr, "%s: wrote %s\n", progname, file);
352 bitmap_to_texture (dpy, p, xgwa.visual,
353 &data->tex_width, &data->tex_height);
354 XFreePixmap (dpy, p);
357 /* Reset to the caller's default */
358 glBindTexture (GL_TEXTURE_2D, old_texture);
364 /* Width of the string in pixels.
367 texture_string_width (texture_font_data *data, const char *c,
372 XFontStruct *f = data->font;
373 int h = f->ascent + f->descent;
376 int cc = *((unsigned char *) c);
379 if (x > max_w) max_w = x;
381 h += f->ascent + f->descent;
384 x += (f->per_char && cc >= f->min_char_or_byte2
385 ? f->per_char[cc-f->min_char_or_byte2].width
386 : f->min_bounds.rbearing);
389 if (x > max_w) max_w = x;
390 if (height_ret) *height_ret = h;
396 /* Draws the string in the scene at the current point.
397 Newlines, tab stops and subscripts are honored.
400 print_texture_string (texture_font_data *data, const char *string)
402 XFontStruct *f = data->font;
403 GLfloat line_height = f->ascent + f->descent;
404 # ifdef DO_SUBSCRIPTS
405 GLfloat sub_shift = (line_height * 0.3);
407 # endif /* DO_SUBSCRIPTS */
408 int cw = texture_string_width (data, "m", 0);
412 GLint old_texture = 0;
416 glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
417 glGetIntegerv (GL_FRONT_FACE, &ofront);
418 glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
424 glNormal3f (0, 0, 1);
427 glMatrixMode (GL_TEXTURE);
429 glMatrixMode (GL_MODELVIEW);
433 for (i = 0; i < strlen(string); i++)
435 unsigned char c = string[i];
443 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
445 # ifdef DO_SUBSCRIPTS
446 else if (c == '[' && (isdigit (string[i+1])))
451 else if (c == ']' && sub_p)
456 # endif /* DO_SUBSCRIPTS */
459 /* For a small font, the texture is divided into 16x16 rectangles
460 whose size are the max_bounds charcell of the font. Within each
461 rectangle, the individual characters' charcells sit in the upper
464 For a larger font, the texture will itself be subdivided, to
465 keep the texture sizes small (in that case we deal with, e.g.,
466 4 grids of 8x8 characters instead of 1 grid of 16x16.)
470 [A]----------------------------
481 |----[B]----------|---| |
485 |--------------------[C] |
488 ------------------------------ cell_height
490 We want to make a quad from point A to point C.
491 We want to position that quad so that point B lies at x,y.
493 int lbearing = (f->per_char && c >= f->min_char_or_byte2
494 ? f->per_char[c - f->min_char_or_byte2].lbearing
495 : f->min_bounds.lbearing);
496 int rbearing = (f->per_char && c >= f->min_char_or_byte2
497 ? f->per_char[c - f->min_char_or_byte2].rbearing
498 : f->max_bounds.rbearing);
499 int ascent = (f->per_char && c >= f->min_char_or_byte2
500 ? f->per_char[c - f->min_char_or_byte2].ascent
501 : f->max_bounds.ascent);
502 int descent = (f->per_char && c >= f->min_char_or_byte2
503 ? f->per_char[c - f->min_char_or_byte2].descent
504 : f->max_bounds.descent);
505 int cwidth = (f->per_char && c >= f->min_char_or_byte2
506 ? f->per_char[c - f->min_char_or_byte2].width
507 : f->max_bounds.width);
509 unsigned char cc = c % (256 / data->ntextures);
511 int gs = (16 / data->grid_mag); /* grid size */
513 int ax = ((int) cc % gs) * data->cell_width; /* point A */
514 int ay = ((int) cc / gs) * data->cell_height;
516 int bx = ax - lbearing; /* point B */
517 int by = ay + ascent;
519 int cx = bx + rbearing; /* point C */
520 int cy = by + descent;
522 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
523 GLfloat tay = (GLfloat) ay / data->tex_height;
525 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
526 GLfloat tcy = (GLfloat) cy / data->tex_height;
528 GLfloat qx0 = x + lbearing; /* quad top left */
529 GLfloat qy0 = y + ascent;
530 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
531 GLfloat qy1 = qy0 - (ascent + descent);
533 if (cwidth > 0 && c != ' ')
535 int which = c / (256 / data->ntextures);
536 if (which >= data->ntextures) abort();
537 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
540 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
541 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
542 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
543 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
546 glDisable(GL_TEXTURE_2D);
547 glBegin (GL_LINE_LOOP);
548 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
549 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
550 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
551 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
553 glEnable(GL_TEXTURE_2D);
562 /* Reset to the caller's default */
563 glBindTexture (GL_TEXTURE_2D, old_texture);
564 glFrontFace (ofront);
566 glMatrixMode (GL_TEXTURE);
567 glMultMatrixf (omatrix);
568 glMatrixMode (GL_MODELVIEW);
570 check_gl_error ("texture font print");
573 /* Releases the font and texture.
576 free_texture_font (texture_font_data *data)
580 XFreeFont (data->dpy, data->font);
581 for (i = 0; i < data->ntextures; i++)
583 glDeleteTextures (1, &data->texid[i]);