-/* texfonts, Copyright (c) 2005 Jamie Zawinski <jwz@jwz.org>
- * Loads X11 fonts into textures for use with OpenGL.
+/* texfonts, Copyright (c) 2005-2014 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
+ *
+ * Renders X11 fonts into textures for use with OpenGL.
+ * A higher level API is in glxfonts.c.
*/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
-#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
-#include <GL/glx.h>
-#include <GL/glu.h>
+
+#ifdef HAVE_COCOA
+# ifdef USE_IPHONE
+# include "jwzgles.h"
+# else
+# include <OpenGL/glu.h>
+# endif
+#else
+# include <GL/glx.h>
+# include <GL/glu.h>
+#endif
+
+#ifdef HAVE_JWZGLES
+# include "jwzgles.h"
+#endif /* HAVE_JWZGLES */
+
#include "resources.h"
#include "texfont.h"
+#define DO_SUBSCRIPTS
+
+
/* These are in xlock-gl.c */
extern void clear_gl_error (void);
extern void check_gl_error (const char *type);
int grid_mag; /* 1, 2, 4, or 8 */
int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */
- GLuint texid[32];
+ GLuint texid[64]; /* must hold ntextures */
};
static int
to_pow2 (int i)
{
- static unsigned int pow2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
- 2048, 4096, 8192, 16384, 32768, 65536 };
+ static const unsigned int pow2[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384, 32768, 65536 };
int j;
for (j = 0; j < sizeof(pow2)/sizeof(*pow2); j++)
if (pow2[j] >= i) return pow2[j];
}
-/* Given a Pixmap of depth 1, converts it to an OpenGL luminance mipmap.
- The 1 bits are drawn, the 0 bits are alpha.
+/* Given a Pixmap (of screen depth), converts it to an OpenGL luminance mipmap.
+ RGB are averaged to grayscale, and the resulting value is treated as alpha.
Pass in the size of the pixmap; the size of the texture is returned
(it may be larger, since GL like powers of 2.)
+
+ We use a screen-depth pixmap instead of a 1bpp bitmap so that if the fonts
+ were drawn with antialiasing, that is preserved.
*/
static void
-bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP)
+bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int *wP, int *hP)
{
Bool mipmap_p = True;
int ow = *wP;
int w2 = to_pow2 (ow);
int h2 = to_pow2 (oh);
int x, y;
- XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, XYPixmap);
- unsigned char *data = (unsigned char *) calloc (w2, (h2 + 1));
+ XImage *image = XGetImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap);
+ unsigned char *data = (unsigned char *) calloc (w2 * 2, (h2 + 1));
unsigned char *out = data;
+
+ /* OpenGLES doesn't support GL_INTENSITY, so instead of using a
+ texture with 1 byte per pixel, the intensity value, we have
+ to use 2 bytes per pixel: solid white, and an alpha value.
+ */
+# ifdef HAVE_JWZGLES
+# undef GL_INTENSITY
+# endif
+
+# ifdef GL_INTENSITY
GLuint iformat = GL_INTENSITY;
- GLuint format = GL_LUMINANCE;
- GLuint type = GL_UNSIGNED_BYTE;
+ GLuint format = GL_LUMINANCE;
+# else
+ GLuint iformat = GL_LUMINANCE_ALPHA;
+ GLuint format = GL_LUMINANCE_ALPHA;
+# endif
+ GLuint type = GL_UNSIGNED_BYTE;
+
+# ifdef HAVE_JWZGLES
+ /* This would work, but it's wasteful for no benefit. */
+ mipmap_p = False;
+# endif
for (y = 0; y < h2; y++)
- for (x = 0; x < w2; x++)
- *out++ = (x >= ow || y >= oh ? 0 :
- XGetPixel (image, x, y) ? 255 : 0);
+ for (x = 0; x < w2; x++) {
+ unsigned long pixel = (x >= ow || y >= oh ? 0 : XGetPixel (image, x, y));
+ /* instead of averaging all three channels, let's just use red,
+ and assume it was already grayscale. */
+ unsigned long r = pixel & visual->red_mask;
+ /* This goofy trick is to make any of RGBA/ABGR/ARGB work. */
+ pixel = ((r >> 24) | (r >> 16) | (r >> 8) | r) & 0xFF;
+# ifndef GL_INTENSITY
+ *out++ = 0xFF; /* 2 bytes per pixel */
+# endif
+ *out++ = pixel;
+ }
XDestroyImage (image);
image = 0;
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
+
+ /* This makes scaled font pixmaps tolerable to look at.
+ LOD bias is part of OpenGL 1.4.
+ GL_EXT_texture_lod_bias has been present since the original iPhone.
+ */
+# if !defined(GL_TEXTURE_LOD_BIAS) && defined(GL_TEXTURE_LOD_BIAS_EXT)
+# define GL_TEXTURE_LOD_BIAS GL_TEXTURE_LOD_BIAS_EXT
+# endif
+# ifdef GL_TEXTURE_LOD_BIAS
+ if (mipmap_p)
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.25);
+# endif
+ clear_gl_error(); /* invalid enum on iPad 3 */
+
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
-/* Loads the font named by the X resource "res" and returns
- a texture-font object.
-*/
-texture_font_data *
-load_texture_font (Display *dpy, char *res)
+static texture_font_data *
+load_texture_xfont (Display *dpy, XFontStruct *f)
{
- texture_font_data *data = 0;
- const char *font = get_string_resource (res, "Font");
- const char *def1 = "-*-times-bold-r-normal-*-240-*";
- const char *def2 = "-*-times-bold-r-normal-*-180-*";
- const char *def3 = "fixed";
- XFontStruct *f;
+ Screen *screen = DefaultScreenOfDisplay (dpy);
+ Window root = RootWindowOfScreen (screen);
+ XWindowAttributes xgwa;
int which;
+ GLint old_texture = 0;
+ texture_font_data *data = 0;
- check_gl_error ("stale texture font");
-
- if (!res || !*res) abort();
- if (!font) font = def1;
+ glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
- f = XLoadQueryFont(dpy, font);
- if (!f && !!strcmp (font, def1))
- {
- fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
- progname, font, def1);
- font = def1;
- f = XLoadQueryFont(dpy, font);
- }
-
- if (!f && !!strcmp (font, def2))
- {
- fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
- progname, font, def2);
- font = def2;
- f = XLoadQueryFont(dpy, font);
- }
-
- if (!f && !!strcmp (font, def3))
- {
- fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
- progname, font, def3);
- font = def3;
- f = XLoadQueryFont(dpy, font);
- }
-
- if (!f)
- {
- fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
- progname, font);
- exit (1);
- }
+ XGetWindowAttributes (dpy, root, &xgwa);
data = (texture_font_data *) calloc (1, sizeof(*data));
data->dpy = dpy;
(modulo the "ntextures" scaling.)
Make it square-ish, since GL likes dimensions to be powers of 2.
*/
- Screen *screen = DefaultScreenOfDisplay (dpy);
- Window root = RootWindowOfScreen (screen);
XGCValues gcv;
GC gc;
Pixmap p;
data->cell_width = cw;
data->cell_height = ch;
- p = XCreatePixmap (dpy, root, w, h, 1);
+ p = XCreatePixmap (dpy, root, w, h, xgwa.depth);
gcv.font = f->fid;
- gcv.foreground = 0;
- gcv.background = 0;
+ gcv.foreground = BlackPixelOfScreen (xgwa.screen);
+ gcv.background = BlackPixelOfScreen (xgwa.screen);
gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv);
XFillRectangle (dpy, p, gc, 0, 0, w, h);
- XSetForeground (dpy, gc, 1);
+ XSetForeground (dpy, gc, WhitePixelOfScreen (xgwa.screen));
for (i = 0; i < 256 / data->ntextures; i++)
{
int ii = (i + (which * 256 / data->ntextures));
/* See comment in print_texture_string for bit layout explanation.
*/
- int lbearing = (f->per_char
+ int lbearing = (f->per_char && ii >= f->min_char_or_byte2
? f->per_char[ii - f->min_char_or_byte2].lbearing
: f->min_bounds.lbearing);
- int ascent = (f->per_char
+ int ascent = (f->per_char && ii >= f->min_char_or_byte2
? f->per_char[ii - f->min_char_or_byte2].ascent
: f->max_bounds.ascent);
- int width = (f->per_char
+ int width = (f->per_char && ii >= f->min_char_or_byte2
? f->per_char[ii - f->min_char_or_byte2].width
: f->max_bounds.width);
data->tex_width = w;
data->tex_height = h;
-#if 0 /* debugging: splat the bitmap onto the desktop root window */
+#if 0 /* debugging: write the bitmap to a pgm file */
{
- Window win = RootWindow (dpy, 0);
- GC gc2 = XCreateGC (dpy, win, 0, &gcv);
- XSetForeground (dpy, gc2, BlackPixel (dpy, 0));
- XSetBackground (dpy, gc2, WhitePixel (dpy, 0));
- XCopyPlane (dpy, p, win, gc2, 0, 0, w, h, 0, 0, 1);
- XFreeGC (dpy, gc2);
- XSync(dpy, False);
- usleep (100000);
+ char file[255];
+ XImage *image;
+ int x, y;
+ FILE *ff;
+ sprintf (file, "/tmp/%02d.pgm", which);
+ image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap);
+ ff = fopen (file, "w");
+ fprintf (ff, "P5\n%d %d\n255\n", w, h);
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++) {
+ unsigned long pix = XGetPixel (image, x, y);
+ unsigned long r = (pix & xgwa.visual->red_mask);
+ r = ((r >> 24) | (r >> 16) | (r >> 8) | r);
+ fprintf (ff, "%c", (char) r);
+ }
+ fclose (ff);
+ XDestroyImage (image);
+ fprintf (stderr, "%s: wrote %s (%d x %d)\n", progname, file,
+ f->max_bounds.rbearing - f->min_bounds.lbearing,
+ f->max_bounds.ascent + f->max_bounds.descent);
}
-#endif
+#endif /* 0 */
- bitmap_to_texture (dpy, p, &data->tex_width, &data->tex_height);
+ bitmap_to_texture (dpy, p, xgwa.visual,
+ &data->tex_width, &data->tex_height);
XFreePixmap (dpy, p);
}
+ /* Reset to the caller's default */
+ glBindTexture (GL_TEXTURE_2D, old_texture);
+
return data;
}
-/* Width of the string in pixels.
+/* Loads the font named by the X resource "res" and returns
+ a texture-font object.
+*/
+texture_font_data *
+load_texture_font (Display *dpy, char *res)
+{
+ char *font = get_string_resource (dpy, res, "Font");
+ const char *def1 = "-*-helvetica-medium-r-normal-*-240-*";
+ const char *def2 = "-*-helvetica-medium-r-normal-*-180-*";
+ const char *def3 = "fixed";
+ XFontStruct *f;
+
+ if (!strcmp (res, "fpsFont"))
+ def1 = "-*-courier-bold-r-normal-*-180-*"; /* Kludge. Sue me. */
+
+ if (!res || !*res) abort();
+ if (!font) font = strdup(def1);
+
+ f = XLoadQueryFont(dpy, font);
+ if (!f && !!strcmp (font, def1))
+ {
+ fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+ progname, font, def1);
+ free (font);
+ font = strdup (def1);
+ f = XLoadQueryFont(dpy, font);
+ }
+
+ if (!f && !!strcmp (font, def2))
+ {
+ fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+ progname, font, def2);
+ free (font);
+ font = strdup (def2);
+ f = XLoadQueryFont(dpy, font);
+ }
+
+ if (!f && !!strcmp (font, def3))
+ {
+ fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
+ progname, font, def3);
+ free (font);
+ font = strdup (def3);
+ f = XLoadQueryFont(dpy, font);
+ }
+
+ if (!f)
+ {
+ fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
+ progname, font);
+ exit (1);
+ }
+
+ free (font);
+ font = 0;
+
+ return load_texture_xfont (dpy, f);
+}
+
+
+/* Bounding box of the multi-line string, in pixels.
*/
int
-texture_string_width (texture_font_data *data, const char *c,
- int *line_height_ret)
+texture_string_width (texture_font_data *data, const char *c, int *height_ret)
{
- int w = 0;
XFontStruct *f = data->font;
+ int x = 0;
+ int max_w = 0;
+ int lh = f->ascent + f->descent;
+ int h = lh;
while (*c)
{
int cc = *((unsigned char *) c);
- w += (f->per_char
- ? f->per_char[cc-f->min_char_or_byte2].width
- : f->max_bounds.width);
+ if (*c == '\n')
+ {
+ if (x > max_w) max_w = x;
+ x = 0;
+ h += lh;
+ }
+ else
+ x += (f->per_char
+ ? f->per_char[cc-f->min_char_or_byte2].width
+ : f->min_bounds.rbearing);
c++;
}
- if (line_height_ret)
- *line_height_ret = f->ascent + f->descent;
- return w;
+ if (x > max_w) max_w = x;
+ if (height_ret) *height_ret = h;
+ return max_w;
}
/* Draws the string in the scene at the origin.
Newlines and tab stops are honored.
+ Any numbers inside [] will be rendered as a subscript.
+ Assumes the font has been loaded as with load_texture_font().
*/
void
print_texture_string (texture_font_data *data, const char *string)
int tabs = cw * 7;
int x, y;
unsigned int i;
+ GLint old_texture = 0;
+ GLfloat omatrix[16];
+ int ofront;
+
+ glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture);
+ glGetIntegerv (GL_FRONT_FACE, &ofront);
+ glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
clear_gl_error ();
glPushMatrix();
glNormal3f (0, 0, 1);
+ glFrontFace (GL_CW);
+
+ glMatrixMode (GL_TEXTURE);
+ glLoadIdentity ();
+ glMatrixMode (GL_MODELVIEW);
x = 0;
y = 0;
}
else if (c == '\t')
{
- x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
+ if (tabs)
+ x = ((x + tabs) / tabs) * tabs; /* tab to tab stop */
}
# ifdef DO_SUBSCRIPTS
else if (c == '[' && (isdigit (string[i+1])))
We want to make a quad from point A to point C.
We want to position that quad so that point B lies at x,y.
*/
- int lbearing = (f->per_char
+ int lbearing = (f->per_char && c >= f->min_char_or_byte2
? f->per_char[c - f->min_char_or_byte2].lbearing
: f->min_bounds.lbearing);
- int rbearing = (f->per_char
+ int rbearing = (f->per_char && c >= f->min_char_or_byte2
? f->per_char[c - f->min_char_or_byte2].rbearing
: f->max_bounds.rbearing);
- int ascent = (f->per_char
+ int ascent = (f->per_char && c >= f->min_char_or_byte2
? f->per_char[c - f->min_char_or_byte2].ascent
: f->max_bounds.ascent);
- int descent = (f->per_char
+ int descent = (f->per_char && c >= f->min_char_or_byte2
? f->per_char[c - f->min_char_or_byte2].descent
: f->max_bounds.descent);
- int cwidth = (f->per_char
+ int cwidth = (f->per_char && c >= f->min_char_or_byte2
? f->per_char[c - f->min_char_or_byte2].width
: f->max_bounds.width);
- char cc = c % (256 / data->ntextures);
+ unsigned char cc = c % (256 / data->ntextures);
int gs = (16 / data->grid_mag); /* grid size */
int bx = ax - lbearing; /* point B */
int by = ay + ascent;
- int cx = bx + rbearing; /* point C */
- int cy = by + descent;
+ int cx = bx + rbearing + 1; /* point C */
+ int cy = by + descent + 1;
GLfloat tax = (GLfloat) ax / data->tex_width; /* tex coords of A */
GLfloat tay = (GLfloat) ay / data->tex_height;
}
glPopMatrix();
+ /* Reset to the caller's default */
+ glBindTexture (GL_TEXTURE_2D, old_texture);
+ glFrontFace (ofront);
+
+ glMatrixMode (GL_TEXTURE);
+ glMultMatrixf (omatrix);
+ glMatrixMode (GL_MODELVIEW);
+
check_gl_error ("texture font print");
}
free_texture_font (texture_font_data *data)
{
int i;
- if (data->font)
- XFreeFont (data->dpy, data->font);
+
for (i = 0; i < data->ntextures; i++)
if (data->texid[i])
glDeleteTextures (1, &data->texid[i]);
+
+ if (data->font)
+ XFreeFont (data->dpy, data->font);
+
free (data);
}