1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1995 Thorsten.Ohl @ Physik.TH-Darmstadt.de
5 * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
6 * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
28/**
29 * Fake implementation of glXUseXFont().
30 */
31
32
33#include <stdio.h>
34#include "main/core.h"
35#include <GL/glx.h>
36
37
38/* Some debugging info.  */
39
40#ifdef DEBUG
41#include <ctype.h>
42
43int debug_xfonts = 0;
44
45static void
46dump_char_struct(XCharStruct * ch, char *prefix)
47{
48   printf("%slbearing = %d, rbearing = %d, width = %d\n",
49	  prefix, ch->lbearing, ch->rbearing, ch->width);
50   printf("%sascent = %d, descent = %d, attributes = %u\n",
51	  prefix, ch->ascent, ch->descent, (unsigned int) ch->attributes);
52}
53
54static void
55dump_font_struct(XFontStruct * font)
56{
57   printf("ascent = %d, descent = %d\n", font->ascent, font->descent);
58   printf("char_or_byte2 = (%u,%u)\n",
59	  font->min_char_or_byte2, font->max_char_or_byte2);
60   printf("byte1 = (%u,%u)\n", font->min_byte1, font->max_byte1);
61   printf("all_chars_exist = %s\n", font->all_chars_exist ? "True" : "False");
62   printf("default_char = %c (\\%03o)\n",
63	  (char) (isprint(font->default_char) ? font->default_char : ' '),
64	  font->default_char);
65   dump_char_struct(&font->min_bounds, "min> ");
66   dump_char_struct(&font->max_bounds, "max> ");
67#if 0
68   for (c = font->min_char_or_byte2; c <= font->max_char_or_byte2; c++) {
69      char prefix[8];
70      sprintf(prefix, "%d> ", c);
71      dump_char_struct(&font->per_char[c], prefix);
72   }
73#endif
74}
75
76static void
77dump_bitmap(unsigned int width, unsigned int height, GLubyte * bitmap)
78{
79   unsigned int x, y;
80
81   printf("    ");
82   for (x = 0; x < 8 * width; x++)
83      printf("%o", 7 - (x % 8));
84   putchar('\n');
85   for (y = 0; y < height; y++) {
86      printf("%3o:", y);
87      for (x = 0; x < 8 * width; x++)
88	 putchar((bitmap[width * (height - y - 1) + x / 8] & (1 << (7 - (x %
89									 8))))
90		 ? '*' : '.');
91      printf("   ");
92      for (x = 0; x < width; x++)
93	 printf("0x%02x, ", bitmap[width * (height - y - 1) + x]);
94      putchar('\n');
95   }
96}
97#endif /* DEBUG */
98
99
100/* Implementation.  */
101
102/* Fill a BITMAP with a character C from thew current font
103   in the graphics context GC.  WIDTH is the width in bytes
104   and HEIGHT is the height in bits.
105
106   Note that the generated bitmaps must be used with
107
108        glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
109        glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
110        glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
111        glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
112        glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
113        glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
114
115   Possible optimizations:
116
117     * use only one reusable pixmap with the maximum dimensions.
118     * draw the entire font into a single pixmap (careful with
119       proportional fonts!).
120*/
121
122
123/*
124 * Generate OpenGL-compatible bitmap.
125 */
126static void
127fill_bitmap(Display * dpy, Window win, GC gc,
128	    unsigned int width, unsigned int height,
129	    int x0, int y0, unsigned int c, GLubyte * bitmap)
130{
131   XImage *image;
132   unsigned int x, y;
133   Pixmap pixmap;
134   XChar2b char2b;
135
136   pixmap = XCreatePixmap(dpy, win, 8 * width, height, 1);
137   XSetForeground(dpy, gc, 0);
138   XFillRectangle(dpy, pixmap, gc, 0, 0, 8 * width, height);
139   XSetForeground(dpy, gc, 1);
140
141   char2b.byte1 = (c >> 8) & 0xff;
142   char2b.byte2 = (c & 0xff);
143
144   XDrawString16(dpy, pixmap, gc, x0, y0, &char2b, 1);
145
146   image = XGetImage(dpy, pixmap, 0, 0, 8 * width, height, 1, XYPixmap);
147   if (image) {
148      /* Fill the bitmap (X11 and OpenGL are upside down wrt each other).  */
149      for (y = 0; y < height; y++)
150	 for (x = 0; x < 8 * width; x++)
151	    if (XGetPixel(image, x, y))
152	       bitmap[width * (height - y - 1) + x / 8] |=
153		  (1 << (7 - (x % 8)));
154      XDestroyImage(image);
155   }
156
157   XFreePixmap(dpy, pixmap);
158}
159
160/*
161 * determine if a given glyph is valid and return the
162 * corresponding XCharStruct.
163 */
164static XCharStruct *
165isvalid(XFontStruct * fs, unsigned int which)
166{
167   unsigned int rows, pages;
168   unsigned int byte1 = 0, byte2 = 0;
169   int i, valid = 1;
170
171   rows = fs->max_byte1 - fs->min_byte1 + 1;
172   pages = fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1;
173
174   if (rows == 1) {
175      /* "linear" fonts */
176      if ((fs->min_char_or_byte2 > which) || (fs->max_char_or_byte2 < which))
177	 valid = 0;
178   }
179   else {
180      /* "matrix" fonts */
181      byte2 = which & 0xff;
182      byte1 = which >> 8;
183      if ((fs->min_char_or_byte2 > byte2) ||
184	  (fs->max_char_or_byte2 < byte2) ||
185	  (fs->min_byte1 > byte1) || (fs->max_byte1 < byte1))
186	 valid = 0;
187   }
188
189   if (valid) {
190      if (fs->per_char) {
191	 if (rows == 1) {
192	    /* "linear" fonts */
193	    return (fs->per_char + (which - fs->min_char_or_byte2));
194	 }
195	 else {
196	    /* "matrix" fonts */
197	    i = ((byte1 - fs->min_byte1) * pages) +
198	       (byte2 - fs->min_char_or_byte2);
199	    return (fs->per_char + i);
200	 }
201      }
202      else {
203	 return (&fs->min_bounds);
204      }
205   }
206   return (NULL);
207}
208
209
210PUBLIC void
211glXUseXFont(Font font, int first, int count, int listbase)
212{
213   Display *dpy;
214   Window win;
215   Pixmap pixmap;
216   GC gc;
217   XGCValues values;
218   unsigned long valuemask;
219   XFontStruct *fs;
220   GLint swapbytes, lsbfirst, rowlength;
221   GLint skiprows, skippixels, alignment;
222   unsigned int max_width, max_height, max_bm_width, max_bm_height;
223   GLubyte *bm;
224   int i;
225
226   dpy = glXGetCurrentDisplay();
227   if (!dpy)
228      return;			/* I guess glXMakeCurrent wasn't called */
229   i = DefaultScreen(dpy);
230   win = RootWindow(dpy, i);
231
232   fs = XQueryFont(dpy, font);
233   if (!fs) {
234      _mesa_error(NULL, GL_INVALID_VALUE,
235		  "Couldn't get font structure information");
236      return;
237   }
238
239   /* Allocate a bitmap that can fit all characters.  */
240   max_width = fs->max_bounds.rbearing - fs->min_bounds.lbearing;
241   max_height = fs->max_bounds.ascent + fs->max_bounds.descent;
242   max_bm_width = (max_width + 7) / 8;
243   max_bm_height = max_height;
244
245   bm = malloc((max_bm_width * max_bm_height) * sizeof(GLubyte));
246   if (!bm) {
247      XFreeFontInfo(NULL, fs, 1);
248      _mesa_error(NULL, GL_OUT_OF_MEMORY,
249		  "Couldn't allocate bitmap in glXUseXFont()");
250      return;
251   }
252
253#if 0
254   /* get the page info */
255   pages = fs->max_char_or_byte2 - fs->min_char_or_byte2 + 1;
256   firstchar = (fs->min_byte1 << 8) + fs->min_char_or_byte2;
257   lastchar = (fs->max_byte1 << 8) + fs->max_char_or_byte2;
258   rows = fs->max_byte1 - fs->min_byte1 + 1;
259   unsigned int first_char, last_char, pages, rows;
260#endif
261
262   /* Save the current packing mode for bitmaps.  */
263   glGetIntegerv(GL_UNPACK_SWAP_BYTES, &swapbytes);
264   glGetIntegerv(GL_UNPACK_LSB_FIRST, &lsbfirst);
265   glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rowlength);
266   glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skiprows);
267   glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skippixels);
268   glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
269
270   /* Enforce a standard packing mode which is compatible with
271      fill_bitmap() from above.  This is actually the default mode,
272      except for the (non)alignment.  */
273   glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
274   glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
275   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
276   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
277   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
278   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
279
280   pixmap = XCreatePixmap(dpy, win, 10, 10, 1);
281   values.foreground = BlackPixel(dpy, DefaultScreen(dpy));
282   values.background = WhitePixel(dpy, DefaultScreen(dpy));
283   values.font = fs->fid;
284   valuemask = GCForeground | GCBackground | GCFont;
285   gc = XCreateGC(dpy, pixmap, valuemask, &values);
286   XFreePixmap(dpy, pixmap);
287
288#ifdef DEBUG
289   if (debug_xfonts)
290      dump_font_struct(fs);
291#endif
292
293   for (i = 0; i < count; i++) {
294      unsigned int width, height, bm_width, bm_height;
295      GLfloat x0, y0, dx, dy;
296      XCharStruct *ch;
297      int x, y;
298      unsigned int c = first + i;
299      int list = listbase + i;
300      int valid;
301
302      /* check on index validity and get the bounds */
303      ch = isvalid(fs, c);
304      if (!ch) {
305	 ch = &fs->max_bounds;
306	 valid = 0;
307      }
308      else {
309	 valid = 1;
310      }
311
312#ifdef DEBUG
313      if (debug_xfonts) {
314	 char s[7];
315	 sprintf(s, isprint(c) ? "%c> " : "\\%03o> ", c);
316	 dump_char_struct(ch, s);
317      }
318#endif
319
320      /* glBitmap()' parameters:
321         straight from the glXUseXFont(3) manpage.  */
322      width = ch->rbearing - ch->lbearing;
323      height = ch->ascent + ch->descent;
324      x0 = -ch->lbearing;
325      y0 = ch->descent - 0;	/* XXX used to subtract 1 here */
326      /* but that caused a conformace failure */
327      dx = ch->width;
328      dy = 0;
329
330      /* X11's starting point.  */
331      x = -ch->lbearing;
332      y = ch->ascent;
333
334      /* Round the width to a multiple of eight.  We will use this also
335         for the pixmap for capturing the X11 font.  This is slightly
336         inefficient, but it makes the OpenGL part real easy.  */
337      bm_width = (width + 7) / 8;
338      bm_height = height;
339
340      glNewList(list, GL_COMPILE);
341      if (valid && (bm_width > 0) && (bm_height > 0)) {
342
343	 memset(bm, '\0', bm_width * bm_height);
344	 fill_bitmap(dpy, win, gc, bm_width, bm_height, x, y, c, bm);
345
346	 glBitmap(width, height, x0, y0, dx, dy, bm);
347#ifdef DEBUG
348	 if (debug_xfonts) {
349	    printf("width/height = %u/%u\n", width, height);
350	    printf("bm_width/bm_height = %u/%u\n", bm_width, bm_height);
351	    dump_bitmap(bm_width, bm_height, bm);
352	 }
353#endif
354      }
355      else {
356	 glBitmap(0, 0, 0.0, 0.0, dx, dy, NULL);
357      }
358      glEndList();
359   }
360
361   free(bm);
362   XFreeFontInfo(NULL, fs, 1);
363   XFreeGC(dpy, gc);
364
365   /* Restore saved packing modes.  */
366   glPixelStorei(GL_UNPACK_SWAP_BYTES, swapbytes);
367   glPixelStorei(GL_UNPACK_LSB_FIRST, lsbfirst);
368   glPixelStorei(GL_UNPACK_ROW_LENGTH, rowlength);
369   glPixelStorei(GL_UNPACK_SKIP_ROWS, skiprows);
370   glPixelStorei(GL_UNPACK_SKIP_PIXELS, skippixels);
371   glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
372}
373