#include #include #include #include "base.h" #include "font.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "engine.h" Uint8 prescale2[4] = { 0, 85, 170, 255 }, prescale3[8] = { 0, 36, 72, 109, 145, 182, 218, 255 }; #define STRBUFSIZ 512 /* font file format ================ off|siz|description ---+---+------------------------------- 0 | 2 | Number of entries = nbentries 2 | 1 | Flags 3 | 1 | maxX (maximum width) 4 | 1 | maxY (maximum height) 5 | 1 | base (bottom line from top) 6 | 1 | inter (size of the interline) 7 | X | char entries 7+X| Y | char map X = (maxX * maxY + 1) * nbentries Y = nbentries * 4 Flags: ===== 0000000R R = RGBA (=1) or Alpha (=0) RGBA in 1232 format: ABBGGGRR Each entries: ============ off|siz|description ---+---+------------------------------- 0 | 1 | True size of the entry 1 | Z | Datas Z = maxX * maxY Datas are stored in order X first, then Y. Char map: ======== nbentries entries, each entry = 4 bytes = 2 uint16 off|siz|description ---+---+------------------------------- 0 | 2 | Unicode (?) 2 | 2 | Corresponding char entry I'm not sure about my word 'Unicode'. I write this only to say it's an attempt to make the fonts "internationals". If the "unicode" is < 255, then it should match only one byte in the string. Otherwise, it should match two bytes. Variables comments ================== nbcU = number of chars on X by texture nbcV = number of chars on Y by texture nbcT = number of char by texture nbT = number of textures */ mogltk::ColorP colorcached[16] = { DOS_BLACK, DOS_BLUE, DOS_GREEN, DOS_CYAN, DOS_RED, DOS_MAGENTA, DOS_BRAWN, DOS_WHITE, DOS_GRAY, DOS_HIGH_BLUE, DOS_HIGH_GREEN, DOS_HIGH_CYAN, DOS_HIGH_RED, DOS_HIGH_MAGENTA, DOS_YELLOW, DOS_HIGH_WHITE }; mogltk::font::font(Handle * ffont) : textcolor(255, 255, 255, 255), shadow(0), wspace(0) { int i, j; nbentries = ffont->readU16(); flags = ffont->readU8(); maxX = ffont->readU8(); maxY = ffont->readU8(); base = ffont->readU8(); inter = ffont->readU8(); nbcU = 256 / maxX; nbcV = 256 / maxY; nbcT = nbcU * nbcV; nbT = nbentries / nbcT; if (nbentries % nbcT) { nbT++; } printm(M_INFO, "Creating font texture: %i entries, flags = 0x%02x, maxX = %i, maxY = %i\n", nbentries, flags, maxX, maxY); printm(M_INFO, "Which makes %i texture(s), with %i char by texture, %i on X, and %i on Y\n", nbT, nbcT, nbcU, nbcV); fonttex = (texture **) malloc(nbT * sizeof(texture *)); for (i = 0; i < 16; i++) { fontcache[i] = (texture **) malloc(nbT * sizeof(texture *)); } for (i = 0; i < nbT; i++) { fonttex[i] = alloctexture(); for (j = 0; j < 15; j++) { fontcache[j][i] = 0; } fontcache[15][i] = fonttex[i]; } sizes = (Uint8 *) malloc(nbentries * sizeof(Uint8)); Uint8 * curtex = (Uint8 *) fonttex[0]->GetSurface()->pixels; Uint32 curU = 0, curV = 0, curT = 0; for (i = 0; i < nbentries; i++) { sizes[i] = ffont->readU8(); for (int v = 0; v < maxY; v++) { for (int u = 0; u < maxX; u++) { Uint8 f; f = ffont->readU8(); if (flags & 1) { Uint8 r, g, b, a; r = f & 3; g = (f >> 2) & 7; b = (f >> 5) & 3; a = (f >> 7) & 1; curtex[(curU + u + (curV + v) * 256) * 4 + 0] = prescale2[r]; curtex[(curU + u + (curV + v) * 256) * 4 + 1] = prescale3[g]; curtex[(curU + u + (curV + v) * 256) * 4 + 2] = prescale2[b]; curtex[(curU + u + (curV + v) * 256) * 4 + 3] = a ? 255 : 0; } else { curtex[(curU + u + (curV + v) * 256) * 4 + 0] = 255; curtex[(curU + u + (curV + v) * 256) * 4 + 1] = 255; curtex[(curU + u + (curV + v) * 256) * 4 + 2] = 255; curtex[(curU + u + (curV + v) * 256) * 4 + 3] = f; } } } if (((curU += maxX) + maxX) > 256) { curU = 0; if (((curV += maxY) + maxY) > 256) { curV = 0; if ((curT + 1) != nbT) curtex = (Uint8 *) fonttex[++curT]->GetSurface()->pixels; } } } corresp = (Uint16 *) malloc(nbentries * 2 * sizeof(Uint16)); for (i = 0; i < 2 * nbentries; i++) { corresp[i] = ffont->readU16(); } } mogltk::font::~font() { int i, j; for (i = 0; i < nbT; i++) for (j = 0; j < 16; j++) if (fontcache[j][i]) delete fontcache[j][i]; for (i = 0; i < 16; i++) free((void *&) fontcache[i]); free((void *&) fonttex); free(sizes); } void mogltk::font::drawentry(Uint16 entry, int x, int y, ColorP c) { bool locked = false; int trueentry, cx, cy, px, py; SDL_Rect src, dst; if (SDL_MUSTLOCK(mogltk::engine::base_o->getsurface())) { locked = true; SDL_LockSurface(mogltk::engine::base_o->getsurface()); } if (shadow) { int os = shadow; shadow = 0; drawentry(entry, x + os, y + os, BLACK); shadow = os; } checknbind(entry / nbcT, c); y -= base; trueentry = entry % nbcT; cx = trueentry % nbcU; cy = trueentry / nbcU; px = cx * maxX; py = cy * maxY; src.x = px; src.y = py; src.w = maxX; src.h = maxY; dst.x = x; dst.y = y; dst.w = maxX; dst.h = maxY; SDL_BlitSurface(fonttex[entry / nbcT]->GetSurface(), &src, mogltk::engine::base_o->getsurface(), &dst); if (locked) SDL_UnlockSurface(mogltk::engine::base_o->getsurface()); } void mogltk::font::drawtotex(texture * t, Uint16 entry, int x, int y, ColorP c) { bool locked = false; int trueentry, cx, cy, px, py; SDL_Rect src, dst; if (SDL_MUSTLOCK(t->GetSurface())) { locked = true; SDL_LockSurface(t->GetSurface()); } if (shadow) { int os = shadow; shadow = 0; drawtotex(t, entry, x + os, y + os, BLACK); shadow = os; } checknbind(entry / nbcT, c); y -= base; trueentry = entry % nbcT; cx = trueentry % nbcU; cy = trueentry / nbcU; px = cx * maxX; py = cy * maxY; src.x = px; src.y = py; src.w = maxX; src.h = maxY; dst.x = x; dst.y = y; dst.w = maxX; dst.h = maxY; SDL_BlitSurface(fonttex[entry / nbcT]->GetSurface(), &src, t->GetSurface(), &dst); if (locked) SDL_UnlockSurface(t->GetSurface()); } void mogltk::font::putcursor(int x, int y) { cx = ox = x; cy = y; } void mogltk::font::putentry(Uint16 entry, ColorP c) { drawentry(entry, cx, cy, c); cx += sizes[entry] + wspace; } void mogltk::font::putentryontex(texture * t, Uint16 entry, ColorP c) { drawtotex(t, entry, cx, cy, c); cx += sizes[entry] + wspace; } void mogltk::font::drawchar(char ch, ColorP c) { Uint16 * p; int i; for (i = 0, p = corresp; i < nbentries; i++, p++) { if (*(p++) == ch) { putentry(*p, c); return; } } } void mogltk::font::drawcharontex(texture * t, char ch, ColorP c) { Uint16 * p; int i; for (i = 0, p = corresp; i < nbentries; i++, p++) { if (*(p++) == ch) { putentryontex(t, *p, c); return; } } } int mogltk::font::findchar(char ch) const { Uint16 * p; int i; for (i = 0, p = corresp; i < nbentries; i++, p++) { if (*(p++) == ch) { return *p; } } return -1; } void mogltk::font::newline(void) { cx = ox; cy += inter; } int mogltk::font::printf(const ugly_string & m, va_list ap) { char * p; static char buffer[STRBUFSIZ + 1]; int r; #ifdef HAVE_VSNPRINTF r = vsnprintf(buffer, STRBUFSIZ, m.p, ap); #else r = vsprintf(buffer, m.p, ap); #endif for (p = buffer; *p; p++) { if (*p == '\n') { newline(); } else { drawchar(*p, textcolor); } } return r; } int mogltk::font::printf(const ugly_string & m, ...) { va_list ap; int r; va_start(ap, m); r = printf(m, ap); va_end(ap); return r; } int mogltk::font::printf(const char * p, ...) { ugly_string m; va_list ap; int r; m.p = p; va_start(ap, p); r = printf(m, ap); va_end(ap); return r; } mogltk::rect mogltk::font::size(const ugly_string & m, va_list ap) { char * p; static char buffer[STRBUFSIZ + 1]; rect r; int mw, w; r.x = cx; r.y = cy; r.h = inter; r.w = 0; mw = 0; w = 0; #ifdef HAVE_VSNPRINTF vsnprintf(buffer, STRBUFSIZ, m.p, ap); #else vsprintf(buffer, m.p, ap); #endif for (p = buffer; *p; p++) { if (*p == '\n') { if (*(p+1)) { r.h += inter; if (w > mw) { mw = w; } w = 0; } } else { w += sizes[findchar(*p)] + wspace; } } if (w > mw) { mw = w; } r.w = mw; return r; } mogltk::rect mogltk::font::size(const ugly_string & m, ...) { va_list ap; rect r; va_start(ap, m); r = size(m, ap); va_end(ap); return r; } mogltk::rect mogltk::font::size(const char * p, ...) { ugly_string m; va_list ap; rect r; m.p = p; va_start(ap, p); r = size(m, ap); va_end(ap); return r; } mogltk::rect mogltk::font::printtotex(texture * t, const ugly_string & m, va_list ap) { char * p; static char buffer[STRBUFSIZ + 1]; rect r; int mw, w; r.x = cx; r.y = cy; r.h = inter; r.w = 0; mw = 0; w = 0; #ifdef HAVE_VSNPRINTF vsnprintf(buffer, STRBUFSIZ, m.p, ap); #else vsprintf(buffer, m.p, ap); #endif for (p = buffer; *p; p++) { if (*p == '\n') { if (*(p+1)) { r.h += inter; if (w > mw) { mw = w; } w = 0; } } else { w += sizes[findchar(*p)] + wspace; } } if (w > mw) { mw = w; } r.w = mw; for (p = buffer; *p; p++) { if (*p == '\n') { newline(); } else { drawcharontex(t, *p, textcolor); } } return r; } mogltk::rect mogltk::font::printtotex(texture * t, const ugly_string & m, ...) { va_list ap; rect r; va_start(ap, m); r = printtotex(t, m, ap); va_end(ap); return r; } mogltk::rect mogltk::font::printtotex(texture * t, const char * p, ...) { ugly_string m; va_list ap; rect r; m.p = p; va_start(ap, p); r = printtotex(t, m, ap); va_end(ap); return r; } inline unsigned int nextpower(unsigned int n) { unsigned int i; if (!n) return n; if (ISPOT(n)) return n; for (i = 31; i >= 0; i--) { if ((n >> i) & 1) { return 1 << (i + 1); } } } mogltk::texture * mogltk::font::printtex(rect * _r, const ugly_string & m, va_list ap) { rect r; char * p; static char buffer[STRBUFSIZ + 1]; int mw, w, pw, ph; texture * t; int ocx, ocy, oox; ocx = cx; ocy = cy; oox = ox; cx = ox = 0; cy = base; r.x = cx; r.y = cy; r.h = inter; r.w = 0; mw = 0; w = 0; #ifdef HAVE_VSNPRINTF vsnprintf(buffer, STRBUFSIZ, m.p, ap); #else vsprintf(buffer, m.p, ap); #endif for (p = buffer; *p; p++) { if (*p == '\n') { if (*(p+1)) { r.h += inter; if (w > mw) { mw = w; } w = 0; } } else { w += sizes[findchar(*p)] + wspace; } } if (w > mw) { mw = w; } r.w = mw; pw = nextpower(r.w); ph = nextpower(r.h); t = new texture(pw, ph); for (p = buffer; *p; p++) { if (*p == '\n') { newline(); } else { drawcharontex(t, *p, textcolor); } } if (_r) *_r = r; return t; } mogltk::texture * mogltk::font::printtex(rect * r, const ugly_string & m, ...) { va_list ap; texture * t; va_start(ap, m); t = printtex(r, m, ap); va_end(ap); return t; } mogltk::texture * mogltk::font::printtex(rect * r, const char * p, ...) { ugly_string m; va_list ap; texture * t; m.p = p; va_start(ap, p); t = printtex(r, m, ap); va_end(ap); return t; } void mogltk::font::setcolor(ColorP c) { textcolor = c; } void mogltk::font::setshadow(int s) { shadow = s; } void mogltk::font::setwspace(int w) { wspace = w; } int mogltk::font::singletextsize(const String & s) const { unsigned int i; int r = 0; for (i = 0; i < s.strlen(); i++) { r += sizes[findchar(s[i])] + wspace; } return r; } mogltk::font * mogltk::SystemFont; mogltk::texture * mogltk::font::alloctexture() { return new mogltk::texture(256, 256); } void mogltk::font::Bind(int f) { fonttex[f]->Bind(); } void mogltk::font::checknbind(int index, ColorP c) { int i, x, y; ColorP oldmax = ColorP::Max, t; ColorP::Max = c.c; SDL_PixelFormat * f = fonttex[0]->GetSurface()->format; for (i = 0; i < 15; i++) if (c == colorcached[i]) break; if (!fontcache[i][index]) { fontcache[i][index] = alloctexture(); for (y = 0; y < 256; y++) { for (x = 0; x < 256; x++) { t.fromSDL(fontcache[15][index]->GetPixels()[(y << 8) + x], f); fontcache[i][index]->GetPixels()[(y << 8) + x] = t.toSDL(f); } } } fonttex[index] = fontcache[i][index]; ColorP::Max = oldmax.c; }