25ceb422112b234d6f848120b594efec2654b173
[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 #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    Foregroune 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
138     width = overall.lbearing + overall.rbearing + margin + margin + 1;
139     height = ((f->ascent + f->descent) * lines) + margin + margin;
140
141     /* GL texture sizes must be powers of two. */
142     {
143       int w2 = to_pow2(width);
144       int h2 = to_pow2(height);
145       xoff = (w2 - width)  / 2;
146       yoff = (h2 - height) / 2;
147       width  = w2;
148       height = h2;
149     }
150
151     bitmap = XCreatePixmap(dpy, RootWindowOfScreen (screen), width, height, 1);
152
153     gcv.font = f->fid;
154     gcv.foreground = bg;
155     gc = XCreateGC (dpy, bitmap, (GCFont | GCForeground), &gcv);
156     XFillRectangle(dpy, bitmap, gc, 0, 0, width, height);
157     XSetForeground(dpy, gc, fg);
158
159     token = text2;
160     lines = 0;
161     while ((line = strtok(token, "\r\n")))
162       {
163         XCharStruct o2;
164         int ascent, descent, direction;
165         token = 0;
166
167         XTextExtents(f, line, strlen(line),
168                      &direction, &ascent, &descent, &o2);
169         XDrawString(dpy, bitmap, gc,
170                     overall.lbearing + margin + xoff,
171                     ((f->ascent * (lines + 1)) +
172                      (f->descent * lines) +
173                      margin +
174                      yoff),
175                     line, strlen(line));
176         lines++;
177       }
178     free(text2);
179
180     XUnloadFont(dpy, f->fid);
181     XFree((XPointer) f);
182     XFreeGC(dpy, gc);
183   }
184
185   /* Convert the server-side Pixmap to a client-side GL-ordered XImage.
186    */
187   {
188     XImage *ximage1, *ximage2;
189     unsigned long fg, bg;
190     int x, y;
191
192     ximage1 = XGetImage (dpy, bitmap, 0, 0, width, height, ~0L, ZPixmap);
193     XFreePixmap(dpy, bitmap);
194     ximage2 = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
195                             width, height, 32, 0);
196
197     ximage2->data = (char *) malloc (height * ximage2->bytes_per_line);
198
199     /* Translate the 1-bit image to a deep image:
200        first figure out what the colors are.
201      */
202     {
203       int rpos, gpos, bpos, apos;  /* bitfield positions */
204
205       /* Note that unlike X, which is endianness-agnostic (since any XImage
206          can have its own specific bit ordering, with the server reversing
207          things as necessary) OpenGL pretends everything is client-side, so
208          we need to pack things in the right order for the client machine.
209        */
210 #if 0
211     /* #### Cherub says that the little-endian case must be taken on MacOSX,
212             or else the colors/alpha are the wrong way around.  How can
213             that be the case?
214      */
215       if (bigendian())
216         rpos = 24, gpos = 16, bpos =  8, apos =  0;
217       else
218 #endif
219         rpos =  0, gpos =  8, bpos = 16, apos = 24;
220
221       fg = (((unsigned long) (texture_fg[0] * 255.0) << rpos) |
222             ((unsigned long) (texture_fg[1] * 255.0) << gpos) |
223             ((unsigned long) (texture_fg[2] * 255.0) << bpos) |
224             ((unsigned long) (texture_fg[3] * 255.0) << apos));
225       bg = (((unsigned long) (texture_bg[0] * 255.0) << rpos) |
226             ((unsigned long) (texture_bg[1] * 255.0) << gpos) |
227             ((unsigned long) (texture_bg[2] * 255.0) << bpos) |
228             ((unsigned long) (texture_bg[3] * 255.0) << apos));
229     }
230
231     for (y = 0; y < height; y++)
232       {
233         int y2 = (height-1-y); /* Texture maps are upside down. */
234         for (x = 0; x < width; x++)
235           XPutPixel (ximage2, x, y, 
236                      XGetPixel (ximage1, x, y2) ? fg : bg);
237       }
238
239     XDestroyImage (ximage1);
240
241 #if 0
242     for (y = 0; y < height; y++)
243       {
244         int y2 = (height-1-y); /* Texture maps are upside down. */
245         for (x = 0; x < width; x++)
246           fputc ((XGetPixel (ximage2, x, y2) == fg ? '#' : ' '), stdout);
247         fputc ('\n', stdout);
248       }
249     fputc ('\n', stdout);
250 #endif /* 0 */
251
252     return ximage2;
253   }
254 }