source: git/lib/gentexfont.c @ 2d66554

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 2d66554 was 1848b52, checked in by Olly Betts <olly@…>, 20 years ago

Utility to generate .txf files.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@2481 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 15.6 KB
Line 
1
2/* Copyright (c) Mark J. Kilgard, 1997. */
3
4/* This program is freely distributable without licensing fees  and is
5   provided without guarantee or warrantee expressed or  implied. This
6   program is -not- in the public domain. */
7
8/* X compile line: cc -o gentexfont gentexfont.c -lX11 */
9
10#include <assert.h>
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14#include <unistd.h>
15#include <X11/Xlib.h>
16#include <X11/Xutil.h>
17#include <math.h>
18
19#include "TexFont.h"
20
21typedef struct {
22  short width;
23  short height;
24  short xoffset;
25  short yoffset;
26  short advance;
27  unsigned char *bitmap;
28} PerGlyphInfo, *PerGlyphInfoPtr;
29
30typedef struct {
31  int min_char;
32  int max_char;
33  int max_ascent;
34  int max_descent;
35  PerGlyphInfo glyph[1];
36} FontInfo, *FontInfoPtr;
37
38Display *dpy;
39FontInfoPtr fontinfo;
40int format = TXF_FORMAT_BITMAP;
41int gap = 1;
42
43/* #define REPORT_GLYPHS */
44#ifdef REPORT_GLYPHS
45#define DEBUG_GLYPH4(msg,a,b,c,d) printf(msg,a,b,c,d)
46#define DEBUG_GLYPH(msg) printf(msg)
47#else
48#define DEBUG_GLYPH4(msg,a,b,c,d) { /* nothing */ }
49#define DEBUG_GLYPH(msg) { /* nothing */ }
50#endif
51
52#define MAX_GLYPHS_PER_GRAB 512  /* this is big enough for 2^9 glyph
53                                    character sets */
54
55FontInfoPtr
56SuckGlyphsFromServer(Display * dpy, Font font)
57{
58  Pixmap offscreen;
59  XFontStruct *fontinfo;
60  XImage *image;
61  GC xgc;
62  XGCValues values;
63  int numchars;
64  int width, height, pixwidth;
65  int i, j;
66  XCharStruct *charinfo;
67  XChar2b character;
68  unsigned char *bitmapData;
69  int x, y;
70  int spanLength;
71  int charWidth, charHeight, maxSpanLength;
72  int grabList[MAX_GLYPHS_PER_GRAB];
73  int glyphsPerGrab = MAX_GLYPHS_PER_GRAB;
74  int numToGrab, thisglyph;
75  FontInfoPtr myfontinfo;
76
77  fontinfo = XQueryFont(dpy, font);
78  if (!fontinfo)
79    return NULL;
80
81  numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1;
82  if (numchars < 1)
83    return NULL;
84
85  myfontinfo = (FontInfoPtr) malloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerGlyphInfo));
86  if (!myfontinfo)
87    return NULL;
88
89  myfontinfo->min_char = fontinfo->min_char_or_byte2;
90  myfontinfo->max_char = fontinfo->max_char_or_byte2;
91  myfontinfo->max_ascent = fontinfo->max_bounds.ascent;
92  myfontinfo->max_descent = fontinfo->max_bounds.descent;
93
94  width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing;
95  height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent;
96
97  maxSpanLength = (width + 7) / 8;
98  /* Be careful determining the width of the pixmap; the X protocol allows
99     pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates
100     max out at 2^15-1 (signed short size).  If the width is too large, we
101     need to limit the glyphs per grab.  */
102  if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) {
103    glyphsPerGrab = (1 << 15) / (8 * maxSpanLength);
104  }
105  pixwidth = glyphsPerGrab * 8 * maxSpanLength;
106  offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)),
107    pixwidth, height, 1);
108
109  values.font = font;
110  values.background = 0;
111  values.foreground = 0;
112  xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values);
113
114  XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
115  XSetForeground(dpy, xgc, 1);
116
117  numToGrab = 0;
118  if (fontinfo->per_char == NULL) {
119    charinfo = &(fontinfo->min_bounds);
120    charWidth = charinfo->rbearing - charinfo->lbearing;
121    charHeight = charinfo->ascent + charinfo->descent;
122    spanLength = (charWidth + 7) / 8;
123  }
124  for (i = 0; i < numchars; i++) {
125    if (fontinfo->per_char != NULL) {
126      charinfo = &(fontinfo->per_char[i]);
127      charWidth = charinfo->rbearing - charinfo->lbearing;
128      charHeight = charinfo->ascent + charinfo->descent;
129      if (charWidth == 0 || charHeight == 0) {
130        /* Still must move raster pos even if empty character */
131        myfontinfo->glyph[i].width = 0;
132        myfontinfo->glyph[i].height = 0;
133        myfontinfo->glyph[i].xoffset = 0;
134        myfontinfo->glyph[i].yoffset = 0;
135        myfontinfo->glyph[i].advance = charinfo->width;
136        myfontinfo->glyph[i].bitmap = NULL;
137        goto PossiblyDoGrab;
138      }
139    }
140    grabList[numToGrab] = i;
141
142    /* XXX is this right for large fonts? */
143    character.byte2 = (i + fontinfo->min_char_or_byte2) & 255;
144    character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8;
145
146    /* XXX we could use XDrawImageString16 which would also paint the backing
147
148       rectangle but X server bugs in some scalable font rasterizers makes it
149
150       more effective to do XFillRectangles to clear the pixmap and
151       XDrawImage16 for the text.  */
152    XDrawString16(dpy, offscreen, xgc,
153      -charinfo->lbearing + 8 * maxSpanLength * numToGrab,
154      charinfo->ascent, &character, 1);
155
156    numToGrab++;
157
158  PossiblyDoGrab:
159
160    if (numToGrab >= glyphsPerGrab || i == numchars - 1) {
161      image = XGetImage(dpy, offscreen,
162        0, 0, pixwidth, height, 1, XYPixmap);
163      for (j = 0; j < numToGrab; j++) {
164        thisglyph = grabList[j];
165        if (fontinfo->per_char != NULL) {
166          charinfo = &(fontinfo->per_char[thisglyph]);
167          charWidth = charinfo->rbearing - charinfo->lbearing;
168          charHeight = charinfo->ascent + charinfo->descent;
169          spanLength = (charWidth + 7) / 8;
170        }
171        bitmapData = calloc(height * spanLength, sizeof(char));
172        if (!bitmapData)
173          goto FreeFontAndReturn;
174        DEBUG_GLYPH4("index %d, glyph %d (%d by %d)\n",
175          j, thisglyph + fontinfo->min_char_or_byte2, charWidth, charHeight);
176        for (y = 0; y < charHeight; y++) {
177          for (x = 0; x < charWidth; x++) {
178            /* XXX The algorithm used to suck across the font ensures that
179               each glyph begins on a byte boundary.  In theory this would
180               make it convienent to copy the glyph into a byte oriented
181               bitmap.  We actually use the XGetPixel function to extract
182               each pixel from the image which is not that efficient.  We
183               could either do tighter packing in the pixmap or more
184               efficient extraction from the image.  Oh well.  */
185            if (XGetPixel(image, j * maxSpanLength * 8 + x, charHeight - 1 - y)) {
186              DEBUG_GLYPH("x");
187              bitmapData[y * spanLength + x / 8] |= (1 << (x & 7));
188            } else {
189              DEBUG_GLYPH(" ");
190            }
191          }
192          DEBUG_GLYPH("\n");
193        }
194        myfontinfo->glyph[thisglyph].width = charWidth;
195        myfontinfo->glyph[thisglyph].height = charHeight;
196        myfontinfo->glyph[thisglyph].xoffset = charinfo->lbearing;
197        myfontinfo->glyph[thisglyph].yoffset = -charinfo->descent;
198        myfontinfo->glyph[thisglyph].advance = charinfo->width;
199        myfontinfo->glyph[thisglyph].bitmap = bitmapData;
200      }
201      XDestroyImage(image);
202      numToGrab = 0;
203      /* do we need to clear the offscreen pixmap to get more? */
204      if (i < numchars - 1) {
205        XSetForeground(dpy, xgc, 0);
206        XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
207        XSetForeground(dpy, xgc, 1);
208      }
209    }
210  }
211  XFreeGC(dpy, xgc);
212  XFreePixmap(dpy, offscreen);
213  return myfontinfo;
214
215FreeFontAndReturn:
216  XDestroyImage(image);
217  XFreeGC(dpy, xgc);
218  XFreePixmap(dpy, offscreen);
219  for (j = i - 1; j >= 0; j--) {
220    if (myfontinfo->glyph[j].bitmap)
221      free(myfontinfo->glyph[j].bitmap);
222  }
223  free(myfontinfo);
224  return NULL;
225}
226
227void
228printGlyph(FontInfoPtr font, int c)
229{
230  PerGlyphInfoPtr glyph;
231  unsigned char *bitmapData;
232  int width, height, spanLength;
233  int x, y;
234
235  if (c < font->min_char || c > font->max_char) {
236    printf("out of range glyph\n");
237    return;
238  }
239  glyph = &font->glyph[c - font->min_char];
240  bitmapData = glyph->bitmap;
241  if (bitmapData) {
242    width = glyph->width;
243    spanLength = (width + 7) / 8;
244    height = glyph->height;
245
246    for (y = 0; y < height; y++) {
247      for (x = 0; x < width; x++) {
248        if (bitmapData[y * spanLength + x / 8] & (1 << (x & 7))) {
249          putchar('X');
250        } else {
251          putchar('.');
252        }
253      }
254      putchar('\n');
255    }
256  }
257}
258
259void
260getMetric(FontInfoPtr font, int c, TexGlyphInfo * tgi)
261{
262  PerGlyphInfoPtr glyph;
263  unsigned char *bitmapData;
264
265  tgi->c = c;
266  if (c < font->min_char || c > font->max_char) {
267    tgi->width = 0;
268    tgi->height = 0;
269    tgi->xoffset = 0;
270    tgi->yoffset = 0;
271    tgi->dummy = 0;
272    tgi->advance = 0;
273    return;
274  }
275  glyph = &font->glyph[c - font->min_char];
276  bitmapData = glyph->bitmap;
277  if (bitmapData) {
278    tgi->width = glyph->width;
279    tgi->height = glyph->height;
280    tgi->xoffset = glyph->xoffset;
281    tgi->yoffset = glyph->yoffset;
282  } else {
283    tgi->width = 0;
284    tgi->height = 0;
285    tgi->xoffset = 0;
286    tgi->yoffset = 0;
287  }
288  tgi->dummy = 0;
289  tgi->advance = glyph->advance;
290}
291
292int
293glyphCompare(const void *a, const void *b)
294{
295  unsigned char *c1 = (unsigned char *) a;
296  unsigned char *c2 = (unsigned char *) b;
297  TexGlyphInfo tgi1;
298  TexGlyphInfo tgi2;
299
300  getMetric(fontinfo, *c1, &tgi1);
301  getMetric(fontinfo, *c2, &tgi2);
302  return tgi2.height - tgi1.height;
303}
304
305int
306getFontel(unsigned char *bitmapData, int spanLength, int i, int j)
307{
308  return bitmapData[i * spanLength + j / 8] & (1 << (j & 7)) ? 255 : 0;
309}
310
311void
312placeGlyph(FontInfoPtr font, int c, unsigned char *texarea, int stride, int x, int y)
313{
314  PerGlyphInfoPtr glyph;
315  unsigned char *bitmapData;
316  int width, height, spanLength;
317  int i, j;
318
319  if (c < font->min_char || c > font->max_char) {
320    printf("out of range glyph\n");
321    return;
322  }
323  glyph = &font->glyph[c - font->min_char];
324  bitmapData = glyph->bitmap;
325  if (bitmapData) {
326    width = glyph->width;
327    spanLength = (width + 7) / 8;
328    height = glyph->height;
329
330    for (i = 0; i < height; i++) {
331      for (j = 0; j < width; j++) {
332        texarea[stride * (y + i) + x + j] =
333          getFontel(bitmapData, spanLength, i, j);
334      }
335    }
336  }
337}
338
339char *
340nodupstring(char *s)
341{
342  int len, i, p;
343  char *new;
344
345  len = strlen(s);
346  new = (char *) calloc(len + 1, 1);
347  p = 0;
348  for (i = 0; i < len; i++) {
349    if (!strchr(new, s[i])) {
350      new[p] = s[i];
351      p++;
352    }
353  }
354  new = realloc(new, p + 1);
355  return new;
356}
357
358int
359main(int argc, char *argv[])
360{
361  int texw, texh;
362  unsigned char *texarea, *texbitmap;
363  FILE *file;
364  int len, stride;
365  unsigned char *glist;
366  int width, height;
367  int px, py, maxheight;
368  TexGlyphInfo tgi;
369  int usageError = 0;
370  char *fontname, *filename;
371  XFontStruct *xfont;
372  int endianness;
373  int i, j;
374
375  texw = texh = 256;
376  glist = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijmklmnopqrstuvwxyz?.;,!*:\"/+@#$%^&()";
377  fontname = "-adobe-courier-bold-r-normal--46-*-100-100-m-*-iso8859-1";
378  filename = "default.txf";
379
380  for (i = 1; i < argc; i++) {
381    if (!strcmp(argv[i], "-w")) {
382      i++;
383      texw = atoi(argv[i]);
384    } else if (!strcmp(argv[i], "-h")) {
385      i++;
386      texh = atoi(argv[i]);
387    } else if (!strcmp(argv[i], "-gap")) {
388      i++;
389      gap = atoi(argv[i]);
390    } else if (!strcmp(argv[i], "-byte")) {
391      format = TXF_FORMAT_BYTE;
392      break;
393    } else if (!strcmp(argv[i], "-bitmap")) {
394      format = TXF_FORMAT_BITMAP;
395    } else if (!strcmp(argv[i], "-glist")) {
396      i++;
397      glist = (unsigned char *) argv[i];
398    } else if (!strcmp(argv[i], "-fn")) {
399      i++;
400      fontname = argv[i];
401    } else if (!strcmp(argv[i], "-file")) {
402      i++;
403      filename = argv[i];
404    } else {
405      usageError = 1;
406    }
407  }
408
409  if (usageError) {
410    putchar('\n');
411    printf("usage: texfontgen [options] txf-file\n");
412    printf(" -w #          textureWidth (def=%d)\n", texw);
413    printf(" -h #          textureHeight (def=%d)\n", texh);
414    printf(" -gap #        gap between glyphs (def=%d)\n", gap);
415    printf(" -bitmap       use a bitmap encoding (default)\n");
416    printf(" -byte         use a byte encoding (less compact)\n");
417    printf(" -glist ABC    glyph list (def=%s)\n", glist);
418    printf(" -fn name      X font name (def=%s)\n", fontname);
419    printf(" -file name    output file for textured font (def=%s)\n", fontname);
420    putchar('\n');
421    exit(1);
422  }
423  texarea = calloc(texw * texh, sizeof(unsigned char));
424  glist = (unsigned char *) nodupstring((char *) glist);
425
426  dpy = XOpenDisplay(NULL);
427  if (!dpy) {
428    printf("could not open display\n");
429    exit(1);
430  }
431  /* find an OpenGL-capable RGB visual with depth buffer */
432  xfont = XLoadQueryFont(dpy, fontname);
433  if (!xfont) {
434    printf("could not get load X font: %s\n", fontname);
435    exit(1);
436  }
437  fontinfo = SuckGlyphsFromServer(dpy, xfont->fid);
438  if (!fontinfo) {
439    printf("could not get font glyphs\n");
440    exit(1);
441  }
442  len = strlen((char *) glist);
443  qsort(glist, len, sizeof(unsigned char), glyphCompare);
444
445  file = fopen(filename, "wb");
446  fwrite("\377txf", 1, 4, file);
447  endianness = 0x12345678;
448  assert(sizeof(int) == 4);  /* Ensure external file format size. */
449  fwrite(&endianness, sizeof(int), 1, file);
450  fwrite(&format, sizeof(int), 1, file);
451  fwrite(&texw, sizeof(int), 1, file);
452  fwrite(&texh, sizeof(int), 1, file);
453  fwrite(&fontinfo->max_ascent, sizeof(int), 1, file);
454  fwrite(&fontinfo->max_descent, sizeof(int), 1, file);
455  fwrite(&len, sizeof(int), 1, file);
456
457  px = gap;
458  py = gap;
459  maxheight = 0;
460  for (i = 0; i < len; i++) {
461    if (glist[i] != 0) {  /* If not already processed... */
462
463      /* Try to find a character from the glist that will fit on the
464         remaining space on the current row. */
465
466      int foundWidthFit = 0;
467      int c;
468
469      getMetric(fontinfo, glist[i], &tgi);
470      width = tgi.width;
471      height = tgi.height;
472      if (height > 0 && width > 0) {
473        for (j = i; j < len;) {
474          if (height > 0 && width > 0) {
475            if (px + width + gap < texw) {
476              foundWidthFit = 1;
477              if (j != i) {
478                i--;  /* Step back so i loop increment leaves us at same character. */
479              }
480              break;
481            }
482          }
483          j++;
484          getMetric(fontinfo, glist[j], &tgi);
485          width = tgi.width;
486          height = tgi.height;
487        }
488
489        /* If a fit was found, use that character; otherwise, advance a line
490           in  the texture. */
491        if (foundWidthFit) {
492          if (height > maxheight) {
493            maxheight = height;
494          }
495          c = j;
496        } else {
497          getMetric(fontinfo, glist[i], &tgi);
498          width = tgi.width;
499          height = tgi.height;
500
501          py += maxheight + gap;
502          px = gap;
503          maxheight = height;
504          if (py + height + gap >= texh) {
505            printf("Overflowed texture space.\n");
506            exit(1);
507          }
508          c = i;
509        }
510
511        /* Place the glyph in the texture image. */
512        placeGlyph(fontinfo, glist[c], texarea, texw, px, py);
513
514        /* Assign glyph's texture coordinate. */
515        tgi.x = px;
516        tgi.y = py;
517
518        /* Advance by glyph width, remaining in the current line. */
519        px += width + gap;
520      } else {
521        /* No texture image; assign invalid bogus texture coordinates. */
522        tgi.x = -1;
523        tgi.y = -1;
524      }
525      glist[c] = 0;     /* Mark processed; don't process again. */
526      assert(sizeof(tgi) == 12);  /* Ensure external file format size. */
527      fwrite(&tgi, sizeof(tgi), 1, file);
528    }
529  }
530
531  switch (format) {
532  case TXF_FORMAT_BYTE:
533    fwrite(texarea, texw * texh, 1, file);
534    break;
535  case TXF_FORMAT_BITMAP:
536    stride = (texw + 7) >> 3;
537    texbitmap = (unsigned char *) calloc(stride * texh, 1);
538    for (i = 0; i < texh; i++) {
539      for (j = 0; j < texw; j++) {
540        if (texarea[i * texw + j] >= 128) {
541          texbitmap[i * stride + (j >> 3)] |= 1 << (j & 7);
542        }
543      }
544    }
545    fwrite(texbitmap, stride * texh, 1, file);
546    free(texbitmap);
547    break;
548  default:
549    printf("Unknown texture font format.\n");
550    exit(1);
551  }
552  free(texarea);
553  fclose(file);
554  return 0;
555}
Note: See TracBrowser for help on using the repository browser.