backported text rendering from sauer

git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2873 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
Lee Salzman
2008-10-10 03:00:53 +00:00
parent 5e246d3d5d
commit b80bbb88d3
9 changed files with 351 additions and 249 deletions
+42 -25
View File
@@ -28,30 +28,49 @@ struct console : consolebuffer
else fullconsole = ++fullconsole % 3;
}
void addline(const char *sf, bool highlight) { consolebuffer::addline(sf, highlight, totalmillis); }
void addline(const char *sf) { consolebuffer::addline(sf, totalmillis); }
vector<char *> visible;
void render()
{
if(fullconsole)
int conwidth = VIRTW*2 - 2*CONSPAD - 2*FONTH/3, h = VIRTH*2 - 2*CONSPAD - 2*FONTH/3,
conheight = min(fullconsole ? (h*(fullconsole==1 ? altconsize : fullconsize))/100 : FONTH*consize, h);
if(fullconsole) blendbox(CONSPAD, CONSPAD, conwidth+CONSPAD+2*FONTH/3, conheight+CONSPAD+2*FONTH/3, true);
int numl = conlines.length(), offset = min(conskip, numl);
if(!fullconsole && confade)
{
int w = VIRTW*2, h = VIRTH*2 - 2*CONSPAD - 2*FONTH/3;
int numl = ((h*(fullconsole==1 ? altconsize : fullconsize))/100)/(FONTH*5/4);
if(numl <= 0) return;
int offset = min(conskip, max(conlines.length() - numl, 0));
blendbox(CONSPAD, CONSPAD, w-CONSPAD, 2*CONSPAD+numl*(FONTH*5/4)+2*FONTH/3, true);
loopi(numl) draw_text(offset+i>=conlines.length() ? "" : conlines[offset+i].cref, CONSPAD+FONTH/3, CONSPAD+(FONTH*5/4)*(numl-i-1)+FONTH/3);
}
else if(consize)
{
visible.setsizenodelete(0);
loopv(conlines) if(conskip ? i>=conskip-1 || i>=conlines.length()-consize : (!confade || totalmillis-conlines[i].millis<confade*1000))
if(!conskip)
{
visible.add(conlines[i].cref);
if(visible.length()>=consize) break;
numl = 0;
loopvrev(conlines) if(totalmillis-conlines[i].millis < confade*1000) { numl = i+1; break; }
}
loopvj(visible) draw_text(visible[j], CONSPAD+FONTH/3, CONSPAD+(FONTH*5/4)*(visible.length()-j-1)+FONTH/3);
else offset--;
}
int y = 0;
loopi(numl) //determine visible height
{
// shuffle backwards to fill if necessary
int idx = offset+i < numl ? offset+i : --offset;
char *line = conlines[idx].line;
int width, height;
text_bounds(line, width, height, conwidth);
y += height;
if(y > conheight) { numl = i; if(offset == idx) ++offset; break; }
}
y = CONSPAD+FONTH/3;
loopi(numl)
{
int idx = offset + numl-i-1;
char *line = conlines[idx].line;
draw_text(line, CONSPAD+FONTH/3, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth);
int width, height;
text_bounds(line, width, height, conwidth);
y += height;
}
}
@@ -77,19 +96,17 @@ void conoutf(const char *s, ...)
string sp;
filtertext(sp, sf);
puts(sp);
s = sf;
vector<char *> lines;
text_block(s, curfont ? VIRTW*2-2*CONSPAD-2*FONTH/3 : 0, lines);
loopv(lines) con.addline(lines[i], false);
lines.deletecontentsa();
con.addline(sf);
}
void rendercommand(int x, int y)
int rendercommand(int x, int y, int w)
{
s_sprintfd(s)("> %s", cmdline.buf);
int offset = text_width(s, cmdline.pos>=0 ? cmdline.pos+2 : -1);
rendercursor(x+offset, y, char_width(cmdline.pos>=0 ? cmdline.buf[cmdline.pos] : '_'));
draw_text(s, x, y);
int width, height;
text_bounds(s, width, height, w);
y -= height - FONTH;
draw_text(s, x, y, 0xFF, 0xFF, 0xFF, 0xFF, cmdline.pos>=0 ? cmdline.pos+2 : strlen(s), w);
return height;
}
// keymap is defined externally in keymap.cfg
+4 -14
View File
@@ -1,26 +1,16 @@
struct cline { char *cref; int millis; };
struct cline { char *line; int millis; };
struct consolebuffer
{
vector<cline> conlines;
void addline(const char *sf, bool highlight, int millis) // add a line to the console buffer
void addline(const char *sf, int millis) // add a line to the console buffer
{
cline cl;
cl.cref = conlines.length()>100 ? conlines.pop().cref : newstringbuf(""); // constrain the buffer size
cl.line = conlines.length()>100 ? conlines.pop().line : newstringbuf(""); // constrain the buffer size
cl.millis = millis; // for how long to keep line on screen
conlines.insert(0,cl);
if(highlight) // show line in a different colour, for chat etc.
{
cl.cref[0] = '\f';
cl.cref[1] = '0';
cl.cref[2] = 0;
s_strcat(cl.cref, sf);
}
else
{
s_strcpy(cl.cref, sf);
}
s_strcpy(cl.line, sf);
}
virtual ~consolebuffer() {}
+60 -37
View File
@@ -309,7 +309,7 @@ int numargs(char *args)
return argidx;
}
void renderdoc(int x, int y)
void renderdoc(int x, int y, int doch)
{
if(!docvisible) return;
@@ -328,10 +328,11 @@ void renderdoc(int x, int y)
docident *ident = docidents.access(cmd);
if(ident)
{
const int linemax = VIRTW*4/3;
cvector doclines;
vector<const char *> doclines;
char *label = doclines.add(newstringbuf(ident->name)); // label
char *label = newstringbuf(); // label
doclines.add(label);
s_sprintf(label)("~%s", ident->name);
loopvj(ident->arguments)
{
s_strcat(label, " ");
@@ -339,9 +340,7 @@ void renderdoc(int x, int y)
}
doclines.add(NULL);
cvector desc;
text_block(ident->desc, linemax, desc); // desc
loopvj(desc) doclines.add(desc[j]);
doclines.add(ident->desc);
doclines.add(NULL);
if(ident->arguments.length() > 0) // args
@@ -379,8 +378,7 @@ void renderdoc(int x, int y)
{
docargument *a = &ident->arguments[j];
if(!a) continue;
char *argstr = doclines.add(new string);
s_sprintf(argstr)("\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc,
s_sprintf(doclines.add(newstringbuf()))("~\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc,
a->values ? "(" : "", a->values ? a->values : "", a->values ? ")" : "");
}
@@ -389,35 +387,28 @@ void renderdoc(int x, int y)
if(ident->remarks.length()) // remarks
{
loopvj(ident->remarks)
{
cvector remarks;
text_block(ident->remarks[j], linemax, remarks);
loopvk(remarks) doclines.add(remarks[k]);
}
loopvj(ident->remarks) doclines.add(ident->remarks[j]);
doclines.add(NULL);
}
if(ident->examples.length()) // examples
{
doclines.add(newstring(ident->examples.length() == 1 ? "Example:" : "Examples:"));
doclines.add(ident->examples.length() == 1 ? "Example:" : "Examples:");
loopvj(ident->examples)
{
cvector lines;
text_block(ident->examples[j].code, linemax, lines);
text_block(ident->examples[j].explanation, linemax, lines);
loopvk(lines) doclines.add(lines[k]);
doclines.add(ident->examples[j].code);
doclines.add(ident->examples[j].explanation);
}
doclines.add(NULL);
}
if(ident->keys.length()) // default keys
{
doclines.add(newstring(ident->keys.length() == 1 ? "Default key:" : "Default keys:"));
doclines.add(ident->keys.length() == 1 ? "Default key:" : "Default keys:");
loopvj(ident->keys)
{
dockey &k = ident->keys[j];
s_sprintfd(line)("%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : "");
s_sprintfd(line)("~%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : "");
doclines.add(newstring(line));
}
doclines.add(NULL);
@@ -437,25 +428,57 @@ void renderdoc(int x, int y)
loopj(sizeof(categories)/sizeof(category))
{
if(!strlen(categories[j].refs)) continue;
char *line = doclines.add(newstringbuf(categories[j].label));
s_strcat(line, ": ");
s_strcat(line, categories[j].refs);
s_sprintf(doclines.add(newstringbuf()))("~%s: %s", categories[j].label, categories[j].refs);
}
}
int screenlines = (VIRTH*2/3/FONTH)-1;
if(docskip) docskip = min(docskip, doclines.length() - screenlines); // normalize
bool more = docskip < doclines.length() - screenlines;
for(int j = docskip; j < min(doclines.length(), docskip+screenlines); j++)
{
if(doclines[j]) draw_textf("%s", x, y+j*FONTH, doclines[j]);
}
doclines.deletecontentsa();
while(doclines.length() && !doclines.last()) doclines.pop();
if(more) draw_textf("\f4more (F3)", x, y+screenlines*FONTH); // footer
if(docskip > 0) draw_textf("\f4less (F2)", x, y+(screenlines+1)*FONTH);
draw_textf("\f4disable doc reference (F1)", x, y+(screenlines+2)*FONTH);
doch -= 3*FONTH + FONTH/2;
int offset = min(docskip, doclines.length()-1), maxl = offset, cury = 0;
for(int j = offset; j < doclines.length(); j++)
{
const char *str = doclines[j];
int width = 0, height = FONTH;
if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3);
if(cury + height > doch) break;
cury += height;
maxl = j+1;
}
if(offset > 0 && maxl >= doclines.length())
{
for(int j = offset-1; j >= 0; j--)
{
const char *str = doclines[j];
int width = 0, height = FONTH;
if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3);
if(cury + height > doch) break;
cury += height;
offset = j;
}
}
cury = y;
for(int j = offset; j < maxl; j++)
{
const char *str = doclines[j];
if(str)
{
const char *text = *str=='~' ? str+1 : str;
draw_text(text, x, cury, 0xFF, 0xFF, 0xFF, 0xFF, -1, str==text ? VIRTW*4/3 : -1);
int width, height;
text_bounds(text, width, height, str==text ? VIRTW*4/3 : -1);
cury += height;
if(str!=text) delete[] str;
}
else cury += FONTH;
}
if(maxl < doclines.length()) draw_text("\f4more (F3)", x, y+doch); // footer
if(offset > 0) draw_text("\f4less (F2)", x, y+doch+FONTH);
draw_text("\f4disable doc reference (F1)", x, y+doch+2*FONTH);
return;
}
}
+23
View File
@@ -137,6 +137,29 @@ static inline uint hthash(const ivec &k)
return k.x^k.y^k.z;
}
struct bvec
{
union
{
struct { uchar x, y, z; };
uchar v[3];
};
bvec() {}
bvec(uchar x, uchar y, uchar z) : x(x), y(y), z(z) {}
bvec(const vec &v) : x((uchar)((v.x+1)*255/2)), y((uchar)((v.y+1)*255/2)), z((uchar)((v.z+1)*255/2)) {}
uchar &operator[](int i) { return v[i]; }
uchar operator[](int i) const { return v[i]; }
bool operator==(const bvec &v) const { return x==v.x && y==v.y && z==v.z; }
bool operator!=(const bvec &v) const { return x!=v.x || y!=v.y || z!=v.z; }
bool iszero() const { return x==0 && y==0 && z==0; }
vec tovec() const { return vec(x*(2.0f/255.0f)-1.0f, y*(2.0f/255.0f)-1.0f, z*(2.0f/255.0f)-1.0f); }
};
struct glmatrixf
{
float v[16];
+3 -5
View File
@@ -205,9 +205,7 @@ struct mitemtextinput : mitemtext
int tw = VIRTW/4;
if(selection) renderbg(x+w-tw, y-FONTH/6, tw, NULL);
draw_text(text, x, y);
int offset = text_width(input.buf, input.pos>=0 ? input.pos : -1);
if(selection) rendercursor(x+w-tw+offset, y, char_width(input.pos>=0 ? input.buf[input.pos] : '_'));
draw_text(input.buf, x+w-tw, y);
draw_text(input.buf, x+w-tw, y, 255, 255, 255, 255, selection ? (input.pos>=0 ? input.pos : strlen(input.buf)) : -1);
}
virtual void focus(bool on)
@@ -364,11 +362,11 @@ struct mitemkeyinput : mitem
DELETEA(bindcmd);
}
virtual int width() { return text_width(text)+(keyname ? text_width(keyname) : char_width('_')); }
virtual int width() { return text_width(text)+text_width(keyname ? keyname : " "); }
virtual void render(int x, int y, int w)
{
int tk = (keyname ? text_width(keyname) : char_width('_'));
int tk = text_width(keyname ? keyname : " ");
static color capturec(0.4f, 0, 0);
if(isselection()) blendbox(x+w-tk-FONTH, y-FONTH/6, x+w+FONTH, y+FONTH+FONTH/6, false, -1, capture ? &capturec : NULL);
draw_text(text, x, y);
+7 -6
View File
@@ -69,7 +69,7 @@ extern int execcontext;
// console
extern void keypress(int code, bool isdown, int cooked, SDLMod mod = KMOD_NONE);
extern void rendercommand(int x, int y);
extern int rendercommand(int x, int y, int w);
extern void renderconsole();
extern char *getcurcommand();
extern char *addreleaseaction(const char *s);
@@ -447,12 +447,13 @@ extern int VIRTW; // virtual screen size for text & HUD
extern font *curfont;
extern bool setfont(const char *name);
extern void draw_text(const char *str, int left, int top);
extern void draw_text(const char *str, int left, int top, int r = 255, int g = 255, int b = 255, int a = 255, int cursor = -1, int maxwidth = -1);
extern void draw_textf(const char *fstr, int left, int top, ...);
extern int char_width(int c, int x = 0);
extern int text_width(const char *str, int limit = -1);
extern int text_width(const char *str);
extern int text_visible(const char *str, int max);
extern void text_block(const char *str, int max, vector<char *> &lines);
extern void text_bounds(const char *str, int &width, int &height, int maxwidth = -1);
extern int text_visible(const char *str, int hitx, int hity, int maxwidth);
extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth);
extern void text_startcolumns();
extern void text_endcolumns();
@@ -595,7 +596,7 @@ extern void checkitems(playerent *d);
extern void perlinarea(block &b, int scale, int seed, int psize);
// doc
extern void renderdoc(int x, int y);
extern void renderdoc(int x, int y, int doch);
extern void renderdocmenu(void *menu, bool init);
extern void toggledoc();
extern void scrolldoc(int i);
+7 -6
View File
@@ -262,12 +262,12 @@ void drawradarent(float x, float y, float yaw, int col, int row, float iconsize,
struct hudmessages : consolebuffer
{
void addline(const char *sf) { consolebuffer::addline(sf, false, totalmillis); }
void addline(const char *sf) { consolebuffer::addline(sf, totalmillis); }
void editlastline(const char *sf)
{
if(!conlines.length()) return;
conlines[0].millis = totalmillis;
s_strcpy(conlines[0].cref, sf);
s_strcpy(conlines[0].line, sf);
}
void render()
{
@@ -278,8 +278,8 @@ struct hudmessages : consolebuffer
loopi(min(conlines.length(), 3)) if(totalmillis-conlines[i].millis<dispmillis)
{
cline &c = conlines[i];
int tw = text_width(c.cref);
draw_text(c.cref, int(tw > VIRTW*0.8f ? 0 : (VIRTW*0.8f-tw)/2), int(((VIRTH*0.8f)/4*3)+FONTH*i+pow((totalmillis-c.millis)/(float)dispmillis, 4)*VIRTH*0.8f/4.0f));
int tw = text_width(c.line);
draw_text(c.line, int(tw > VIRTW*0.8f ? 0 : (VIRTW*0.8f-tw)/2), int(((VIRTH*0.8f)/4*3)+FONTH*i+pow((totalmillis-c.millis)/(float)dispmillis, 4)*VIRTH*0.8f/4.0f));
}
}
};
@@ -477,7 +477,8 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat
glMatrixMode(GL_PROJECTION);
char *infostr = editinfo();
if(command) rendercommand(20, 1570);
int commandh = 1570 + FONTH;
if(command) commandh -= rendercommand(20, 1570, VIRTW);
else if(infostr) draw_text(infostr, 20, 1570);
else if(targetplayer && !spectating) draw_text(colorname(targetplayer), 20, 1570);
@@ -486,7 +487,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat
if(!hideconsole) renderconsole();
if(menu) rendermenu();
else if(command) renderdoc(40, VIRTH);
else if(command) renderdoc(40, VIRTH, max(commandh*2 - VIRTH, 0));
if(!hidestats)
{
const int left = (VIRTW-225-10)*2, top = (VIRTH*7/8)*2;
+204 -156
View File
@@ -54,17 +54,62 @@ bool setfont(const char *name)
return true;
}
int char_width(int c, int x)
int text_width(const char *str)
{
if(!curfont) return x;
else if(c=='\t') x = (x+PIXELTAB)/PIXELTAB*PIXELTAB;
else if(c==' ') x += curfont->defaultw;
else if(curfont->chars.inrange(c-33))
{
c -= 33;
x += curfont->chars[c].w + 1;
int width, height;
text_bounds(str, width, height);
return width;
}
void draw_textf(const char *fstr, int left, int top, ...)
{
s_sprintfdlv(str, top, fstr);
draw_text(str, left, top);
}
static int draw_char(int c, int x, int y)
{
font::charinfo &info = curfont->chars[c-33];
float tc_left = (info.x + curfont->offsetx) / float(curfont->tex->xs);
float tc_top = (info.y + curfont->offsety) / float(curfont->tex->ys);
float tc_right = (info.x + info.w + curfont->offsetw) / float(curfont->tex->xs);
float tc_bottom = (info.y + info.h + curfont->offseth) / float(curfont->tex->ys);
glTexCoord2f(tc_left, tc_top ); glVertex2f(x, y);
glTexCoord2f(tc_right, tc_top ); glVertex2f(x + info.w, y);
glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h);
glTexCoord2f(tc_left, tc_bottom); glVertex2f(x, y + info.h);
xtraverts += 4;
return info.w;
}
//stack[sp] is current color index
static void text_color(char c, char *stack, int size, int &sp, bvec color, int a)
{
if(c=='s') // save color
{
c = stack[sp];
if(sp<size-1) stack[++sp] = c;
}
return x;
else
{
if(c=='r') c = stack[(sp > 0) ? --sp : sp]; // restore color
else stack[sp] = c;
switch(c)
{
case '0': color = bvec(64, 255, 128); break; // green: player talk
case '1': color = bvec(96, 160, 255); break; // blue: team chat
case '2': color = bvec(255, 192, 64); break; // yellow: gameplay action messages, only actions done by players
case '3': color = bvec(255, 64, 64); break; // red: important errors and notes
case '4': color = bvec(128, 128, 128); break; // gray
case '5': color = bvec(255, 255, 255); break; // white
case '6': color = bvec(96, 48, 0); break; // dark brown
case '7': color = bvec(128, 48, 48); break; // dark red: dead admin
// white (provided color): everything else
}
glColor4ub(color.x, color.y, color.z, a);
}
}
static vector<int> *columns = NULL;
@@ -79,170 +124,173 @@ void text_endcolumns()
DELETEP(columns);
}
int text_width(const char *str, int limit)
{
if(!str) return 0;
int x = 0, col = 0, colx = 0;
for(int i = 0; str[i] && (limit<0 || i<limit); i++)
{
switch(str[i])
{
case '\f':
i++;
break;
#define TABALIGN(x) ((((x)+PIXELTAB)/PIXELTAB)*PIXELTAB)
case '\t':
if(columns)
{
while(col>=columns->length()) columns->add(0);
int w = char_width('\t', x) - colx;
w = max(w, (*columns)[col]);
(*columns)[col] = w;
col++;
colx += w;
x = colx;
#define TEXTGETCOLUMN \
if(columns && col<columns->length()) \
{ \
colx += (*columns)[col++]; \
x = colx; \
} \
else x = TABALIGN(x);
#define TEXTSETCOLUMN \
if(columns) \
{ \
while(col>=columns->length()) columns->add(0); \
int w = TABALIGN(x) - colx; \
w = max(w, (*columns)[col]); \
(*columns)[col] = w; \
col++; \
colx += w; \
x = colx; \
} \
else x = TABALIGN(x);
#define TEXTSKELETON \
int y = 0, x = 0, col = 0, colx = 0;\
int i;\
for(i = 0; str[i]; i++)\
{\
TEXTINDEX(i)\
int c = str[i];\
if(c=='\t') { TEXTTAB(i); TEXTWHITE(i) }\
else if(c==' ') { x += curfont->defaultw; TEXTWHITE(i) }\
else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\
else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\
else if(curfont->chars.inrange(c-33))\
{\
if(maxwidth != -1)\
{\
int j = i;\
int w = curfont->chars[c-33].w;\
for(; str[i+1]; i++)\
{\
int c = str[i+1];\
if(c=='\f') { if(str[i+2]) i++; continue; }\
if(i-j > 16) break;\
if(!curfont->chars.inrange(c-33)) break;\
int cw = curfont->chars[c-33].w + 1;\
if(w + cw >= maxwidth) break;\
w += cw;\
}\
if(x + w >= maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\
TEXTWORD\
}\
else\
{ TEXTCHAR(i) }\
}\
}
//all the chars are guaranteed to be either drawable or color commands
#define TEXTWORDSKELETON \
for(; j <= i; j++)\
{\
TEXTINDEX(j)\
int c = str[j];\
if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\
else { TEXTCHAR(j) }\
}
else x = char_width('\t', x);
break;
default:
x = char_width(str[i], x);
break;
}
}
return x;
}
int text_visible(const char *str, int max)
int text_visible(const char *str, int hitx, int hity, int maxwidth)
{
int i = 0, x = 0;
while(str[i])
{
if(str[i]=='\f')
{
i += 2;
continue;
}
x = char_width(str[i], x);
if(x > max) return i;
++i;
}
#define TEXTINDEX(idx)
#define TEXTTAB(idx) TEXTGETCOLUMN
#define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx;
#define TEXTLINE(idx) if(y+FONTH > hity) return idx;
#define TEXTCOLOR(idx)
#define TEXTCHAR(idx) x += curfont->chars[c-33].w+1; TEXTWHITE(idx)
#define TEXTWORD TEXTWORDSKELETON
TEXTSKELETON
#undef TEXTINDEX
#undef TEXTTAB
#undef TEXTWHITE
#undef TEXTLINE
#undef TEXTCOLOR
#undef TEXTCHAR
#undef TEXTWORD
return i;
}
// cut strings to fit on screen
void text_block(const char *str, int max, vector<char *> &lines)
//inverse of text_visible
void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth)
{
if(!str) return;
int visible;
cvector colordata;
while((visible = max ? text_visible(str, max) : (int)strlen(str)))
{
const char *newline = (const char *)memchr(str, '\n', visible);
if(newline) visible = newline+1-str;
else if(str[visible]) // wrap words
{
int v = visible;
while(v > 0 && str[v] != ' ') v--;
if(v) visible = v+1;
}
size_t colorsize = colordata.length()*2;
char *t = lines.add(newstring((size_t)visible+colorsize));
char *color = t;
loopv(colordata) // add colors from previous line
{
s_strncpy(color, colordata[i], 3);
color += 2;
}
s_strncpy(color, str, visible+1);
colordata.setsize(0);
for(char *c = strchr(t, '\f'); c && c[1]; c = strchr(c+1, '\f')) colordata.add(c); // store colors of this line
str += visible;
}
#define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; }
#define TEXTTAB(idx) TEXTGETCOLUMN
#define TEXTWHITE(idx)
#define TEXTLINE(idx)
#define TEXTCOLOR(idx)
#define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1;
#define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
cx = INT_MIN;
cy = 0;
TEXTSKELETON
if(cx == INT_MIN) { cx = x; cy = y; }
#undef TEXTINDEX
#undef TEXTTAB
#undef TEXTWHITE
#undef TEXTLINE
#undef TEXTCOLOR
#undef TEXTCHAR
#undef TEXTWORD
}
void draw_textf(const char *fstr, int left, int top, ...)
void text_bounds(const char *str, int &width, int &height, int maxwidth)
{
s_sprintfdlv(str, top, fstr);
draw_text(str, left, top);
#define TEXTINDEX(idx)
#define TEXTTAB(idx) TEXTSETCOLUMN
#define TEXTWHITE(idx)
#define TEXTLINE(idx) if(x > width) width = x;
#define TEXTCOLOR(idx)
#define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1;
#define TEXTWORD x += w + 1;
width = 0;
TEXTSKELETON
height = y + FONTH;
TEXTLINE(_)
#undef TEXTINDEX
#undef TEXTTAB
#undef TEXTWHITE
#undef TEXTLINE
#undef TEXTCOLOR
#undef TEXTCHAR
#undef TEXTWORD
}
void draw_text(const char *str, int left, int top)
void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
{
if(!curfont) return;
glBlendFunc(GL_ONE, GL_ONE);
#define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; cc = str[idx]; }
#define TEXTTAB(idx) TEXTGETCOLUMN
#define TEXTWHITE(idx)
#define TEXTLINE(idx)
#define TEXTCOLOR(idx) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a);
#define TEXTCHAR(idx) x += draw_char(c, left+x, top+y)+1;
#define TEXTWORD TEXTWORDSKELETON
char colorstack[10];
bvec color(r, g, b);
int colorpos = 0, cx = INT_MIN, cy = 0, cc = ' ';
colorstack[0] = 'c'; //indicate user color
glBlendFunc(GL_SRC_ALPHA, curfont->tex->bpp==32 ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE);
glBindTexture(GL_TEXTURE_2D, curfont->tex->id);
static float colorstack[8][4];
int colorpos = 0, x = left, y = top, col = 0, colx = 0;
glBegin(GL_QUADS);
// ATI bug -- initial color must be set after glBegin
glColor3ub(255, 255, 255);
for(int i = 0; str[i]; i++)
{
int c = str[i];
switch(c)
{
case '\t':
if(columns && col<columns->length())
{
colx += (*columns)[col++];
x = left + colx;
}
else x = (x-left+PIXELTAB)/PIXELTAB*PIXELTAB+left;
continue;
case '\f':
switch(str[++i])
{
case '0': glColor3ub(64, 255, 128); continue; // green: player talk
case '1': glColor3ub(96, 160, 255); continue; // blue: team chat
case '2': glColor3ub(255, 192, 64); continue; // yellow: gameplay action messages, only actions done by players
case '3': glColor3ub(255, 64, 64); continue; // red: important errors and notes
case '4': glColor3ub(128, 128, 128); continue; // gray
case '5': glColor3ub(255, 255, 255); continue; // white: everything else
case '6': glColor3ub(96, 48, 0); continue; // dark brown
case '7': glColor3ub(128, 48, 48); continue; // dark red: dead admin
case 's': // save color
if((size_t)colorpos<sizeof(colorstack)/sizeof(colorstack[0]))
{
glEnd();
glGetFloatv(GL_CURRENT_COLOR, colorstack[colorpos++]);
glBegin(GL_QUADS);
}
continue;
case 'r': // restore color
if(colorpos>0)
glColor4fv(colorstack[--colorpos]);
continue;
default: continue;
}
case ' ':
x += curfont->defaultw;
continue;
}
c -= 33;
if(!curfont->chars.inrange(c)) continue;
font::charinfo &info = curfont->chars[c];
float tc_left = (info.x + curfont->offsetx) / float(curfont->tex->xs);
float tc_top = (info.y + curfont->offsety) / float(curfont->tex->ys);
float tc_right = (info.x + info.w + curfont->offsetw) / float(curfont->tex->xs);
float tc_bottom = (info.y + info.h + curfont->offseth) / float(curfont->tex->ys);
glTexCoord2f(tc_left, tc_top ); glVertex2f(x, y);
glTexCoord2f(tc_right, tc_top ); glVertex2f(x + info.w, y);
glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h);
glTexCoord2f(tc_left, tc_bottom); glVertex2f(x, y + info.h);
xtraverts += 4;
x += info.w + 1;
}
glColor4ub(color.x, color.y, color.z, a);
TEXTSKELETON
glEnd();
if(cursor >= 0)
{
if(cx == INT_MIN) { cx = x; cy = y; }
if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
int cw = curfont->chars.inrange(cc-33) ? curfont->chars[cc-33].w + 1 : curfont->defaultw;
rendercursor(left+cx, top+cy, cw);
}
#undef TEXTINDEX
#undef TEXTTAB
#undef TEXTWHITE
#undef TEXTLINE
#undef TEXTCOLOR
#undef TEXTCHAR
#undef TEXTWORD
}
void reloadfonts()
+1
View File
@@ -508,6 +508,7 @@ template <class K, class T> struct hashtable
inline char *newstring(size_t l) { return new char[l+1]; }
inline char *newstring(const char *s, size_t l) { return s_strncpy(newstring(l), s, l+1); }
inline char *newstring(const char *s) { return newstring(s, strlen(s)); }
inline char *newstringbuf() { return newstring(_MAXDEFSTR-1); }
inline char *newstringbuf(const char *s) { return newstring(s, _MAXDEFSTR-1); }
extern char *path(char *s);