1 /* texfonts, Copyright (c) 2005-2013 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: write the bitmap to a pgm file */
322 sprintf (file, "/tmp/%02d.pgm", which);
323 image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
324 ff = fopen (file, "w");
325 fprintf (ff, "P5\n%d %d\n255\n", w, h);
326 for (y = 0; y < h; y++)
327 for (x = 0; x < w; x++) {
328 unsigned long pix = XGetPixel (image, x, y);
329 unsigned long r = (pix & xgwa.visual->red_mask);
330 r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
331 fprintf (ff, "%c", (char) r);
334 XDestroyImage (image);
335 fprintf (stderr, "%s: wrote %s (%d x %d)\n", progname, file,
336 f->max_bounds.rbearing - f->min_bounds.lbearing,
337 f->max_bounds.ascent + f->max_bounds.descent);
341 bitmap_to_texture (dpy, p, xgwa.visual,
342 &data->tex_width, &data->tex_height);
343 XFreePixmap (dpy, p);
346 /* Reset to the caller's default */
347 glBindTexture (GL_TEXTURE_2D, old_texture);
353 /* Width of the string in pixels.
356 texture_string_width (texture_font_data *data, const char *c,
361 XFontStruct *f = data->font;
362 int h = f->ascent + f->descent;
365 int cc = *((unsigned char *) c);
368 if (x > max_w) max_w = x;
370 h += f->ascent + f->descent;
373 x += (f->per_char && cc >= f->min_char_or_byte2
374 ? f->per_char[cc-f->min_char_or_byte2].width
375 : f->min_bounds.rbearing);
378 if (x > max_w) max_w = x;
379 if (height_ret) *height_ret = h;
385 /* Draws the string in the scene at the current point.
386 Newlines, tab stops and subscripts are honored.
389 print_texture_string (texture_font_data *data, const char *string)
391 XFontStruct *f = data->font;
392 GLfloat line_height = f->ascent + f->descent;
393 # ifdef DO_SUBSCRIPTS
394 GLfloat sub_shift = (line_height * 0.3);
396 # endif /* DO_SUBSCRIPTS */
397 int cw = texture_string_width (data, "m", 0);
401 GLint old_texture = 0;
405 glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
406 glGetIntegerv (GL_FRONT_FACE, &ofront);
407 glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
413 glNormal3f (0, 0, 1);
416 glMatrixMode (GL_TEXTURE);
418 glMatrixMode (GL_MODELVIEW);
422 for (i = 0; i < strlen(string); i++)
424 unsigned char c = string[i];
433 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
435 # ifdef DO_SUBSCRIPTS
436 else if (c == '[' && (isdigit (string[i+1])))
441 else if (c == ']' && sub_p)
446 # endif /* DO_SUBSCRIPTS */
449 /* For a small font, the texture is divided into 16x16 rectangles
450 whose size are the max_bounds charcell of the font. Within each
451 rectangle, the individual characters' charcells sit in the upper
454 For a larger font, the texture will itself be subdivided, to
455 keep the texture sizes small (in that case we deal with, e.g.,
456 4 grids of 8x8 characters instead of 1 grid of 16x16.)
460 [A]----------------------------
471 |----[B]----------|---| |
475 |--------------------[C] |
478 ------------------------------ cell_height
480 We want to make a quad from point A to point C.
481 We want to position that quad so that point B lies at x,y.
483 int lbearing = (f->per_char && c >= f->min_char_or_byte2
484 ? f->per_char[c - f->min_char_or_byte2].lbearing
485 : f->min_bounds.lbearing);
486 int rbearing = (f->per_char && c >= f->min_char_or_byte2
487 ? f->per_char[c - f->min_char_or_byte2].rbearing
488 : f->max_bounds.rbearing);
489 int ascent = (f->per_char && c >= f->min_char_or_byte2
490 ? f->per_char[c - f->min_char_or_byte2].ascent
491 : f->max_bounds.ascent);
492 int descent = (f->per_char && c >= f->min_char_or_byte2
493 ? f->per_char[c - f->min_char_or_byte2].descent
494 : f->max_bounds.descent);
495 int cwidth = (f->per_char && c >= f->min_char_or_byte2
496 ? f->per_char[c - f->min_char_or_byte2].width
497 : f->max_bounds.width);
499 unsigned char cc = c % (256 / data->ntextures);
501 int gs = (16 / data->grid_mag); /* grid size */
503 int ax = ((int) cc % gs) * data->cell_width; /* point A */
504 int ay = ((int) cc / gs) * data->cell_height;
506 int bx = ax - lbearing; /* point B */
507 int by = ay + ascent;
509 int cx = bx + rbearing + 1; /* point C */
510 int cy = by + descent + 1;
512 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
513 GLfloat tay = (GLfloat) ay / data->tex_height;
515 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
516 GLfloat tcy = (GLfloat) cy / data->tex_height;
518 GLfloat qx0 = x + lbearing; /* quad top left */
519 GLfloat qy0 = y + ascent;
520 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
521 GLfloat qy1 = qy0 - (ascent + descent);
523 if (cwidth > 0 && c != ' ')
525 int which = c / (256 / data->ntextures);
526 if (which >= data->ntextures) abort();
527 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
530 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
531 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
532 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
533 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
536 glDisable(GL_TEXTURE_2D);
537 glBegin (GL_LINE_LOOP);
538 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
539 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
540 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
541 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
543 glEnable(GL_TEXTURE_2D);
552 /* Reset to the caller's default */
553 glBindTexture (GL_TEXTURE_2D, old_texture);
554 glFrontFace (ofront);
556 glMatrixMode (GL_TEXTURE);
557 glMultMatrixf (omatrix);
558 glMatrixMode (GL_MODELVIEW);
560 check_gl_error ("texture font print");
563 /* Releases the font and texture.
566 free_texture_font (texture_font_data *data)
570 XFreeFont (data->dpy, data->font);
571 for (i = 0; i < data->ntextures; i++)
573 glDeleteTextures (1, &data->texid[i]);