summaryrefslogtreecommitdiffstats
path: root/hacks/glx/texfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/texfont.c')
-rw-r--r--hacks/glx/texfont.c1429
1 files changed, 0 insertions, 1429 deletions
diff --git a/hacks/glx/texfont.c b/hacks/glx/texfont.c
deleted file mode 100644
index d5951dc..0000000
--- a/hacks/glx/texfont.c
+++ /dev/null
@@ -1,1429 +0,0 @@
-/* texfont, Copyright © 2005-2021 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
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * 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 or GLSL.
- */
-
-#include "screenhackI.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-
-#ifdef HAVE_XSHM_EXTENSION
-# include "xshm.h"
-#endif /* HAVE_XSHM_EXTENSION */
-
-#include "fps.h" /* for current_device_rotation() */
-#include "pow2.h"
-#include "resources.h"
-#include "texfont.h"
-#include "utf8wc.h"
-
-#ifdef HAVE_GLSL
-# include "glsl-utils.h"
-#endif /* HAVE_GLSL */
-
-#undef HAVE_XSHM_EXTENSION /* doesn't actually do any good here */
-
-extern void clear_gl_error (void); /* xlock-gl.c */
-extern void check_gl_error (const char *type);
-extern float jwxyz_font_scale (Window); /* jwxyz-cocoa.m */
-
-
-#ifdef HAVE_GLSL
-/* Shader programs for rendering text textures when the caller is using
- GLSL and the GLES 3.x API rather than the OpenGL 3.1 or GLES 1.x APIs.
- */
-static const GLchar *shader_version_2_1 = "#version 120\n";
-static const GLchar *shader_version_3_0 = "#version 130\n";
-static const GLchar *shader_version_3_0_es = "#version 300 es \n\
- precision highp float; \n\
- precision highp int; \n\
-";
-static const GLchar *vertex_shader_attribs_2_1 = "\
- attribute vec2 VertexCoord; \n\
- attribute vec2 VertexTex; \n\
- varying vec2 TexCoord; \n\
- varying vec4 Color; \n\
-";
-static const GLchar *vertex_shader_attribs_3_0 = "\
- in vec2 VertexCoord; \n\
- in vec2 VertexTex; \n\
- out vec2 TexCoord; \n\
- out vec4 Color; \n\
-";
-static const GLchar *vertex_shader_main = "\
- uniform mat4 ProjMat; \n\
- uniform vec4 FontColor; \n\
- void main (void) \n\
- { \n\
- gl_Position = ProjMat*vec4 (VertexCoord, 0, 1); \n\
- TexCoord = VertexTex; \n\
- Color = FontColor; \n\
- } \n\
-";
-static const GLchar *fragment_shader_attribs_2_1 = "\
- varying vec4 Color; \n\
- varying vec2 TexCoord; \n\
-";
-static const GLchar *fragment_shader_attribs_3_0 = "\
- in vec4 Color; \n\
- in vec2 TexCoord; \n\
- out vec4 FragColor; \n\
-";
-static const GLchar *fragment_shader_main = "\
- uniform sampler2D TexSampler; \n\
- void main (void) \n\
- { \n\
- const float MinAlpha = 0.01; \n\
- const float LODBias = 0.25; \n\
- if (Color.a <= MinAlpha) \n\
- discard; \n\
-";
-static const GLchar *fragment_shader_out_2_1 = "\
- gl_FragColor = Color*texture2D (TexSampler, TexCoord.st, LODBias); \n\
- }\n";
-static const GLchar *fragment_shader_out_3_0 = "\
- FragColor = Color*texture (TexSampler, TexCoord.st, LODBias); \n\
- }\n";
-#endif /* HAVE_GLSL */
-
-
-/* LRU cache of textures, to optimize the case where we're drawing the
- same strings repeatedly.
- */
-typedef struct texfont_cache texfont_cache;
-struct texfont_cache {
- char *string;
- GLuint texid;
- XCharStruct extents;
- int tex_width, tex_height;
- texfont_cache *next;
-};
-
-struct texture_font_data {
- Display *dpy;
- XftFont *xftfont;
- int cache_size;
- texfont_cache *cache;
-# ifdef HAVE_GLSL
- Bool shaders_initialized, use_shaders;
- GLuint shader_program;
- GLint vertex_coord_index, vertex_tex_index;
- GLint proj_mat_index, font_color_index, tex_sampler_index;
-# endif /* HAVE_GLSL */
-};
-
-
-/* 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 (const texture_font_data *tfdata, Pixmap p,
- Visual *visual, int depth, int *wP, int *hP)
-{
- Display *dpy = tfdata->dpy;
- Bool mipmap_p = True;
- int ow = *wP;
- int oh = *hP;
- GLsizei w2 = (GLsizei) to_pow2 (ow);
- GLsizei h2 = (GLsizei) to_pow2 (oh);
- int x, y, max, scale;
- XImage *image = 0;
- 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 HAVE_XSHM_EXTENSION
- Bool use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
- XShmSegmentInfo shm_info;
-# endif /* HAVE_XSHM_EXTENSION */
-
- /* If either dimension is larger than the supported size, reduce.
- We still return the old size to keep the caller's math working,
- but the texture itself will have fewer pixels in it.
- */
- glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max);
- scale = 1;
- while (w2 > max || h2 > max)
- {
- w2 /= 2;
- h2 /= 2;
- scale *= 2;
- }
-
-# ifdef HAVE_XSHM_EXTENSION
- if (use_shm)
- {
- image = create_xshm_image (dpy, visual, depth, ZPixmap, 0, &shm_info,
- ow, oh);
- if (image)
- XShmGetImage (dpy, p, image, 0, 0, ~0L);
- else
- use_shm = False;
- }
-# endif /* HAVE_XSHM_EXTENSION */
-
- if (!image) {
- /* XCreateImage fills in (red|green_blue)_mask. XGetImage only does that
- when reading from a Window, not when it's a Pixmap.
- */
- image = XCreateImage (dpy, visual, depth, ZPixmap, 0, NULL, ow, oh,
- BitmapPad (dpy), 0);
- image->data = malloc (image->height * image->bytes_per_line);
- XGetSubImage (dpy, p, 0, 0, ow, oh, ~0L, ZPixmap, image, 0, 0);
- }
-
-# ifdef HAVE_JWZGLES
- /* This would work, but it's wasteful for no benefit. */
- mipmap_p = False;
-# endif
-
-# 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
- enough. */
- int sx = x * scale;
- int sy = y * scale;
- unsigned long pixel = (sx >= ow || sy >= oh ? 0 :
- XGetPixel (image, sx, sy));
- /* instead of averaging all three channels, let's just use red,
- and assume it was already grayscale. */
- unsigned long r = pixel & image->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_JWXYZ
- 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)
- destroy_xshm_image (dpy, image, &shm_info);
- else
-# endif /* HAVE_XSHM_EXTENSION */
- {
- /* We malloc'd image->data, so we free it. */
- free (image->data);
- image->data = NULL;
- XDestroyImage (image);
- }
-
- image = 0;
-
- {
-# ifdef GL_INTENSITY
- GLuint iformat = GL_INTENSITY;
- GLuint format = GL_LUMINANCE;
-# else
- GLuint iformat = GL_LUMINANCE_ALPHA;
- GLuint format = GL_LUMINANCE_ALPHA;
-# endif
- GLuint type = GL_UNSIGNED_BYTE;
-
-#ifdef HAVE_GLSL
- if (tfdata->use_shaders)
- {
- glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format,
- type, data);
- glGenerateMipmap (GL_TEXTURE_2D);
- }
- else
-#endif /* HAVE_GLSL */
- {
- if (mipmap_p)
- gluBuild2DMipmaps (GL_TEXTURE_2D, iformat, w2, h2, format,
- type, data);
- else
- glTexImage2D (GL_TEXTURE_2D, 0, iformat, w2, h2, 0, format,
- type, data);
- }
- }
-
- {
- char msg[100];
- sprintf (msg, "texture font %s (%d x %d)",
- mipmap_p ? "gluBuild2DMipmaps" : "glTexImage2D",
- w2, h2);
- check_gl_error (msg);
- }
-
- free (data);
-
- *wP = w2 * scale;
- *hP = h2 * scale;
-}
-
-
-/* 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)
-{
- int screen = DefaultScreen (dpy);
- char *font = get_string_resource (dpy, res, "Font");
- const char *def1 = "sans-serif 18";
- const char *def2 = "sans-serif 14";
- const char *def3 = "monospace";
- XftFont *f = 0;
- texture_font_data *data;
- int cache_size = get_integer_resource (dpy, "texFontCacheSize", "Integer");
-
- /* Hacks that draw a lot of different strings on the screen simultaneously,
- like Star Wars, should set this to a larger value for performance. */
- if (cache_size <= 0)
- cache_size = 30;
-
- if (!res || !*res) abort();
-
- if (!strcmp (res, "fpsFont")) { /* Kludge. */
- def1 = "monospace bold 18"; /* also fps.c */
- cache_size = 0; /* No need for a cache on FPS: already throttled. */
- }
-
- if (!font) font = strdup(def1);
-
- f = load_xft_font_retry (dpy, screen, 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 = load_xft_font_retry (dpy, screen, 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 = load_xft_font_retry (dpy, screen, 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 = load_xft_font_retry (dpy, screen, font);
- }
-
- if (!f)
- {
- fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
- progname, font);
- abort();
- }
-
- free (font);
- font = 0;
-
- data = (texture_font_data *) calloc (1, sizeof(*data));
- data->dpy = dpy;
- data->xftfont = f;
- data->cache_size = cache_size;
-
-#ifdef HAVE_GLSL
- /* Setting data->shaders_initialized to False will cause
- initialize_textfont_shaders_glsl to be called by print_texture_label,
- if necessary. If strings are displayed by print_texture_string, the
- caller is responsible for calling initialize_textfont_shaders_glsl
- first. */
- data->shaders_initialized = False;
- data->use_shaders = False;
- data->shader_program = 0;
- data->vertex_coord_index = -1;
- data->vertex_tex_index = -1;
- data->proj_mat_index = -1;
- data->font_color_index = -1;
- data->tex_sampler_index = -1;
-#endif /* HAVE_GLSL */
-
- return data;
-}
-
-
-/* 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 void
-iterate_texture_string (texture_font_data *data,
- const char *s,
- int draw_x, int draw_y,
- XftDraw *xftdraw, XftColor *xftcolor,
- XCharStruct *metrics_ret)
-{
- int line_height = data->xftfont->ascent + data->xftfont->descent;
- int subscript_offset = line_height * 0.3;
- const char *os = s;
- Bool sub_p = False, osub_p = False;
- int cw = 0, tabs = 0;
- XCharStruct overall = { 0, };
- int x = 0, y = 0;
- int ox = x, oy = y;
-
- while (1)
- {
- if (*s == 0 ||
- *s == '\n' ||
- *s == '\t' ||
- (*s == '[' && isdigit(s[1])) ||
- (*s == ']' && sub_p))
- {
- 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 = 0;
- y += line_height;
- sub_p = False;
- }
- else if (*s == '\t')
- {
- if (! cw)
- {
- /* Measure "m" to determine tab width. */
- XGlyphInfo e;
- XftTextExtentsUtf8 (data->dpy, data->xftfont,
- (FcChar8 *) "m", 1, &e);
- cw = e.xOff;
- if (cw <= 0) cw = 1;
- tabs = cw * 7;
- }
- x = ((x + tabs) / tabs) * tabs;
- }
- else if (*s == '[' && isdigit(s[1]))
- sub_p = True;
- else if (*s == ']' && sub_p)
- sub_p = False;
-
- if (xftdraw && s != os)
- XftDrawStringUtf8 (xftdraw, xftcolor, data->xftfont,
- draw_x + ox,
- draw_y +
- oy + (osub_p ? subscript_offset : 0),
- (FcChar8 *) os, (int) (s - os));
- if (!*s) break;
- os = s+1;
- ox = x;
- oy = y;
- osub_p = sub_p;
- }
- s++;
- }
-
- if (metrics_ret)
- *metrics_ret = overall;
-}
-
-
-/* Bounding box of the multi-line string, in pixels,
- and overall ascent/descent of the font.
- */
-void
-texture_string_metrics (texture_font_data *data, const char *s,
- XCharStruct *metrics_ret,
- int *ascent_ret, int *descent_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 *
-texfont_get_cache (texture_font_data *data, const char *string)
-{
- int count = 0;
- texfont_cache *prev = 0, *prev2 = 0, *curr = 0, *next = 0;
-
- if (data->cache)
- for (prev2 = 0, prev = 0, curr = data->cache, next = curr->next;
- curr;
- prev2 = prev, prev = curr, curr = next,
- next = (curr ? curr->next : 0), count++)
- {
- if (!strcmp (string, curr->string))
- {
- if (prev)
- prev->next = next; /* Unlink from list */
- if (curr != data->cache)
- {
- curr->next = data->cache; /* Move to front */
- data->cache = curr;
- }
- return curr;
- }
- }
-
- /* Made it to the end of the list without a hit.
- If the cache is full, empty out the last one on the list,
- and move it to the front. Keep the texid.
- */
- if (count > data->cache_size)
- {
- if (!prev) abort();
- free (prev->string);
- 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)
- prev->next = data->cache;
- data->cache = prev;
- return prev;
- }
-
- /* Not cached, and cache not full. Add a new entry at the front,
- and allocate a new texture for it.
- */
- curr = (struct texfont_cache *) calloc (1, sizeof(*prev));
- glGenTextures (1, &curr->texid);
- curr->string = 0;
- curr->next = data->cache;
- data->cache = curr;
-
- return curr;
-}
-
-
-static Pixmap
-string_to_pixmap (texture_font_data *data, const char *string,
- XCharStruct *extents_ret,
- int *width_ret, int *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);
- if (width_ret) *width_ret = width;
- if (height_ret) *height_ret = height;
- if (extents_ret) *extents_ret = overall;
- return p;
-}
-
-
-/* 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);
- XWindowAttributes xgwa;
- int width, height;
- Pixmap p = string_to_pixmap (data, string, extents_ret, &width, &height);
-
- /* Copy the bits from the Pixmap into a texture, unless it's cached.
- */
- XGetWindowAttributes (data->dpy, window, &xgwa);
- bitmap_to_texture (data, p, xgwa.visual, xgwa.depth, &width, &height);
- XFreePixmap (data->dpy, p);
-
- if (tex_width_ret) *tex_width_ret = width;
- if (tex_height_ret) *tex_height_ret = height;
-}
-
-
-/* True if the string appears to be a "missing" character.
- The string should contain a single UTF8 character.
-
- Since there may be no reliable way to tell whether a font contains a
- character or has substituted a "missing" glyph for it, this function
- examines the bits to see if it is either solid black, or is a simple
- rectangle, which is what most fonts use.
- */
-Bool
-blank_character_p (texture_font_data *data, const char *string)
-{
- Window window = RootWindow (data->dpy, 0);
- int width, height;
- Pixmap p;
- XImage *im;
- int x, y, j;
- int *xings;
- XWindowAttributes xgwa;
- Bool ret = False;
-
-# ifdef HAVE_XFT
- /* Xft lets us actually ask whether the character is in the font!
- I'm not sure that this is a guarantee that the character is not
- a substitution rectangle, however. */
- {
- unsigned long uc = 0;
- utf8_decode ((const unsigned char *) string, strlen (string), &uc);
- ret = !XftCharExists (data->dpy, data->xftfont, (FcChar32) uc);
-
-# if 0
- {
- const unsigned char *s = (unsigned char *) string;
- fprintf (stderr, "## %d %lu", ret, uc);
- for (; *s; s++) fprintf (stderr, " %02x", *s);
- fprintf (stderr, "\t [%s]\n", string);
- }
-# endif
-
- if (ret) return ret; /* If Xft says it is blank, believe it. */
- }
-# endif /* HAVE_XFT */
-
- /* If we don't have real Xft, we have to check the bits.
- If we do have Xft and it says that the character exists,
- verify that by checking the bits anyway.
- */
-
- p = string_to_pixmap (data, string, 0, &width, &height);
- im = XGetImage (data->dpy, p, 0, 0, width, height, ~0L, ZPixmap);
- xings = (int *) calloc (height, sizeof(*xings));
- XFreePixmap (data->dpy, p);
- XGetWindowAttributes (data->dpy, window, &xgwa);
-
- for (y = 0, j = 0; y < im->height; y++)
- {
- unsigned long prev = 0;
- int c = 0;
- for (x = 0; x < im->width; x++)
- {
- unsigned long p = XGetPixel (im, x, y);
- p = (p & 0x0000FF00); /* Take just one channel, any channel */
- p = p ? 1 : 0; /* Only care about B&W */
- if (p != prev) c++;
- prev = p;
- }
- if (j == 0 || xings[j-1] != c)
- xings[j++] = c;
- }
-
- /* xings contains a schematic of how many times the color changed
- on a line, with duplicates removed, e.g.:
-
- * 1 ***** 1 **** 1 **** 1
- * * 3 * * 3 * * 3 * * 3
- * * . ***** 1 * 1 * * .
- ******* 1 * * 3 * . * * .
- * * 3 * * . * * 3 * * .
- * * . ***** 1 **** 1 **** 1
-
- So "131" is the pattern for a hollow rectangle, which is what most
- fonts use for missing characters. It also gets a false positive on
- capital or lower case O, and on 0 without a slash, but I can live
- with that.
- */
-
- if (j <= 1)
- ret = True;
- else if (j == 3 && xings[0] == 1 && xings[1] == 3 && xings[2] == 1)
- ret = True;
- else if (im->width < 2 || im->height < 2)
- ret = True;
-
- XDestroyImage (im);
- free (xings);
- return ret;
-}
-
-
-
-/* Set the various OpenGL parameters for properly rendering things
- with a texture generated by string_to_texture().
- */
-void
-enable_texture_string_parameters (texture_font_data *data)
-{
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- return;
-# endif /* HAVE_GLSL */
-
- glEnable (GL_TEXTURE_2D);
-
- /* 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 */
-
- /* Don't write the transparent parts of the quad into the depth buffer. */
-# ifndef HAVE_ANDROID /* WTF? */
- glAlphaFunc (GL_GREATER, 0.01);
-# endif
- glEnable (GL_ALPHA_TEST);
- glDisable (GL_LIGHTING);
- glDisable (GL_TEXTURE_GEN_S);
- glDisable (GL_TEXTURE_GEN_T);
-}
-
-
-/* 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)
-{
- 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 = texfont_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)
- {
- overall = data->cache->extents;
- tex_width = data->cache->tex_width;
- tex_height = data->cache->tex_height;
- }
- else
- {
- string_to_texture (data, string, &overall, &tex_width, &tex_height);
- }
-
- {
- int ofront, oblend;
- Bool alpha_p = False, blend_p = False, light_p = False;
- Bool gen_s_p = False, gen_t_p = False;
- GLfloat omatrix[16];
- GLfloat qx0, qy0, qx1, qy1;
- GLfloat tx0, ty0, tx1, ty1;
-
- /* If face culling is not enabled, draw front and back. */
- Bool draw_back_face_p = !glIsEnabled (GL_CULL_FACE);
-
- /* Save the prevailing texture environment. */
- glGetIntegerv (GL_FRONT_FACE, &ofront);
-
-# ifdef HAVE_GLSL
- if (!data->use_shaders)
-# endif /* HAVE_GLSL */
- {
- glGetIntegerv (GL_BLEND_DST, &oblend);
- glGetFloatv (GL_TEXTURE_MATRIX, omatrix);
- check_gl_error ("texture font matrix");
- blend_p = glIsEnabled (GL_BLEND);
- alpha_p = glIsEnabled (GL_ALPHA_TEST);
- light_p = glIsEnabled (GL_LIGHTING);
- gen_s_p = glIsEnabled (GL_TEXTURE_GEN_S);
- gen_t_p = glIsEnabled (GL_TEXTURE_GEN_T);
-
- glPushMatrix();
-
- glNormal3f (0, 0, 1);
-
- glMatrixMode (GL_TEXTURE);
- glLoadIdentity ();
- glMatrixMode (GL_MODELVIEW);
- }
-
- glFrontFace (GL_CW);
-
- enable_texture_string_parameters (data);
-
- /* Draw a quad with that texture on it, possibly using a cached texture.
- Position the XCharStruct origin at 0,0 in the scene.
- */
- qx0 = overall.lbearing;
- qy0 = -overall.descent;
- qx1 = overall.rbearing;
- qy1 = overall.ascent;
-
- tx0 = 0;
- ty1 = 0;
- tx1 = (overall.rbearing - overall.lbearing) / (GLfloat) tex_width;
- ty0 = (overall.ascent + overall.descent) / (GLfloat) tex_height;
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- {
- static const GLuint indices[6] = { 0, 1, 2, 2, 3, 0 };
- GLfloat v[4][2], t[4][2];
-
- v[0][0] = qx0; v[0][1] = qy0; t[0][0] = tx0; t[0][1] = ty0;
- v[1][0] = qx1; v[1][1] = qy0; t[1][0] = tx1; t[1][1] = ty0;
- v[2][0] = qx1; v[2][1] = qy1; t[2][0] = tx1; t[2][1] = ty1;
- v[3][0] = qx0; v[3][1] = qy1; t[3][0] = tx0; t[3][1] = ty1;
-
- glEnableVertexAttribArray (data->vertex_coord_index);
- glVertexAttribPointer (data->vertex_coord_index, 2, GL_FLOAT, GL_FALSE,
- 2 * sizeof(GLfloat), v);
-
- glEnableVertexAttribArray (data->vertex_tex_index);
- glVertexAttribPointer (data->vertex_tex_index, 2, GL_FLOAT, GL_FALSE,
- 2 * sizeof(GLfloat), t);
-
- glEnable (GL_CULL_FACE);
- glFrontFace (GL_CCW);
- glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
-
- if (draw_back_face_p)
- {
- glFrontFace (GL_CW);
- glDrawElements (GL_TRIANGLE_STRIP, 2, GL_UNSIGNED_INT, indices);
- }
-
- glDisableVertexAttribArray (data->vertex_coord_index);
- glDisableVertexAttribArray (data->vertex_tex_index);
-
- glDisable(GL_CULL_FACE);
- }
- else
-# endif /* HAVE_GLSL */
- {
- glEnable (GL_CULL_FACE);
- glFrontFace (GL_CCW);
- glBegin (GL_QUADS);
- glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
- glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
- glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
- glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
- glEnd();
-
- if (draw_back_face_p)
- {
- glFrontFace (GL_CW);
- glBegin (GL_QUADS);
- glTexCoord2f (tx0, ty0); glVertex3f (qx0, qy0, 0);
- glTexCoord2f (tx1, ty0); glVertex3f (qx1, qy0, 0);
- glTexCoord2f (tx1, ty1); glVertex3f (qx1, qy1, 0);
- glTexCoord2f (tx0, ty1); glVertex3f (qx0, qy1, 0);
- glEnd();
- }
-
- glDisable (GL_CULL_FACE);
-
- glPopMatrix();
- }
-
- /* Reset to the caller's texture environment.
- */
-# ifdef HAVE_GLSL
- if (!data->use_shaders)
-# endif /* HAVE_GLSL */
- {
- glBlendFunc (GL_SRC_ALPHA, oblend);
- glMatrixMode (GL_TEXTURE);
- glMultMatrixf (omatrix);
- glMatrixMode (GL_MODELVIEW);
- if (!alpha_p) glDisable (GL_ALPHA_TEST);
- if (light_p) glEnable (GL_LIGHTING);
- if (gen_s_p) glEnable (GL_TEXTURE_GEN_S);
- if (gen_t_p) glEnable (GL_TEXTURE_GEN_T);
- }
-
- if (!blend_p) glDisable (GL_BLEND);
-
- glFrontFace (ofront);
-
- glBindTexture (GL_TEXTURE_2D, old_texture);
-
- check_gl_error ("texture font print");
-
- /* Store this string into the cache, unless that's where it came from.
- */
- if (!cache->string)
- {
- cache->string = strdup (string);
- cache->extents = overall;
- cache->tex_width = tex_width;
- cache->tex_height = tex_height;
- }
- }
-}
-
-
-#ifdef HAVE_GLSL
-/* Initialize the texture font shaders that are used if the hack prefers
- to use a GLSL implementation of the text drawing functionality. This
- function must be called before the first string is displayed. It assumes
- that the OpenGL graphics context is set to the actual context in which
- the shaders will be used. Note that this is not necessarily true in
- load_texture_font (for example, if the -pair option is used to run a
- hack). */
-static void
-initialize_textfont_shaders_glsl (texture_font_data *data)
-{
- GLint gl_major, gl_minor, glsl_major, glsl_minor;
- GLboolean gl_gles3;
- const GLchar *vertex_shader_source[3];
- const GLchar *fragment_shader_source[4];
-
- data->use_shaders = False;
-
- if (!glsl_GetGlAndGlslVersions(&gl_major,&gl_minor,&glsl_major,&glsl_minor,
- &gl_gles3))
- {
- data->shaders_initialized = True;
- return;
- }
-
- if (!gl_gles3)
- {
- if (gl_major < 3 ||
- (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30)))
- {
- if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) ||
- (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20)))
- {
- data->shaders_initialized = True;
- return;
- }
- /* We have at least OpenGL 2.1 and at least GLSL 1.20. */
- vertex_shader_source[0] = shader_version_2_1;
- vertex_shader_source[1] = vertex_shader_attribs_2_1;
- vertex_shader_source[2] = vertex_shader_main;
- fragment_shader_source[0] = shader_version_2_1;
- fragment_shader_source[1] = fragment_shader_attribs_2_1;
- fragment_shader_source[2] = fragment_shader_main;
- fragment_shader_source[3] = fragment_shader_out_2_1;
- }
- else
- {
- /* We have at least OpenGL 3.0 and at least GLSL 1.30. */
- vertex_shader_source[0] = shader_version_3_0;
- vertex_shader_source[1] = vertex_shader_attribs_3_0;
- vertex_shader_source[2] = vertex_shader_main;
- fragment_shader_source[0] = shader_version_3_0;
- fragment_shader_source[1] = fragment_shader_attribs_3_0;
- fragment_shader_source[2] = fragment_shader_main;
- fragment_shader_source[3] = fragment_shader_out_3_0;
- }
- }
- else /* gl_gles3 */
- {
- if (gl_major < 3 || glsl_major < 3)
- {
- data->shaders_initialized = True;
- return;
- }
- /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */
- vertex_shader_source[0] = shader_version_3_0_es;
- vertex_shader_source[1] = vertex_shader_attribs_3_0;
- vertex_shader_source[2] = vertex_shader_main;
- fragment_shader_source[0] = shader_version_3_0_es;
- fragment_shader_source[1] = fragment_shader_attribs_3_0;
- fragment_shader_source[2] = fragment_shader_main;
- fragment_shader_source[3] = fragment_shader_out_3_0;
- }
- if (!glsl_CompileAndLinkShaders(3,vertex_shader_source,
- 4,fragment_shader_source,
- &data->shader_program))
- {
- data->shaders_initialized = True;
- return;
- }
- data->vertex_coord_index = glGetAttribLocation(data->shader_program,
- "VertexCoord");
- data->vertex_tex_index = glGetAttribLocation(data->shader_program,
- "VertexTex");
- data->proj_mat_index = glGetUniformLocation(data->shader_program,
- "ProjMat");
- data->font_color_index = glGetUniformLocation(data->shader_program,
- "FontColor");
- data->tex_sampler_index = glGetUniformLocation(data->shader_program,
- "TexSampler");
- if (data->vertex_coord_index != -1 && data->vertex_tex_index != -1 &&
- data->proj_mat_index != -1 && data->font_color_index != -1 &&
- data->tex_sampler_index != -1)
- {
- data->use_shaders = True;
- data->shaders_initialized = True;
- }
- else
- {
- glDeleteProgram(data->shader_program);
- data->shader_program = 0;
- data->shaders_initialized = True;
- }
-}
-#endif /* HAVE_GLSL */
-
-
-
-static void
-texfont_transrot (texture_font_data *data,
- GLfloat mat[16],
- GLfloat tx, GLfloat ty, GLfloat tz,
- GLfloat r, GLfloat rx, GLfloat ry, GLfloat rz)
-{
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- {
- glsl_Translate (mat, tx, ty, tz);
- glsl_Rotate (mat, r, rx, ry, rz);
- }
- else
-# endif /* HAVE_GLSL */
- {
- glTranslatef (tx, ty, tz);
- glRotatef (r, rx, ry, rz);
- }
-}
-
-
-/* Draws the string on the window at the given pixel position.
- 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().
-
- Position is 0 for center, 1 for top left, 2 for bottom left.
- */
-void
-print_texture_label (Display *dpy,
- texture_font_data *data,
- int window_width, int window_height,
- int position,
- const char *string)
-{
- GLfloat color[4] = { 1, 1, 1, 1 };
- Bool tex_p = False, texs_p = False, text_p = False;
- Bool depth_p = False, fog_p = False, cull_p = False;
- GLint ovp[4];
- GLfloat proj_mat[16];
-# ifndef HAVE_JWZGLES
- GLint opoly[2];
-# endif
-
- cull_p = glIsEnabled (GL_CULL_FACE);
- depth_p = glIsEnabled (GL_DEPTH_TEST);
-
- glGetIntegerv (GL_VIEWPORT, ovp);
-
- /* This call will fail with an OpenGL ES 3.0 context. color will maintain
- its initial value of {1,1,1,1} in this case. We clear the potential
- error afterwards. */
- clear_gl_error();
- glGetFloatv (GL_CURRENT_COLOR, color);
- clear_gl_error ();
-#ifdef HAVE_ANDROID
- Log ("texfont current color 0x%04X (%5.3f,%5.3f,%5.3f,%5.3f)",
- GL_CURRENT_COLOR,color[0], color[1], color[2], color[3]);
-#endif
-
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glDisable (GL_DEPTH_TEST);
-
-
-# ifdef HAVE_GLSL
- if (!data->shaders_initialized)
- {
- if (get_boolean_resource (dpy, "prefersGLSL", "PrefersGLSL"))
- initialize_textfont_shaders_glsl (data);
- }
-
- if (data->use_shaders)
- {
- glUseProgram (data->shader_program);
- glActiveTexture (GL_TEXTURE0);
- glUniform1i (data->tex_sampler_index, 0);
- }
- else
-# endif /* HAVE_GLSL */
- {
- tex_p = glIsEnabled (GL_TEXTURE_2D);
- texs_p = glIsEnabled (GL_TEXTURE_GEN_S);
- text_p = glIsEnabled (GL_TEXTURE_GEN_T);
- fog_p = glIsEnabled (GL_FOG);
-
- glDisable (GL_TEXTURE_GEN_S);
- glDisable (GL_TEXTURE_GEN_T);
- glDisable (GL_CULL_FACE);
- glDisable (GL_FOG);
- glEnable (GL_TEXTURE_2D);
-
-#ifndef HAVE_JWZGLES
- glGetIntegerv (GL_POLYGON_MODE, opoly);
-#endif
- glPolygonMode (GL_FRONT, GL_FILL);
-
- /* Each matrix mode has its own stack, so we need to push/pop
- them separately.
- */
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
- }
-
- {
- XCharStruct cs;
- int ascent, descent;
- int x, y, w, h, swap;
- /* int rot = (int) current_device_rotation(); */
- int rot = 0; /* Since GL hacks rotate now */
-
- glViewport (0, 0, window_width, window_height);
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- {
- glsl_Identity (proj_mat);
- glsl_Orthographic (proj_mat, 0, window_width, 0, window_height, -1 ,1);
- }
- else
-# endif /* HAVE_GLSL */
- {
- glOrtho (0, window_width, 0, window_height, -1, 1);
- }
-
- while (rot <= -180) rot += 360;
- while (rot > 180) rot -= 360;
-
- texture_string_metrics (data, string, &cs, &ascent, &descent);
- h = cs.ascent + cs.descent;
- w = cs.width;
-
-# ifdef HAVE_IPHONE
- {
- /* Size of the font is in points, so scale iOS pixels to points. */
- GLfloat scale = ((window_width > window_height
- ? window_width : window_height)
- / 768.0);
- if (scale < 1) scale = 1;
-
- /* jwxyz-XLoadFont has already doubled the font size, to compensate
- for physically smaller screens. Undo that, since OpenGL hacks
- use full-resolution framebuffers, unlike X11 hacks. */
- scale /= jwxyz_font_scale (RootWindow (dpy, 0));
-
- window_width /= scale;
- window_height /= scale;
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- glsl_Scale (proj_mat, scale, scale, scale);
- else
-# endif /* HAVE_GLSL */
- glScalef (scale, scale, scale);
- }
-# endif /* HAVE_IPHONE */
-
- if (rot > 135 || rot < -135) /* 180 */
- {
- texfont_transrot (data, proj_mat,
- window_width, window_height, 0,
- 180, 0, 0, 1);
- }
- else if (rot > 45) /* 90 */
- {
- texfont_transrot (data, proj_mat,
- window_width, 0, 0,
- 90, 0, 0, 1);
- swap = window_width;
- window_width = window_height;
- window_height = swap;
- }
- else if (rot < -45) /* 270 */
- {
- texfont_transrot (data, proj_mat,
- 0, window_height, 0,
- -90, 0, 0, 1);
- swap = window_width;
- window_width = window_height;
- window_height = swap;
- }
-
- switch (position) {
- case 0: /* center */
- x = (window_width - w) / 2;
- y = (window_height + h) / 2 - ascent;
- break;
- case 1: /* top */
- x = ascent;
- y = window_height - ascent*2;
- break;
- case 2: /* bottom */
- x = ascent;
- y = h;
- break;
- default:
- abort();
- }
-
- texfont_transrot (data, proj_mat,
- x, y, 0,
- 0, 0, 0, 1);
-
- /* draw the text five times, to give it a border. */
- {
- const XPoint offsets[] = {{ -1, -1 },
- { -1, 1 },
- { 1, 1 },
- { 1, -1 },
- { 0, 0 }};
- int i;
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- glUniform4f (data->font_color_index, 0, 0, 0, 1);
- else
-# endif /* HAVE_GLSL */
- glColor3f (0, 0, 0);
-
- for (i = 0; i < countof(offsets); i++)
- {
- if (offsets[i].x == 0)
- {
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- glUniform4fv (data->font_color_index, 1, color);
- else
-# endif /* HAVE_GLSL */
- glColor4fv (color);
- }
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- {
- GLfloat proj_mat_trans[16];
- glsl_CopyMatrix (proj_mat_trans, proj_mat);
- glsl_Translate (proj_mat_trans, offsets[i].x,offsets[i].y, 0);
- glUniformMatrix4fv (data->proj_mat_index, 1, GL_FALSE,
- proj_mat_trans);
- print_texture_string (data, string);
- }
- else
-# endif /* HAVE_GLSL */
- {
- glPushMatrix();
- glTranslatef (offsets[i].x, offsets[i].y, 0);
- print_texture_string (data, string);
- glPopMatrix();
- }
- }
- }
- }
-
-# ifdef HAVE_GLSL
- if (data->use_shaders)
- {
- glUseProgram (0);
- }
- else
-# endif /* HAVE_GLSL */
- {
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
-
- if (tex_p) glEnable (GL_TEXTURE_2D); else glDisable (GL_TEXTURE_2D);
- if (texs_p) glEnable (GL_TEXTURE_GEN_S);
- if (text_p) glEnable (GL_TEXTURE_GEN_T);
- if (fog_p) glEnable (GL_FOG); /*else glDisable (GL_FOG);*/
-#ifndef HAVE_JWZGLES
- glPolygonMode (GL_FRONT, opoly[0]);
-#endif
- }
-
- glViewport (ovp[0], ovp[1], ovp[2], ovp[3]);
-
- if (cull_p) glEnable (GL_CULL_FACE); /*else glDisable (GL_CULL_FACE);*/
- if (depth_p) glEnable (GL_DEPTH_TEST); else glDisable (GL_DEPTH_TEST);
-}
-
-
-#ifdef HAVE_JWXYZ
-char *
-texfont_unicode_character_name (texture_font_data *data, unsigned long uc)
-{
- Font fid = data->xftfont->xfont->fid;
- return jwxyz_unicode_character_name (data->dpy, fid, uc);
-}
-#endif /* HAVE_JWXYZ */
-
-
-
-/* Releases the font and texture.
- */
-void
-free_texture_font (texture_font_data *data)
-{
- while (data->cache)
- {
- texfont_cache *next = data->cache->next;
- glDeleteTextures (1, &data->cache->texid);
- free (data->cache->string);
- free (data->cache);
- data->cache = next;
- }
- if (data->xftfont)
- XftFontClose (data->dpy, data->xftfont);
-
-# ifdef HAVE_GLSL
- if (data->use_shaders &&
- data->shaders_initialized &&
- data->shader_program != 0)
- {
- glUseProgram (0);
- glDeleteProgram (data->shader_program);
- }
-# endif /* HAVE_GLSL */
-
- free (data);
-}