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: 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];
444 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
446 # ifdef DO_SUBSCRIPTS
447 else if (c == '[' && (isdigit (string[i+1])))
452 else if (c == ']' && sub_p)
457 # endif /* DO_SUBSCRIPTS */
460 /* For a small font, the texture is divided into 16x16 rectangles
461 whose size are the max_bounds charcell of the font. Within each
462 rectangle, the individual characters' charcells sit in the upper
465 For a larger font, the texture will itself be subdivided, to
466 keep the texture sizes small (in that case we deal with, e.g.,
467 4 grids of 8x8 characters instead of 1 grid of 16x16.)
471 [A]----------------------------
482 |----[B]----------|---| |
486 |--------------------[C] |
489 ------------------------------ cell_height
491 We want to make a quad from point A to point C.
492 We want to position that quad so that point B lies at x,y.
494 int lbearing = (f->per_char && c >= f->min_char_or_byte2
495 ? f->per_char[c - f->min_char_or_byte2].lbearing
496 : f->min_bounds.lbearing);
497 int rbearing = (f->per_char && c >= f->min_char_or_byte2
498 ? f->per_char[c - f->min_char_or_byte2].rbearing
499 : f->max_bounds.rbearing);
500 int ascent = (f->per_char && c >= f->min_char_or_byte2
501 ? f->per_char[c - f->min_char_or_byte2].ascent
502 : f->max_bounds.ascent);
503 int descent = (f->per_char && c >= f->min_char_or_byte2
504 ? f->per_char[c - f->min_char_or_byte2].descent
505 : f->max_bounds.descent);
506 int cwidth = (f->per_char && c >= f->min_char_or_byte2
507 ? f->per_char[c - f->min_char_or_byte2].width
508 : f->max_bounds.width);
510 unsigned char cc = c % (256 / data->ntextures);
512 int gs = (16 / data->grid_mag); /* grid size */
514 int ax = ((int) cc % gs) * data->cell_width; /* point A */
515 int ay = ((int) cc / gs) * data->cell_height;
517 int bx = ax - lbearing; /* point B */
518 int by = ay + ascent;
520 int cx = bx + rbearing; /* point C */
521 int cy = by + descent;
523 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
524 GLfloat tay = (GLfloat) ay / data->tex_height;
526 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
527 GLfloat tcy = (GLfloat) cy / data->tex_height;
529 GLfloat qx0 = x + lbearing; /* quad top left */
530 GLfloat qy0 = y + ascent;
531 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
532 GLfloat qy1 = qy0 - (ascent + descent);
534 if (cwidth > 0 && c != ' ')
536 int which = c / (256 / data->ntextures);
537 if (which >= data->ntextures) abort();
538 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
541 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
542 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
543 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
544 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
547 glDisable(GL_TEXTURE_2D);
548 glBegin (GL_LINE_LOOP);
549 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
550 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
551 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
552 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
554 glEnable(GL_TEXTURE_2D);
563 /* Reset to the caller's default */
564 glBindTexture (GL_TEXTURE_2D, old_texture);
565 glFrontFace (ofront);
567 glMatrixMode (GL_TEXTURE);
568 glMultMatrixf (omatrix);
569 glMatrixMode (GL_MODELVIEW);
571 check_gl_error ("texture font print");
574 /* Releases the font and texture.
577 free_texture_font (texture_font_data *data)
581 XFreeFont (data->dpy, data->font);
582 for (i = 0; i < data->ntextures; i++)
584 glDeleteTextures (1, &data->texid[i]);