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