1 // The following implementation is based on TexFont API,
2 // implementation and accompanying programs by Mark J. Kilgard.
5 /* Copyright (c) Mark J. Kilgard, 1997. */
6 /* This program is freely distributable without licensing fees and is
7 provided without guarantee or warrantee expressed or implied. This
8 program is -not- in the public domain. */
22 #include <Reve/Reve.h>
23 #include <Reve/GLTextNS.h>
29 TexFont* fgDefaultFont = 0;
32 /* Uncomment to debug various scenarios. */
34 #undef GL_EXT_texture_object
38 int useLuminanceAlpha = 1;
40 /* byte swap a 32-bit value */
41 #define SWAPL(x, n) { \
42 n = ((char *) (x))[0]; \
43 ((char *) (x))[0] = ((char *) (x))[3]; \
44 ((char *) (x))[3] = n; \
45 n = ((char *) (x))[1]; \
46 ((char *) (x))[1] = ((char *) (x))[2]; \
47 ((char *) (x))[2] = n; }
49 /* byte swap a short */
50 #define SWAPS(x, n) { \
51 n = ((char *) (x))[0]; \
52 ((char *) (x))[0] = ((char *) (x))[1]; \
53 ((char *) (x))[1] = n; }
55 /**************************************************************************/
57 static TexGlyphVertexInfo* getTCVI(TexFont * txf, int c)
59 TexGlyphVertexInfo *tgvi;
61 /* Automatically substitute uppercase letters with lowercase if not
62 uppercase available (and vice versa). */
63 if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) {
64 tgvi = txf->lut[c - txf->min_glyph];
70 if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) {
71 return txf->lut[c - txf->min_glyph];
76 if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) {
77 return txf->lut[c - txf->min_glyph];
82 //fprintf(stderr, "texfont: tried to access unavailable font character \"%c\" (%d)\n",
83 // isprint(c) ? c : ' ', c);
85 tgvi = txf->lut[' ' - txf->min_glyph];
86 if (tgvi) return tgvi;
87 tgvi = txf->lut['_' - txf->min_glyph];
88 if (tgvi) return tgvi;
93 /**************************************************************************/
95 static char *lastError;
97 char* txfErrorString(void)
102 /**************************************************************************/
104 TexFont* txfLoadFont(const char *filename)
108 GLfloat w, h, xstep, ystep;
110 unsigned char *texbitmap;
111 int min_glyph, max_glyph;
112 int endianness, swap, format, stride, width, height;
116 file = fopen(filename, "rb");
118 lastError = "file open failed.";
121 txf = (TexFont *) malloc(sizeof(TexFont));
123 lastError = "out of memory.";
126 /* For easy cleanup in error case. */
127 txf->texobj = 0; // MT add
131 txf->teximage = NULL;
133 got = fread(fileid, 1, 4, file);
134 if (got != 4 || strncmp(fileid, "\377txf", 4)) {
135 lastError = "not a texture font file.";
138 assert(sizeof(int) == 4); /* Ensure external file format size. */
139 got = fread(&endianness, sizeof(int), 1, file);
140 if (got == 1 && endianness == 0x12345678) {
142 } else if (got == 1 && endianness == 0x78563412) {
145 lastError = "not a texture font file.";
148 #define EXPECT(n) if (got != n) { lastError = "premature end of file."; goto error; }
149 got = fread(&format, sizeof(int), 1, file);
151 got = fread(&txf->tex_width, sizeof(int), 1, file);
153 got = fread(&txf->tex_height, sizeof(int), 1, file);
155 got = fread(&txf->max_ascent, sizeof(int), 1, file);
157 got = fread(&txf->max_descent, sizeof(int), 1, file);
159 got = fread(&txf->num_glyphs, sizeof(int), 1, file);
164 SWAPL(&txf->tex_width, tmp);
165 SWAPL(&txf->tex_height, tmp);
166 SWAPL(&txf->max_ascent, tmp);
167 SWAPL(&txf->max_descent, tmp);
168 SWAPL(&txf->num_glyphs, tmp);
170 txf->tgi = (TexGlyphInfo *) malloc(txf->num_glyphs * sizeof(TexGlyphInfo));
171 if (txf->tgi == NULL) {
172 lastError = "out of memory.";
175 assert(sizeof(TexGlyphInfo) == 12); /* Ensure external file format size. */
176 got = fread(txf->tgi, sizeof(TexGlyphInfo), txf->num_glyphs, file);
177 EXPECT(txf->num_glyphs);
180 for (i = 0; i < txf->num_glyphs; i++) {
181 SWAPS(&txf->tgi[i].c, tmp);
182 SWAPS(&txf->tgi[i].x, tmp);
183 SWAPS(&txf->tgi[i].y, tmp);
186 txf->tgvi = (TexGlyphVertexInfo *)
187 malloc(txf->num_glyphs * sizeof(TexGlyphVertexInfo));
188 if (txf->tgvi == NULL) {
189 lastError = "out of memory.";
197 for (i = 0; i < txf->num_glyphs; i++) {
201 txf->tgvi[i].t0[0] = tgi->x / w - xstep; // MT - xstep
202 txf->tgvi[i].t0[1] = tgi->y / h - ystep; // MT - ystep
203 txf->tgvi[i].v0[0] = tgi->xoffset;
204 txf->tgvi[i].v0[1] = tgi->yoffset;
205 txf->tgvi[i].t1[0] = (tgi->x + tgi->width) / w + xstep;
206 txf->tgvi[i].t1[1] = tgi->y / h - ystep; // MT - ystep
207 txf->tgvi[i].v1[0] = tgi->xoffset + tgi->width;
208 txf->tgvi[i].v1[1] = tgi->yoffset;
209 txf->tgvi[i].t2[0] = (tgi->x + tgi->width) / w + xstep;
210 txf->tgvi[i].t2[1] = (tgi->y + tgi->height) / h + ystep;
211 txf->tgvi[i].v2[0] = tgi->xoffset + tgi->width;
212 txf->tgvi[i].v2[1] = tgi->yoffset + tgi->height;
213 txf->tgvi[i].t3[0] = tgi->x / w - xstep; // MT - xstep
214 txf->tgvi[i].t3[1] = (tgi->y + tgi->height) / h + ystep;
215 txf->tgvi[i].v3[0] = tgi->xoffset;
216 txf->tgvi[i].v3[1] = tgi->yoffset + tgi->height;
217 txf->tgvi[i].advance = tgi->advance;
219 if(tgi->width > txf->max_width) txf->max_width = tgi->width;
222 min_glyph = txf->tgi[0].c;
223 max_glyph = txf->tgi[0].c;
224 for (i = 1; i < txf->num_glyphs; i++) {
225 if (txf->tgi[i].c < min_glyph) {
226 min_glyph = txf->tgi[i].c;
228 if (txf->tgi[i].c > max_glyph) {
229 max_glyph = txf->tgi[i].c;
232 txf->min_glyph = min_glyph;
233 txf->range = max_glyph - min_glyph + 1;
235 txf->lut = (TexGlyphVertexInfo **)
236 calloc(txf->range, sizeof(TexGlyphVertexInfo *));
237 if (txf->lut == NULL) {
238 lastError = "out of memory.";
241 for (i = 0; i < txf->num_glyphs; i++) {
242 txf->lut[txf->tgi[i].c - txf->min_glyph] = &txf->tgvi[i];
246 case TXF_FORMAT_BYTE:
247 if (useLuminanceAlpha) {
250 orig = (unsigned char *) malloc(txf->tex_width * txf->tex_height);
252 lastError = "out of memory.";
255 got = fread(orig, 1, txf->tex_width * txf->tex_height, file);
256 EXPECT(txf->tex_width * txf->tex_height);
257 txf->teximage = (unsigned char *)
258 malloc(2 * txf->tex_width * txf->tex_height);
259 if (txf->teximage == NULL) {
260 lastError = "out of memory.";
263 for (i = 0; i < txf->tex_width * txf->tex_height; i++) {
264 txf->teximage[i * 2] = orig[i];
265 txf->teximage[i * 2 + 1] = orig[i];
269 txf->teximage = (unsigned char *)
270 malloc(txf->tex_width * txf->tex_height);
271 if (txf->teximage == NULL) {
272 lastError = "out of memory.";
275 got = fread(txf->teximage, 1, txf->tex_width * txf->tex_height, file);
276 EXPECT(txf->tex_width * txf->tex_height);
279 case TXF_FORMAT_BITMAP:
280 width = txf->tex_width;
281 height = txf->tex_height;
282 stride = (width + 7) >> 3;
283 texbitmap = (unsigned char *) malloc(stride * height);
284 if (texbitmap == NULL) {
285 lastError = "out of memory.";
288 got = fread(texbitmap, 1, stride * height, file);
289 EXPECT(stride * height);
290 if (useLuminanceAlpha) {
291 txf->teximage = (unsigned char *) calloc(width * height * 2, 1);
292 if (txf->teximage == NULL) {
293 lastError = "out of memory.";
296 for (i = 0; i < height; i++) {
297 for (j = 0; j < width; j++) {
298 if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7))) {
299 txf->teximage[(i * width + j) * 2] = 255;
300 txf->teximage[(i * width + j) * 2 + 1] = 255;
305 txf->teximage = (unsigned char *) calloc(width * height, 1);
306 if (txf->teximage == NULL) {
307 lastError = "out of memory.";
310 for (i = 0; i < height; i++) {
311 for (j = 0; j < width; j++) {
312 if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7))) {
313 txf->teximage[i * width + j] = 255;
343 /**************************************************************************/
345 GLuint txfEstablishTexture(TexFont * txf, GLuint texobj,
346 GLboolean setupMipmaps)
348 if (txf->texobj == 0) {
350 glGenTextures(1, &txf->texobj);
352 txf->texobj = texobj;
355 glBindTexture(GL_TEXTURE_2D, txf->texobj);
357 if (useLuminanceAlpha) {
359 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA,
360 txf->tex_width, txf->tex_height,
361 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, txf->teximage);
363 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
364 txf->tex_width, txf->tex_height, 0,
365 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, txf->teximage);
369 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_INTENSITY4,
370 txf->tex_width, txf->tex_height,
371 GL_LUMINANCE, GL_UNSIGNED_BYTE, txf->teximage);
373 glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY4,
374 txf->tex_width, txf->tex_height, 0,
375 GL_LUMINANCE, GL_UNSIGNED_BYTE, txf->teximage);
379 // MT: tried changing MIN/MAG filters ... bad idea.
384 /**************************************************************************/
386 void txfBindFontTexture(TexFont * txf)
388 glBindTexture(GL_TEXTURE_2D, txf->texobj);
391 /**************************************************************************/
393 void txfUnloadFont(TexFont * txf)
396 glDeleteTextures(1, &txf->texobj);
407 /**************************************************************************/
409 void txfGetStringMetrics(TexFont * txf, const char *TString, int len,
410 int &width, int &max_ascent, int &max_descent)
412 TexGlyphVertexInfo *tgvi;
417 for (i = 0; i < len; i++) {
418 if (TString[i] == 27) {
419 switch (TString[i + 1]) {
434 tgvi = getTCVI(txf, TString[i]);
435 w += int(tgvi->advance);
436 ma = TMath::Max(ma, (int)( tgvi->v3[1]));
437 md = TMath::Max(md, (int)(-tgvi->v0[1]));
441 max_ascent = ma; // txf->max_ascent;
442 max_descent = md; // txf->max_descent;
443 // printf("%d %d %d %d\n", txf->max_ascent, txf->max_descent, ma, md);
446 /**************************************************************************/
448 void txfRenderGlyph(TexFont * txf, int c)
450 TexGlyphVertexInfo *tgvi;
452 tgvi = getTCVI(txf, c);
454 glTexCoord2fv(tgvi->t0);
455 glVertex2sv(tgvi->v0);
456 glTexCoord2fv(tgvi->t1);
457 glVertex2sv(tgvi->v1);
458 glTexCoord2fv(tgvi->t2);
459 glVertex2sv(tgvi->v2);
460 glTexCoord2fv(tgvi->t3);
461 glVertex2sv(tgvi->v3);
463 glTranslatef(tgvi->advance, 0.0, 0.0);
466 void txfRenderString(TexFont * txf, const char *TString, int len,
470 if(keep_pos) glPushMatrix();
471 for (i = 0; i < len; i++) {
472 txfRenderGlyph(txf, TString[i]);
474 if(keep_pos) glPopMatrix();
477 void txfRenderString(TexFont * txf, const char *TString, int len,
478 GLfloat maxx, GLfloat fadew,
481 GLfloat x = 0, xg0, xg1, yg0, yg1, f0, f1;
482 fadew *= txf->max_width;
483 GLfloat xfade = maxx - fadew;
486 glGetFloatv(GL_CURRENT_COLOR, col);
489 for (int i = 0; i < len; i++) {
491 TexGlyphVertexInfo *tgvi;
493 tgvi = getTCVI(txf, TString[i]);
495 xg0 = x + tgvi->v0[0];
496 xg1 = x + tgvi->v1[0];
501 f0 = 1; if(xg0 > xfade) f0 *= 1 - (xg0-xfade)/fadew;
502 f1 = 1 - (xg1-xfade)/fadew;
504 // printf("XX %s %c %f %f x(%f,%f) y(%f,%f)\n",
505 // TString, TString[i], f0, f1,
506 // xg0, xg1,yg0, yg1);
508 glColor4f(f0*col[0], f0*col[1], f0*col[2], f0*col[3]);
509 glTexCoord2fv(tgvi->t0); glVertex2f(xg0, yg0);
510 glColor4f(f1*col[0], f1*col[1], f1*col[2], f1*col[3]);
511 glTexCoord2fv(tgvi->t1); glVertex2f(xg1, yg0);
512 glTexCoord2fv(tgvi->t2); glVertex2f(xg1, yg1);
513 glColor4f(f0*col[0], f0*col[1], f0*col[2], f0*col[3]);
514 glTexCoord2fv(tgvi->t3); glVertex2f(xg0, yg1);
516 glTexCoord2fv(tgvi->t0); glVertex2f(xg0, yg0);
517 glTexCoord2fv(tgvi->t1); glVertex2f(xg1, yg0);
518 glTexCoord2fv(tgvi->t2); glVertex2f(xg1, yg1);
519 glTexCoord2fv(tgvi->t3); glVertex2f(xg0, yg1);
527 if(!keep_pos) glTranslatef(x, 0.0, 0.0);
530 /**************************************************************************/
532 void txfRenderGlyphZW(TexFont * txf, int c, float z, float w)
534 TexGlyphVertexInfo *tgvi;
536 tgvi = getTCVI(txf, c);
538 glTexCoord2fv(tgvi->t0);
539 glVertex4f(tgvi->v0[0], tgvi->v0[1], z, w);
540 glTexCoord2fv(tgvi->t1);
541 glVertex4f(tgvi->v1[0], tgvi->v1[1], z, w);
542 glTexCoord2fv(tgvi->t2);
543 glVertex4f(tgvi->v2[0], tgvi->v2[1], z, w);
544 glTexCoord2fv(tgvi->t3);
545 glVertex4f(tgvi->v3[0], tgvi->v3[1], z, w);
547 glTranslatef(tgvi->advance, 0.0, 0.0);
550 void txfRenderStringZW(TexFont * txf, const char *TString, int len,
551 float z, float w, bool keep_pos)
555 if(keep_pos) glPushMatrix();
556 for (i = 0; i < len; i++) {
557 txfRenderGlyphZW(txf, TString[i], z, w);
559 if(keep_pos) glPopMatrix();
562 /**************************************************************************/
565 MONO, TOP_BOTTOM, LEFT_RIGHT, FOUR
568 /**************************************************************************/
570 void txfRenderFancyString(TexFont * txf, char *TString, int len)
572 TexGlyphVertexInfo *tgvi;
577 for (i = 0; i < len; i++) {
578 if (TString[i] == 27) {
579 switch (TString[i + 1]) {
582 glColor3ubv((GLubyte *) & TString[i + 2]);
587 memcpy(c, &TString[i + 2], 6);
592 memcpy(c, &TString[i + 2], 6);
597 memcpy(c, &TString[i + 2], 12);
604 txfRenderGlyph(txf, TString[i]);
607 tgvi = getTCVI(txf, TString[i]);
610 glTexCoord2fv(tgvi->t0);
611 glVertex2sv(tgvi->v0);
612 glTexCoord2fv(tgvi->t1);
613 glVertex2sv(tgvi->v1);
615 glTexCoord2fv(tgvi->t2);
616 glVertex2sv(tgvi->v2);
617 glTexCoord2fv(tgvi->t3);
618 glVertex2sv(tgvi->v3);
620 glTranslatef(tgvi->advance, 0.0, 0.0);
623 tgvi = getTCVI(txf, TString[i]);
626 glTexCoord2fv(tgvi->t0);
627 glVertex2sv(tgvi->v0);
629 glTexCoord2fv(tgvi->t1);
630 glVertex2sv(tgvi->v1);
632 glTexCoord2fv(tgvi->t2);
633 glVertex2sv(tgvi->v2);
635 glTexCoord2fv(tgvi->t3);
636 glVertex2sv(tgvi->v3);
638 glTranslatef(tgvi->advance, 0.0, 0.0);
641 tgvi = getTCVI(txf, TString[i]);
644 glTexCoord2fv(tgvi->t0);
645 glVertex2sv(tgvi->v0);
647 glTexCoord2fv(tgvi->t1);
648 glVertex2sv(tgvi->v1);
650 glTexCoord2fv(tgvi->t2);
651 glVertex2sv(tgvi->v2);
653 glTexCoord2fv(tgvi->t3);
654 glVertex2sv(tgvi->v3);
656 glTranslatef(tgvi->advance, 0.0, 0.0);
663 /**************************************************************************/
665 int txfInFont(TexFont * txf, int c)
667 /* NOTE: No uppercase/lowercase substituion. */
668 if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) {
669 if (txf->lut[c - txf->min_glyph]) {
676 /**************************************************************************/
678 bool LoadDefaultFont( TString file)
680 static const Exc_t _eh("GLTextNS::LoadFont ");
683 txfUnloadFont(fgDefaultFont);
687 fgDefaultFont = GLTextNS::txfLoadFont(file.Data());
688 if(fgDefaultFont != 0) {
689 txfEstablishTexture(fgDefaultFont, 0, GL_TRUE);
693 throw(_eh + Form("Error loading font from file '%s': %s",
694 file.Data(), txfErrorString()));