1 /* texfonts, Copyright (c) 2005-2014 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Renders X11 fonts into textures for use with OpenGL.
12 * A higher level API is in glxfonts.c.
28 # include <OpenGL/glu.h>
37 #endif /* HAVE_JWZGLES */
39 #include "resources.h"
45 /* These are in xlock-gl.c */
46 extern void clear_gl_error (void);
47 extern void check_gl_error (const char *type);
50 extern char *progname;
52 struct texture_font_data {
55 int cell_width, cell_height; /* maximal charcell */
56 int tex_width, tex_height; /* size of each texture */
58 int grid_mag; /* 1, 2, 4, or 8 */
59 int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */
61 GLuint texid[64]; /* must hold ntextures */
65 /* return the next larger power of 2. */
69 static const unsigned int pow2[] = {
70 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
71 2048, 4096, 8192, 16384, 32768, 65536 };
73 for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
74 if (pow2[j] >= i) return pow2[j];
75 abort(); /* too big! */
79 /* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
80 RGB are averaged to grayscale, and the resulting value is treated as alpha.
81 Pass in the size of the pixmap; the size of the texture is returned
82 (it may be larger, since GL like powers of 2.)
84 We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
85 were drawn with antialiasing, that is preserved.
88 bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
93 int w2 = to_pow2 (ow);
94 int h2 = to_pow2 (oh);
96 XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
97 unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
98 unsigned char *out = data;
100 /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
101 texture with 1 byte per pixel, the intensity value, we have
102 to use 2 bytes per pixel: solid white, and an alpha value.
109 GLuint iformat = GL_INTENSITY;
110 GLuint format = GL_LUMINANCE;
112 GLuint iformat = GL_LUMINANCE_ALPHA;
113 GLuint format = GL_LUMINANCE_ALPHA;
115 GLuint type = GL_UNSIGNED_BYTE;
118 /* This would work, but it's wasteful for no benefit. */
122 for (y = 0; y < h2; y++)
123 for (x = 0; x < w2; x++) {
124 unsigned long pixel = (x >= ow || y >= oh ? 0 : XGetPixel (image, x, y));
125 /* instead of averaging all three channels, let's just use red,
126 and assume it was already grayscale. */
127 unsigned long r = pixel & visual->red_mask;
128 /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
129 pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
130 # ifndef GL_INTENSITY
131 *out++ = 0xFF; /* 2 bytes per pixel */
135 XDestroyImage (image);
139 gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format, type, data);
141 glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format, type, data);
145 sprintf (msg, "texture font %s (%d x %d)",
146 mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
148 check_gl_error (msg);
152 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
153 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
154 mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
157 /* This makes scaled font pixmaps tolerable to look at.
158 LOD bias is part of OpenGL 1.4.
159 GL_EXT_texture_lod_bias has been present since the original iPhone.
161 # if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
162 # define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
164 # ifdef GL_TEXTURE_LOD_BIAS
166 glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
168 clear_gl_error(); /* invalid enum on iPad 3 */
170 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
171 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
180 static texture_font_data *
181 load_texture_xfont (Display *dpy, XFontStruct *f)
183 Screen *screen = DefaultScreenOfDisplay (dpy);
184 Window root = RootWindowOfScreen (screen);
185 XWindowAttributes xgwa;
187 GLint old_texture = 0;
188 texture_font_data *data = 0;
190 glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
192 XGetWindowAttributes (dpy, root, &xgwa);
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 && ii >= f->min_char_or_byte2
261 ? f->per_char[ii - f->min_char_or_byte2].lbearing
262 : f->min_bounds.lbearing);
263 int ascent = (f->per_char && ii >= f->min_char_or_byte2
264 ? f->per_char[ii - f->min_char_or_byte2].ascent
265 : f->max_bounds.ascent);
266 int width = (f->per_char && ii >= f->min_char_or_byte2
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: write the bitmap to a pgm file */
287 sprintf (file, "/tmp/%02d.pgm", which);
288 image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
289 ff = fopen (file, "w");
290 fprintf (ff, "P5\n%d %d\n255\n", w, h);
291 for (y = 0; y < h; y++)
292 for (x = 0; x < w; x++) {
293 unsigned long pix = XGetPixel (image, x, y);
294 unsigned long r = (pix & xgwa.visual->red_mask);
295 r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
296 fprintf (ff, "%c", (char) r);
299 XDestroyImage (image);
300 fprintf (stderr, "%s: wrote %s (%d x %d)\n", progname, file,
301 f->max_bounds.rbearing - f->min_bounds.lbearing,
302 f->max_bounds.ascent + f->max_bounds.descent);
306 bitmap_to_texture (dpy, p, xgwa.visual,
307 &data->tex_width, &data->tex_height);
308 XFreePixmap (dpy, p);
311 /* Reset to the caller's default */
312 glBindTexture (GL_TEXTURE_2D, old_texture);
318 /* Loads the font named by the X resource "res" and returns
319 a texture-font object.
322 load_texture_font (Display *dpy, char *res)
324 char *font = get_string_resource (dpy, res, "Font");
325 const char *def1 = "-*-helvetica-medium-r-normal-*-240-*";
326 const char *def2 = "-*-helvetica-medium-r-normal-*-180-*";
327 const char *def3 = "fixed";
330 if (!strcmp (res, "fpsFont"))
331 def1 = "-*-courier-bold-r-normal-*-180-*"; /* Kludge. Sue me. */
333 if (!res || !*res) abort();
334 if (!font) font = strdup(def1);
336 f = XLoadQueryFont(dpy, font);
337 if (!f && !!strcmp (font, def1))
339 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
340 progname, font, def1);
342 font = strdup (def1);
343 f = XLoadQueryFont(dpy, font);
346 if (!f && !!strcmp (font, def2))
348 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
349 progname, font, def2);
351 font = strdup (def2);
352 f = XLoadQueryFont(dpy, font);
355 if (!f && !!strcmp (font, def3))
357 fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
358 progname, font, def3);
360 font = strdup (def3);
361 f = XLoadQueryFont(dpy, font);
366 fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
374 return load_texture_xfont (dpy, f);
378 /* Bounding box of the multi-line string, in pixels.
381 texture_string_width (texture_font_data *data, const char *c, int *height_ret)
383 XFontStruct *f = data->font;
386 int lh = f->ascent + f->descent;
390 int cc = *((unsigned char *) c);
393 if (x > max_w) max_w = x;
399 ? f->per_char[cc-f->min_char_or_byte2].width
400 : f->min_bounds.rbearing);
403 if (x > max_w) max_w = x;
404 if (height_ret) *height_ret = h;
409 /* Draws the string in the scene at the origin.
410 Newlines and tab stops are honored.
411 Any numbers inside [] will be rendered as a subscript.
412 Assumes the font has been loaded as with load_texture_font().
415 print_texture_string (texture_font_data *data, const char *string)
417 XFontStruct *f = data->font;
418 GLfloat line_height = f->ascent + f->descent;
419 # ifdef DO_SUBSCRIPTS
420 GLfloat sub_shift = (line_height * 0.3);
422 # endif /* DO_SUBSCRIPTS */
423 int cw = texture_string_width (data, "m", 0);
427 GLint old_texture = 0;
431 glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
432 glGetIntegerv (GL_FRONT_FACE, &ofront);
433 glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
439 glNormal3f (0, 0, 1);
442 glMatrixMode (GL_TEXTURE);
444 glMatrixMode (GL_MODELVIEW);
448 for (i = 0; i < strlen(string); i++)
450 unsigned char c = string[i];
459 x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
461 # ifdef DO_SUBSCRIPTS
462 else if (c == '[' && (isdigit (string[i+1])))
467 else if (c == ']' && sub_p)
472 # endif /* DO_SUBSCRIPTS */
475 /* For a small font, the texture is divided into 16x16 rectangles
476 whose size are the max_bounds charcell of the font. Within each
477 rectangle, the individual characters' charcells sit in the upper
480 For a larger font, the texture will itself be subdivided, to
481 keep the texture sizes small (in that case we deal with, e.g.,
482 4 grids of 8x8 characters instead of 1 grid of 16x16.)
486 [A]----------------------------
497 |----[B]----------|---| |
501 |--------------------[C] |
504 ------------------------------ cell_height
506 We want to make a quad from point A to point C.
507 We want to position that quad so that point B lies at x,y.
509 int lbearing = (f->per_char && c >= f->min_char_or_byte2
510 ? f->per_char[c - f->min_char_or_byte2].lbearing
511 : f->min_bounds.lbearing);
512 int rbearing = (f->per_char && c >= f->min_char_or_byte2
513 ? f->per_char[c - f->min_char_or_byte2].rbearing
514 : f->max_bounds.rbearing);
515 int ascent = (f->per_char && c >= f->min_char_or_byte2
516 ? f->per_char[c - f->min_char_or_byte2].ascent
517 : f->max_bounds.ascent);
518 int descent = (f->per_char && c >= f->min_char_or_byte2
519 ? f->per_char[c - f->min_char_or_byte2].descent
520 : f->max_bounds.descent);
521 int cwidth = (f->per_char && c >= f->min_char_or_byte2
522 ? f->per_char[c - f->min_char_or_byte2].width
523 : f->max_bounds.width);
525 unsigned char cc = c % (256 / data->ntextures);
527 int gs = (16 / data->grid_mag); /* grid size */
529 int ax = ((int) cc % gs) * data->cell_width; /* point A */
530 int ay = ((int) cc / gs) * data->cell_height;
532 int bx = ax - lbearing; /* point B */
533 int by = ay + ascent;
535 int cx = bx + rbearing + 1; /* point C */
536 int cy = by + descent + 1;
538 GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
539 GLfloat tay = (GLfloat) ay / data->tex_height;
541 GLfloat tcx = (GLfloat) cx / data->tex_width; /* tex coords of C */
542 GLfloat tcy = (GLfloat) cy / data->tex_height;
544 GLfloat qx0 = x + lbearing; /* quad top left */
545 GLfloat qy0 = y + ascent;
546 GLfloat qx1 = qx0 + rbearing - lbearing; /* quad bot right */
547 GLfloat qy1 = qy0 - (ascent + descent);
549 if (cwidth > 0 && c != ' ')
551 int which = c / (256 / data->ntextures);
552 if (which >= data->ntextures) abort();
553 glBindTexture (GL_TEXTURE_2D, data->texid[which]);
556 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
557 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
558 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
559 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
562 glDisable(GL_TEXTURE_2D);
563 glBegin (GL_LINE_LOOP);
564 glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0);
565 glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0);
566 glTexCoord2f (tcx, tcy); glVertex3f (qx1, qy1, 0);
567 glTexCoord2f (tax, tcy); glVertex3f (qx0, qy1, 0);
569 glEnable(GL_TEXTURE_2D);
578 /* Reset to the caller's default */
579 glBindTexture (GL_TEXTURE_2D, old_texture);
580 glFrontFace (ofront);
582 glMatrixMode (GL_TEXTURE);
583 glMultMatrixf (omatrix);
584 glMatrixMode (GL_MODELVIEW);
586 check_gl_error ("texture font print");
589 /* Releases the font and texture.
592 free_texture_font (texture_font_data *data)
596 for (i = 0; i < data->ntextures; i++)
598 glDeleteTextures (1, &data->texid[i]);
601 XFreeFont (data->dpy, data->font);