X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Ftexfont.c;h=b70a32d53358c9eaff2e13740ee35d30a22a7be6;hp=2592626954058272a489aa44e20433d71287da2a;hb=d1ae2829ff0fd2a96c16a0c8c5420efaa47d7b30;hpb=7edd66e6bd3209013ee059819747b10b5835635b diff --git a/hacks/glx/texfont.c b/hacks/glx/texfont.c index 25926269..b70a32d5 100644 --- a/hacks/glx/texfont.c +++ b/hacks/glx/texfont.c @@ -1,4 +1,4 @@ -/* texfonts, Copyright (c) 2005-2014 Jamie Zawinski +/* texfonts, Copyright (c) 2005-2015 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -65,8 +65,8 @@ typedef struct texfont_cache texfont_cache; struct texfont_cache { char *string; GLuint texid; - int width, height; - int width2, height2; + XCharStruct extents; + int tex_width, tex_height; texfont_cache *next; }; @@ -154,7 +154,6 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth, # endif /* HAVE_XSHM_EXTENSION */ # ifdef HAVE_XSHM_EXTENSION -# error XX if (use_shm) { image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info, @@ -174,7 +173,10 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth, mipmap_p = False; # endif - for (y = 0; y < h2; y++) +# ifdef DUMP_BITMAPS + fprintf (stderr, "\n"); +# endif + for (y = 0; y < h2; y++) { for (x = 0; x < w2; x++) { /* Might be better to average a scale x scale square of source pixels, but at the resolutions we're dealing with, this is probably good @@ -188,11 +190,36 @@ bitmap_to_texture (Display *dpy, Pixmap p, Visual *visual, int depth, 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; + +# ifdef DUMP_BITMAPS + if (sx < ow && sy < oh) +# ifdef HAVE_COCOA + fprintf (stderr, "%c", + r >= 0xFF000000 ? '#' : + r >= 0x88000000 ? '%' : + r ? '.' : ' '); +# else + fprintf (stderr, "%c", + r >= 0xFF0000 ? '#' : + r >= 0x880000 ? '%' : + r ? '.' : ' '); +# endif +# endif + +# if 0 /* Debugging checkerboard */ + if (sx < ow && sy < oh && (((sx / 4) & 1) ^ ((sy / 4) & 1))) + pixel = 0x3F; +# endif + # ifndef GL_INTENSITY *out++ = 0xFF; /* 2 bytes per pixel (luminance, alpha) */ # endif *out++ = pixel; } +# ifdef DUMP_BITMAPS + fprintf (stderr, "\n"); +# endif + } # ifdef HAVE_XSHM_EXTENSION if (use_shm) @@ -299,28 +326,31 @@ load_texture_font (Display *dpy, char *res) } -/* Measure the string, or render it, depending on whether the XftDraw - is supplied. +/* Measure the string, returning the overall metrics. + Newlines and tab stops are honored. + Any numbers inside [] will be rendered as a subscript. + + The origin is at the origin of the first character, so subsequent + lines of a multi-line string look like descenders (below baseline). + + If an XftDraw is supplied, render the string as well, at X,Y. + Positive Y is down (X11 style, not OpenGL style). */ -static int +static void iterate_texture_string (texture_font_data *data, - const char *s, + const char *s, + int draw_x, int draw_y, XftDraw *xftdraw, XftColor *xftcolor, - int x, int y, - int *height_ret) + XCharStruct *metrics_ret) { int line_height = data->xftfont->ascent + data->xftfont->descent; - int left = x; - int max_x, ox, oy; + int subscript_offset = line_height * 0.3; const char *os = s; Bool sub_p = False, osub_p = False; int cw = 0, tabs = 0; - XGlyphInfo extents; - - y += line_height; - max_x = x; - ox = x; - oy = y; + XCharStruct overall = { 0, }; + int x = 0, y = 0; + int ox = x, oy = y; while (1) { @@ -330,19 +360,39 @@ iterate_texture_string (texture_font_data *data, (*s == '[' && isdigit(s[1])) || (*s == ']' && sub_p)) { - if (s == os) - extents.xOff = 0; - else - XftTextExtentsUtf8 (data->dpy, data->xftfont, - (FcChar8 *) os, (int) (s - os), - &extents); - x += extents.xOff; - if (x > max_x) - max_x = x; + if (s != os) + { + XGlyphInfo e; + XCharStruct c; + int y2 = y; + if (sub_p) y2 += subscript_offset; + + XftTextExtentsUtf8 (data->dpy, data->xftfont, + (FcChar8 *) os, (int) (s - os), + &e); + c.lbearing = -e.x; /* XGlyphInfo to XCharStruct */ + c.rbearing = e.width - e.x; + c.ascent = e.y; + c.descent = e.height - e.y; + c.width = e.xOff; + +# undef MAX +# undef MIN +# define MAX(A,B) ((A)>(B)?(A):(B)) +# define MIN(A,B) ((A)<(B)?(A):(B)) + overall.ascent = MAX (overall.ascent, -y2 + c.ascent); + overall.descent = MAX (overall.descent, y2 + c.descent); + overall.lbearing = MIN (overall.lbearing, (x + c.lbearing)); + overall.rbearing = MAX (overall.rbearing, x + c.rbearing); + overall.width = MAX (overall.width, x + c.width); + x += c.width; +# undef MAX +# undef MIN + } if (*s == '\n') { - x = left; + x = 0; y += line_height; sub_p = False; } @@ -351,9 +401,10 @@ iterate_texture_string (texture_font_data *data, if (! cw) { /* Measure "m" to determine tab width. */ + XGlyphInfo e; XftTextExtentsUtf8 (data->dpy, data->xftfont, - (FcChar8 *) "m", 1, &extents); - cw = extents.xOff; + (FcChar8 *) "m", 1, &e); + cw = e.xOff; if (cw <= 0) cw = 1; tabs = cw * 7; } @@ -366,8 +417,9 @@ iterate_texture_string (texture_font_data *data, if (xftdraw && s != os) XftDrawStringUtf8 (xftdraw, xftcolor, data->xftfont, - ox, - oy + (int) (osub_p ? line_height * 0.3 : 0), + draw_x + ox, + draw_y + + oy + (osub_p ? subscript_offset : 0), (FcChar8 *) os, (int) (s - os)); if (!*s) break; os = s+1; @@ -378,21 +430,30 @@ iterate_texture_string (texture_font_data *data, s++; } - if (height_ret) - *height_ret = y; - return max_x; + if (metrics_ret) + *metrics_ret = overall; } -/* Bounding box of the multi-line string, in pixels. +/* Bounding box of the multi-line string, in pixels, + and overall ascent/descent of the font. */ -int -texture_string_width (texture_font_data *data, const char *s, int *height_ret) +void +texture_string_metrics (texture_font_data *data, const char *s, + XCharStruct *metrics_ret, + int *ascent_ret, int *descent_ret) { - return iterate_texture_string (data, s, 0, 0, 0, 0, height_ret); + if (metrics_ret) + iterate_texture_string (data, s, 0, 0, 0, 0, metrics_ret); + if (ascent_ret) *ascent_ret = data->xftfont->ascent; + if (descent_ret) *descent_ret = data->xftfont->descent; } +/* Returns a cache entry for this string, with a valid texid. + If the returned entry has a string in it, the texture is valid. + Otherwise it is an empty entry waiting to be rendered. + */ static struct texfont_cache * get_cache (texture_font_data *data, const char *string) { @@ -425,11 +486,10 @@ get_cache (texture_font_data *data, const char *string) if (count > data->cache_size) { free (prev->string); - prev->string = 0; - prev->width = 0; - prev->height = 0; - prev->width2 = 0; - prev->height2 = 0; + prev->string = 0; + prev->tex_width = 0; + prev->tex_height = 0; + memset (&prev->extents, 0, sizeof(prev->extents)); if (prev2) prev2->next = 0; if (prev != data->cache) @@ -451,89 +511,160 @@ get_cache (texture_font_data *data, const char *string) } +/* Renders the given string into the prevailing texture. + Returns the metrics of the text, and size of the texture. + */ +void +string_to_texture (texture_font_data *data, const char *string, + XCharStruct *extents_ret, + int *tex_width_ret, int *tex_height_ret) +{ + Window window = RootWindow (data->dpy, 0); + Pixmap p; + XGCValues gcv; + GC gc; + XWindowAttributes xgwa; + XRenderColor rcolor; + XftColor xftcolor; + XftDraw *xftdraw; + int width, height; + XCharStruct overall; + + /* Measure the string and create a Pixmap of the proper size. + */ + XGetWindowAttributes (data->dpy, window, &xgwa); + iterate_texture_string (data, string, 0, 0, 0, 0, &overall); + width = overall.rbearing - overall.lbearing; + height = overall.ascent + overall.descent; + if (width <= 0) width = 1; + if (height <= 0) height = 1; + p = XCreatePixmap (data->dpy, window, width, height, xgwa.depth); + + gcv.foreground = BlackPixelOfScreen (xgwa.screen); + gc = XCreateGC (data->dpy, p, GCForeground, &gcv); + XFillRectangle (data->dpy, p, gc, 0, 0, width, height); + XFreeGC (data->dpy, gc); + + /* Render the string into the pixmap. + */ + rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF; + XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap, + &rcolor, &xftcolor); + xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap); + iterate_texture_string (data, string, + -overall.lbearing, overall.ascent, + xftdraw, &xftcolor, 0); + XftDrawDestroy (xftdraw); + XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor); + + /* Copy the bits from the Pixmap into a texture, unless it's cached. + */ + bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, + &width, &height); + XFreePixmap (data->dpy, p); + + if (extents_ret) *extents_ret = overall; + if (tex_width_ret) *tex_width_ret = width; + if (tex_height_ret) *tex_height_ret = height; +} + + +/* Set the various OpenGL parameters for properly rendering things + with a texture generated by string_to_texture(). + */ +void +enable_texture_string_parameters (void) +{ + glEnable (GL_TEXTURE_2D); + + /* Texture-rendering parameters to make font pixmaps tolerable to look at. + */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + + /* 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 + 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); + + /* Don't write the transparent parts of the quad into the depth buffer. */ + glAlphaFunc (GL_GREATER, 0.01); + glEnable (GL_ALPHA_TEST); + glEnable (GL_BLEND); + glDisable (GL_LIGHTING); + glDisable (GL_TEXTURE_GEN_S); + glDisable (GL_TEXTURE_GEN_T); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + + /* 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(). + + The origin is at the origin of the first character, so subsequent + lines of a multi-line string are below that. */ void print_texture_string (texture_font_data *data, const char *string) { - int line_height = data->xftfont->ascent + data->xftfont->descent; - int margin = line_height * 0.35; - int width, height; - int width2, height2; - XWindowAttributes xgwa; - Pixmap p = 0; + XCharStruct overall; + int tex_width, tex_height; texfont_cache *cache; + GLint old_texture; if (!*string) return; + clear_gl_error (); + + /* Save the prevailing texture ID, and bind ours. Restored at the end. */ + glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture); + cache = get_cache (data, string); + glBindTexture (GL_TEXTURE_2D, cache->texid); + check_gl_error ("texture font binding"); + /* Measure the string and make a pixmap that will fit it, unless it's cached. */ if (cache->string) { - width = data->cache->width; - height = data->cache->height; + overall = data->cache->extents; + tex_width = data->cache->tex_width; + tex_height = data->cache->tex_height; } else - { - Window window = RootWindow (data->dpy, 0); - XGCValues gcv; - GC gc; - - XGetWindowAttributes (data->dpy, window, &xgwa); - width = iterate_texture_string (data, string, 0, 0, 0, 0, &height); - p = XCreatePixmap (data->dpy, window, - width + margin*2, - height + margin*2, - xgwa.depth); - gcv.foreground = BlackPixelOfScreen (xgwa.screen); - gc = XCreateGC (data->dpy, p, GCForeground, &gcv); - XFillRectangle (data->dpy, p, gc, 0, 0, - width + margin*2, - height + margin*2); - XFreeGC (data->dpy, gc); - } - - /* Draw the string into the pixmap, unless it's cached. - */ - if (!cache->string) - { - XRenderColor rcolor; - XftColor xftcolor; - XftDraw *xftdraw; - rcolor.red = rcolor.green = rcolor.blue = rcolor.alpha = 0xFFFF; - XftColorAllocValue (data->dpy, xgwa.visual, xgwa.colormap, - &rcolor, &xftcolor); - xftdraw = XftDrawCreate (data->dpy, p, xgwa.visual, xgwa.colormap); - iterate_texture_string (data, string, xftdraw, &xftcolor, - margin, 0, 0); - XftDrawDestroy (xftdraw); - XftColorFree (data->dpy, xgwa.visual, xgwa.colormap, &xftcolor); - } + string_to_texture (data, string, &overall, &tex_width, &tex_height); { - GLint old_texture; int ofront, oblend; - Bool alpha_p, blend_p; + Bool alpha_p, blend_p, light_p, gen_s_p, gen_t_p; GLfloat omatrix[16]; GLfloat qx0, qy0, qx1, qy1; GLfloat tx0, ty0, tx1, ty1; /* Save the prevailing texture environment, and set up ours. */ - glGetIntegerv (GL_TEXTURE_BINDING_2D, &old_texture); glGetIntegerv (GL_FRONT_FACE, &ofront); glGetIntegerv (GL_BLEND_DST, &oblend); glGetFloatv (GL_TEXTURE_MATRIX, omatrix); blend_p = glIsEnabled (GL_BLEND); alpha_p = glIsEnabled (GL_ALPHA_TEST); - - clear_gl_error (); + light_p = glIsEnabled (GL_LIGHTING); + gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S); + gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T); glPushMatrix(); @@ -544,68 +675,22 @@ print_texture_string (texture_font_data *data, const char *string) glLoadIdentity (); glMatrixMode (GL_MODELVIEW); - glBindTexture (GL_TEXTURE_2D, cache->texid); - check_gl_error ("texture font binding"); - - glEnable(GL_TEXTURE_2D); - - /* Copy the bits from the Pixmap into a texture, unless it's cached. - */ - if (cache->string) - { - width2 = data->cache->width2; - height2 = data->cache->height2; - if (p) abort(); - } - else - { - width2 = width + margin*2; - height2 = height + margin*2; - bitmap_to_texture (data->dpy, p, xgwa.visual, xgwa.depth, - &width2, &height2); - XFreePixmap (data->dpy, p); - } - - - /* Texture-rendering parameters to make font pixmaps tolerable to look at. - */ - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR_MIPMAP_LINEAR); - - /* 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 - 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); - - /* Don't write the transparent parts of the quad into the depth buffer. */ - glAlphaFunc (GL_GREATER, 0.01); - glEnable (GL_ALPHA_TEST); - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + enable_texture_string_parameters(); /* Draw a quad with that texture on it, possibly using a cached texture. + Position the XCharStruct origin at 0,0 in the scene. */ - qx0 = -margin; - qy0 = line_height + margin; - qx1 = width + margin; - qy1 = line_height - height - margin; + qx0 = overall.lbearing; + qy0 = -overall.descent; + qx1 = overall.rbearing; + qy1 = overall.ascent; tx0 = 0; - ty0 = 0; - tx1 = (width + margin*2) / (GLfloat) width2; - ty1 = (height + margin*2) / (GLfloat) height2; + ty1 = 0; + tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width; + ty0 = (overall.ascent + overall.descent) / (GLfloat) tex_height; + glFrontFace (GL_CCW); glBegin (GL_QUADS); glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0); glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0); @@ -621,6 +706,10 @@ print_texture_string (texture_font_data *data, const char *string) glFrontFace (ofront); if (!alpha_p) glDisable (GL_ALPHA_TEST); if (!blend_p) glDisable (GL_BLEND); + if (light_p) glEnable (GL_LIGHTING); + if (gen_s_p) glEnable (GL_TEXTURE_GEN_S); + if (gen_t_p) glEnable (GL_TEXTURE_GEN_T); + glBlendFunc (GL_SRC_ALPHA, oblend); glMatrixMode (GL_TEXTURE); @@ -633,11 +722,10 @@ print_texture_string (texture_font_data *data, const char *string) */ if (!cache->string) { - cache->string = strdup (string); - cache->width = width; - cache->height = height; - cache->width2 = width2; - cache->height2 = height2; + cache->string = strdup (string); + cache->extents = overall; + cache->tex_width = tex_width; + cache->tex_height = tex_height; } } } @@ -662,7 +750,6 @@ print_texture_label (Display *dpy, Bool tex_p = glIsEnabled (GL_TEXTURE_2D); Bool texs_p = glIsEnabled (GL_TEXTURE_GEN_S); Bool text_p = glIsEnabled (GL_TEXTURE_GEN_T); - Bool light_p = glIsEnabled (GL_LIGHTING); Bool depth_p = glIsEnabled (GL_DEPTH_TEST); Bool cull_p = glIsEnabled (GL_CULL_FACE); Bool fog_p = glIsEnabled (GL_FOG); @@ -683,7 +770,6 @@ print_texture_label (Display *dpy, glDisable (GL_TEXTURE_GEN_S); glDisable (GL_TEXTURE_GEN_T); - glDisable (GL_LIGHTING); glDisable (GL_CULL_FACE); glDisable (GL_FOG); @@ -700,7 +786,9 @@ print_texture_label (Display *dpy, glMatrixMode(GL_MODELVIEW); glPushMatrix(); { - int x, y, w, h, lh, swap; + XCharStruct cs; + int ascent, descent; + int x, y, w, h, swap; int rot = (int) current_device_rotation(); glLoadIdentity(); @@ -710,14 +798,14 @@ print_texture_label (Display *dpy, while (rot <= -180) rot += 360; while (rot > 180) rot -= 360; - lh = texture_string_width (data, "M", 0); - w = texture_string_width (data, string, &h); + texture_string_metrics (data, string, &cs, &ascent, &descent); + h = cs.ascent + cs.descent; + w = cs.width; # ifdef USE_IPHONE - /* Size of the font is in points, so scale iOS pixels to points. */ { - GLfloat scale = 1; - scale = window_width / 768.0; + /* Size of the font is in points, so scale iOS pixels to points. */ + GLfloat scale = window_width / 768.0; if (scale < 1) scale = 1; /* jwxyz-XLoadFont has already doubled the font size, to compensate @@ -756,15 +844,15 @@ print_texture_label (Display *dpy, switch (position) { case 0: /* center */ x = (window_width - w) / 2; - y = (window_height - h) / 2; + y = (window_height + h) / 2 - ascent; break; case 1: /* top */ - x = lh; - y = window_height - lh * 2; + x = ascent; + y = window_height - ascent*2; break; case 2: /* bottom */ - x = lh; - y = h - lh; + x = ascent; + y = h; break; default: abort(); @@ -801,7 +889,6 @@ print_texture_label (Display *dpy, if (tex_p) glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D); if (texs_p) glEnable (GL_TEXTURE_GEN_S);/*else glDisable(GL_TEXTURE_GEN_S);*/ if (text_p) glEnable (GL_TEXTURE_GEN_T);/*else glDisable(GL_TEXTURE_GEN_T);*/ - if (light_p) glEnable (GL_LIGHTING); /*else glDisable (GL_LIGHTING);*/ if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST); if (cull_p) glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/ if (fog_p) glEnable (GL_FOG); /*else glDisable (GL_FOG);*/