X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Ftexfont.c;h=2305a937769d1a9e637147ee156af168e5b2bc76;hb=4ade52359b6eba3621566dac79793a33aa4c915f;hp=29cf5336c950015c3de72b834114fe274eb06255;hpb=2d04c4f22466851aedb6ed0f2919d148f726b889;p=xscreensaver diff --git a/hacks/glx/texfont.c b/hacks/glx/texfont.c index 29cf5336..2305a937 100644 --- a/hacks/glx/texfont.c +++ b/hacks/glx/texfont.c @@ -1,4 +1,4 @@ -/* texfonts, Copyright (c) 2005 Jamie Zawinski +/* texfonts, Copyright (c) 2005-2013 Jamie Zawinski * Loads X11 fonts into textures for use with OpenGL. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -11,13 +11,30 @@ */ -#include "config.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include #include -#include -#include + +#ifdef HAVE_COCOA +# ifdef USE_IPHONE +# include "jwzgles.h" +# else +# include +# endif +#else +# include +# include +#endif + +#ifdef HAVE_JWZGLES +# include "jwzgles.h" +#endif /* HAVE_JWZGLES */ + #include "resources.h" #include "texfont.h" @@ -31,9 +48,13 @@ extern char *progname; struct texture_font_data { Display *dpy; XFontStruct *font; - GLuint texid; - int cell_width, cell_height; - int tex_width, tex_height; + int cell_width, cell_height; /* maximal charcell */ + int tex_width, tex_height; /* size of each texture */ + + int grid_mag; /* 1, 2, 4, or 8 */ + int ntextures; /* 1, 4, 16, or 64 (grid_mag ^ 2) */ + + GLuint texid[64]; /* must hold ntextures */ }; @@ -41,8 +62,9 @@ struct texture_font_data { 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]; @@ -50,13 +72,16 @@ to_pow2 (int i) } -/* 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; @@ -64,17 +89,45 @@ bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP) 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; @@ -85,7 +138,7 @@ bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP) { char msg[100]; - sprintf (msg, "%s (%d x %d)", + sprintf (msg, "texture font %s (%d x %d)", mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D", w2, h2); check_gl_error (msg); @@ -112,22 +165,36 @@ bitmap_to_texture (Display *dpy, Pixmap p, int *wP, int *hP) texture_font_data * load_texture_font (Display *dpy, char *res) { + Screen *screen = DefaultScreenOfDisplay (dpy); + Window root = RootWindowOfScreen (screen); + XWindowAttributes xgwa; + 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-*"; + 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; + int which; + GLint old_texture = 0; + + glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture); + + if (!strcmp (res, "fpsFont")) + def1 = "-*-courier-bold-r-normal-*-180-*"; /* Kludge. Sue me. */ + + XGetWindowAttributes (dpy, root, &xgwa); if (!res || !*res) abort(); - if (!font) font = def1; + 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); - font = def1; + free (font); + font = strdup (def1); f = XLoadQueryFont(dpy, font); } @@ -135,7 +202,8 @@ load_texture_font (Display *dpy, char *res) { fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n", progname, font, def2); - font = def2; + free (font); + font = strdup (def2); f = XLoadQueryFont(dpy, font); } @@ -143,7 +211,8 @@ load_texture_font (Display *dpy, char *res) { fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n", progname, font, def3); - font = def3; + free (font); + font = strdup (def3); f = XLoadQueryFont(dpy, font); } @@ -154,78 +223,139 @@ load_texture_font (Display *dpy, char *res) exit (1); } + free (font); + font = 0; + data = (texture_font_data *) calloc (1, sizeof(*data)); data->dpy = dpy; data->font = f; - /* Create a pixmap big enough to fit every character in the font. - Make it square-ish, since GL likes dimensions to be powers of 2. + /* Figure out how many textures to use. + E.g., if we need 1024x1024 bits, use four 512x512 textures, + to be gentle to machines with low texture size limits. */ { - Screen *screen = DefaultScreenOfDisplay (dpy); - Window root = RootWindowOfScreen (screen); - XGCValues gcv; - GC gc; - Pixmap p; - int cw = f->max_bounds.rbearing - f->min_bounds.lbearing; - int ch = f->max_bounds.ascent + f->max_bounds.descent; - int w = cw * 16; - int h = ch * 16; - int i; - - data->cell_width = cw; - data->cell_height = ch; - - p = XCreatePixmap (dpy, root, w, h, 1); - gcv.font = f->fid; - gcv.foreground = 0; - gcv.background = 0; - gc = XCreateGC (dpy, p, (GCFont|GCForeground|GCBackground), &gcv); - XFillRectangle (dpy, p, gc, 0, 0, w, h); - XSetForeground (dpy, gc, 1); - for (i = 0; i < 256; i++) + int w = to_pow2 (16 * (f->max_bounds.rbearing - f->min_bounds.lbearing)); + int h = to_pow2 (16 * (f->max_bounds.ascent + f->max_bounds.descent)); + int i = (w > h ? w : h); + + if (i <= 512) data->grid_mag = 1; /* 1 tex of 16x16 chars */ + else if (i <= 1024) data->grid_mag = 2; /* 4 tex of 8x8 chars */ + else if (i <= 2048) data->grid_mag = 4; /* 16 tex of 4x4 chars */ + else data->grid_mag = 8; /* 32 tex of 2x2 chars */ + + data->ntextures = data->grid_mag * data->grid_mag; + +# if 0 + fprintf (stderr, + "%s: %dx%d grid of %d textures of %dx%d chars (%dx%d bits)\n", + progname, + data->grid_mag, data->grid_mag, + data->ntextures, + 16 / data->grid_mag, 16 / data->grid_mag, + i, i); +# endif + } + + for (which = 0; which < data->ntextures; which++) + { + /* Create a pixmap big enough to fit every character in the font. + (modulo the "ntextures" scaling.) + Make it square-ish, since GL likes dimensions to be powers of 2. + */ + XGCValues gcv; + GC gc; + Pixmap p; + int cw = f->max_bounds.rbearing - f->min_bounds.lbearing; + int ch = f->max_bounds.ascent + f->max_bounds.descent; + int grid_size = (16 / data->grid_mag); + int w = cw * grid_size; + int h = ch * grid_size; + int i; + + data->cell_width = cw; + data->cell_height = ch; + + p = XCreatePixmap (dpy, root, w, h, xgwa.depth); + gcv.font = f->fid; + 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, WhitePixelOfScreen (xgwa.screen)); + for (i = 0; i < 256 / data->ntextures; i++) + { + int ii = (i + (which * 256 / data->ntextures)); + char c = (char) ii; + int x = (i % grid_size) * cw; + int y = (i / grid_size) * ch; + + /* See comment in print_texture_string for bit layout explanation. + */ + 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 && 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 && ii >= f->min_char_or_byte2 + ? f->per_char[ii - f->min_char_or_byte2].width + : f->max_bounds.width); + + if (width == 0) continue; + XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1); + } + XFreeGC (dpy, gc); + + glGenTextures (1, &data->texid[which]); + glBindTexture (GL_TEXTURE_2D, data->texid[which]); + check_gl_error ("texture font load"); + data->tex_width = w; + data->tex_height = h; + +#if 0 /* debugging: splat the bitmap onto the desktop root window */ { - char c = (char) i; - int x = (i % 16) * cw; - int y = (i / 16) * ch; - - /* See comment in print_texture_string for bit layout explanation. */ - - int lbearing = (f->per_char - ? f->per_char[i - f->min_char_or_byte2].lbearing - : f->min_bounds.lbearing); - int ascent = (f->per_char - ? f->per_char[i - f->min_char_or_byte2].ascent - : f->max_bounds.ascent); - int width = (f->per_char - ? f->per_char[i - f->min_char_or_byte2].width - : f->max_bounds.width); - - if (width == 0) continue; - XDrawString (dpy, p, gc, x - lbearing, y + ascent, &c, 1); + Window win = RootWindow (dpy, 0); + GC gc2 = XCreateGC (dpy, win, 0, &gcv); + XSetForeground (dpy, gc2, BlackPixel (dpy, 0)); + XSetBackground (dpy, gc2, WhitePixel (dpy, 0)); + XCopyArea (dpy, p, win, gc2, 0, 0, w, h, 0, 0); + XFreeGC (dpy, gc2); + XSync(dpy, False); + usleep (100000); } - XFreeGC (dpy, gc); +#endif - glGenTextures (1, &data->texid); - glBindTexture (GL_TEXTURE_2D, data->texid); - data->tex_width = w; - data->tex_height = h; +#if 0 /* debugging: write the bitmap to a pgm file */ + { + char file[255]; + XImage *image; + int x, y; + FILE *f; + sprintf (file, "/tmp/%02d.pgm", which); + image = XGetImage (dpy, p, 0, 0, w, h, ~0L, ZPixmap); + f = fopen (file, "w"); + fprintf (f, "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 (f, "%c", (char) r); + } + fclose (f); + XDestroyImage (image); + fprintf (stderr, "%s: wrote %s\n", progname, file); + } +#endif -#if 0 /* debugging: splat the bitmap onto the desktop root window */ - { - 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); + bitmap_to_texture (dpy, p, xgwa.visual, + &data->tex_width, &data->tex_height); + XFreePixmap (dpy, p); } -#endif - bitmap_to_texture (dpy, p, &data->tex_width, &data->tex_height); - XFreePixmap (dpy, p); - } + /* Reset to the caller's default */ + glBindTexture (GL_TEXTURE_2D, old_texture); return data; } @@ -235,26 +365,36 @@ load_texture_font (Display *dpy, char *res) */ int texture_string_width (texture_font_data *data, const char *c, - int *line_height_ret) + int *height_ret) { - int w = 0; + int x = 0; + int max_w = 0; XFontStruct *f = data->font; + int h = f->ascent + f->descent; 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 += f->ascent + f->descent; + } + else + x += (f->per_char && cc >= f->min_char_or_byte2 + ? 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. +/* Draws the string in the scene at the current point. + Newlines, tab stops and subscripts are honored. */ void print_texture_string (texture_font_data *data, const char *string) @@ -269,17 +409,30 @@ 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(); - glBindTexture (GL_TEXTURE_2D, data->texid); glNormal3f (0, 0, 1); + glFrontFace (GL_CW); + + glMatrixMode (GL_TEXTURE); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); x = 0; y = 0; for (i = 0; i < strlen(string); i++) { - char c = string[i]; + unsigned char c = string[i]; if (c == '\n') { y -= line_height; @@ -287,7 +440,8 @@ print_texture_string (texture_font_data *data, const char *string) } 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]))) @@ -303,9 +457,16 @@ print_texture_string (texture_font_data *data, const char *string) # endif /* DO_SUBSCRIPTS */ else { - /* The texture is divided into 16x16 rectangles whose size are - the max_bounds charcell of the font. Within each rectangle, - the individual characters' charcells sit in the upper left. + /* For a small font, the texture is divided into 16x16 rectangles + whose size are the max_bounds charcell of the font. Within each + rectangle, the individual characters' charcells sit in the upper + left. + + For a larger font, the texture will itself be subdivided, to + keep the texture sizes small (in that case we deal with, e.g., + 4 grids of 8x8 characters instead of 1 grid of 16x16.) + + Within each texture: [A]---------------------------- | | | | | @@ -330,24 +491,28 @@ print_texture_string (texture_font_data *data, const char *string) 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); - int ax = ((int) c % 16) * data->cell_width; /* point A */ - int ay = ((int) c / 16) * data->cell_height; + unsigned char cc = c % (256 / data->ntextures); + + int gs = (16 / data->grid_mag); /* grid size */ + + int ax = ((int) cc % gs) * data->cell_width; /* point A */ + int ay = ((int) cc / gs) * data->cell_height; int bx = ax - lbearing; /* point B */ int by = ay + ascent; @@ -368,6 +533,10 @@ print_texture_string (texture_font_data *data, const char *string) if (cwidth > 0 && c != ' ') { + int which = c / (256 / data->ntextures); + if (which >= data->ntextures) abort(); + glBindTexture (GL_TEXTURE_2D, data->texid[which]); + glBegin (GL_QUADS); glTexCoord2f (tax, tay); glVertex3f (qx0, qy0, 0); glTexCoord2f (tcx, tay); glVertex3f (qx1, qy0, 0); @@ -390,6 +559,16 @@ print_texture_string (texture_font_data *data, const char *string) } } 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"); } /* Releases the font and texture. @@ -397,9 +576,11 @@ print_texture_string (texture_font_data *data, const char *string) void free_texture_font (texture_font_data *data) { + int i; if (data->font) XFreeFont (data->dpy, data->font); - if (data->texid) - glDeleteTextures (1, &data->texid); + for (i = 0; i < data->ntextures; i++) + if (data->texid[i]) + glDeleteTextures (1, &data->texid[i]); free (data); }