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