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