bb51736787de110104f6f8921cf3c0cd0f0e42d6
[xscreensaver] / hacks / glx / font-ximage.c
1 /* font-ximage.c --- renders text to an XImage for use with OpenGL.
2  * xscreensaver, Copyright (c) 2001-2013 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #ifdef HAVE_COCOA
22 # include "jwxyz.h"
23 #else  /* !HAVE_COCOA */
24 # include <X11/Xlib.h>
25 # include <X11/Xutil.h>
26 # include <GL/gl.h>     /* only for GLfloat */
27 #endif /* !HAVE_COCOA */
28
29 #ifdef HAVE_JWZGLES
30 # include "jwzgles.h"
31 #endif /* HAVE_JWZGLES */
32
33 extern char *progname;
34
35 #include "font-ximage.h"
36
37 #undef MAX
38 #define MAX(a,b) ((a)>(b)?(a):(b))
39
40 #undef countof
41 #define countof(x) (sizeof((x))/sizeof((*x)))
42
43 #if 0
44 static Bool
45 bigendian (void)
46 {
47   union { int i; char c[sizeof(int)]; } u;
48   u.i = 1;
49   return !u.c[0];
50 }
51 #endif
52
53 /* return the next larger power of 2. */
54 static int
55 to_pow2 (int i)
56 {
57   static const unsigned int pow2[] = { 
58     1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
59     2048, 4096, 8192, 16384, 32768, 65536 };
60   int j;
61   for (j = 0; j < countof(pow2); j++)
62     if (pow2[j] >= i) return pow2[j];
63   abort();  /* too big! */
64 }
65
66
67 /* Returns an XImage structure containing the string rendered in the font.
68    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
69    extra byte set to 0xFF.
70
71    Foreground and background are GL-style color specifiers: 4 floats from
72    0.0-1.0.
73  */
74 XImage *
75 text_to_ximage (Screen *screen, Visual *visual,
76                 const char *font,
77                 const char *text_lines,
78                 GLfloat *texture_fg,
79                 GLfloat *texture_bg)
80 {
81   Display *dpy = DisplayOfScreen (screen);
82   int width, height;
83   XFontStruct *f;
84   Pixmap bitmap;
85
86   f = XLoadQueryFont(dpy, font);
87   if (!f)
88     {
89       f = XLoadQueryFont(dpy, "fixed");
90       if (f)
91         fprintf (stderr, "%s: unable to load font \"%s\"; using \"fixed\".\n",
92                  progname, font);
93       else
94         {
95           fprintf (stderr, "%s: unable to load fonts \"%s\" or \"fixed\"!\n",
96                    progname, font);
97           exit (1);
98         }
99     }
100
101   /* Parse the text, and render it to `bitmap'
102    */
103   {
104     char *text, *text2, *line, *token;
105     int lines;
106     XCharStruct overall;
107     XGCValues gcv;
108     GC gc;
109
110     int margin = 2;
111     int fg = 1;
112     int bg = 0;
113     int xoff, yoff;
114
115     text  = strdup (text_lines);
116     while (*text &&
117            (text[strlen(text)-1] == '\r' ||
118             text[strlen(text)-1] == '\n'))
119       text[strlen(text)-1] = 0;
120
121     text2 = strdup (text);
122
123     memset(&overall, 0, sizeof(overall));
124     token = text;
125     lines = 0;
126     while ((line = strtok (token, "\r\n")))
127       {
128         XCharStruct o2;
129         int ascent, descent, direction;
130         token = 0;
131         XTextExtents (f, line, strlen(line),
132                       &direction, &ascent, &descent, &o2);
133         overall.lbearing = MAX(overall.lbearing, o2.lbearing);
134         overall.rbearing = MAX(overall.rbearing, o2.rbearing);
135         lines++;
136       }
137     free (text);
138     text = 0;
139
140     width = overall.lbearing + overall.rbearing + margin + margin + 1;
141     height = ((f->ascent + f->descent) * lines) + margin + margin;
142
143     /* GL texture sizes must be powers of two. */
144     {
145       int w2 = to_pow2(width);
146       int h2 = to_pow2(height);
147       xoff = (w2 - width)  / 2;
148       yoff = (h2 - height) / 2;
149       width  = w2;
150       height = h2;
151     }
152
153     bitmap = XCreatePixmap(dpy, RootWindowOfScreen (screen), width, height, 1);
154
155     gcv.font = f->fid;
156     gcv.foreground = bg;
157     gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
158     XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
159     XSetForeground(dpy, gc, fg);
160
161     token = text2;
162     lines = 0;
163     while ((line = strtok(token, "\r\n")))
164       {
165         XCharStruct o2;
166         int ascent, descent, direction;
167         token = 0;
168
169         XTextExtents(f, line, strlen(line),
170                      &direction, &ascent, &descent, &o2);
171         XDrawString(dpy, bitmap, gc,
172                     overall.lbearing + margin + xoff,
173                     ((f->ascent * (lines + 1)) +
174                      (f->descent * lines) +
175                      margin +
176                      yoff),
177                     line, strlen(line));
178         lines++;
179       }
180     free(text2);
181
182     XUnloadFont(dpy, f->fid);
183     XFree((XPointer) f);
184     XFreeGC(dpy, gc);
185   }
186
187   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
188    */
189   {
190     XImage *ximage1, *ximage2;
191     unsigned long fg, bg;
192     int x, y;
193
194     ximage1 = XGetImage (dpy, bitmap, 0, 0, width, height, ~0L, ZPixmap);
195     XFreePixmap(dpy, bitmap);
196     ximage2 = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
197                             width, height, 32, 0);
198
199     ximage2->data = (char *) malloc (height * ximage2->bytes_per_line);
200
201     /* Translate the 1-bit image to a deep image:
202        first figure out what the colors are.
203      */
204     {
205       int rpos, gpos, bpos, apos;  /* bitfield positions */
206
207       /* Note that unlike X, which is endianness-agnostic (since any XImage
208          can have its own specific bit ordering, with the server reversing
209          things as necessary) OpenGL pretends everything is client-side, so
210          we need to pack things in the right order for the client machine.
211        */
212 #if 0
213     /* #### Cherub says that the little-endian case must be taken on MacOSX,
214             or else the colors/alpha are the wrong way around.  How can
215             that be the case?
216      */
217       if (bigendian())
218         rpos = 24, gpos = 16, bpos =  8, apos =  0;
219       else
220 #endif
221         rpos =  0, gpos =  8, bpos = 16, apos = 24;
222
223       fg = (((unsigned long) (texture_fg[0] * 255.0) << rpos) |
224             ((unsigned long) (texture_fg[1] * 255.0) << gpos) |
225             ((unsigned long) (texture_fg[2] * 255.0) << bpos) |
226             ((unsigned long) (texture_fg[3] * 255.0) << apos));
227       bg = (((unsigned long) (texture_bg[0] * 255.0) << rpos) |
228             ((unsigned long) (texture_bg[1] * 255.0) << gpos) |
229             ((unsigned long) (texture_bg[2] * 255.0) << bpos) |
230             ((unsigned long) (texture_bg[3] * 255.0) << apos));
231     }
232
233     for (y = 0; y < height; y++)
234       {
235         int y2 = (height-1-y); /* Texture maps are upside down. */
236         for (x = 0; x < width; x++)
237           XPutPixel (ximage2, x, y, 
238                      XGetPixel (ximage1, x, y2) ? fg : bg);
239       }
240
241     XDestroyImage (ximage1);
242
243 #if 0
244     for (y = 0; y < height; y++)
245       {
246         int y2 = (height-1-y); /* Texture maps are upside down. */
247         for (x = 0; x < width; x++)
248           fputc ((XGetPixel (ximage2, x, y2) == fg ? '#' : ' '), stdout);
249         fputc ('\n', stdout);
250       }
251     fputc ('\n', stdout);
252 #endif /* 0 */
253
254     return ximage2;
255   }
256 }