389 lines
9.3 KiB
C
389 lines
9.3 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2007 HermitWorks Entertainment Corporation
|
|
|
|
This file is part of the Space Trader source code.
|
|
|
|
The Space Trader source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
The Space Trader source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with the Space Trader source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "client.h"
|
|
|
|
#define CPU_TEXT_SHADOW
|
|
|
|
typedef struct
|
|
{
|
|
float x; // horiz position
|
|
float y; // vert position
|
|
float w; // width
|
|
float h; // height;
|
|
|
|
} rectDef_t;
|
|
|
|
|
|
void CL_RenderText_Line( lineInfo_t * line, fontInfo_t * font, float x, float y, float fontScale,
|
|
float letterScaleX, float letterScaleY, int smallcaps, int cursorLoc, int cursorChar, qboolean colors )
|
|
{
|
|
re.SetColor( line->startColor );
|
|
re.TextDrawLine( line, font, 0.0f, x, y, fontScale, letterScaleX, letterScaleY, smallcaps, cursorLoc, cursorChar, colors );
|
|
}
|
|
|
|
void CL_RenderText_FindBounds( const lineInfo_t * lines, int count, float * left, float * right )
|
|
{
|
|
if ( count > 0 )
|
|
{
|
|
float l = lines[ 0 ].ox;
|
|
float r = lines[ 0 ].ox + lines[ 0 ].width;
|
|
|
|
int i;
|
|
for ( i=1; i<count; i++ )
|
|
{
|
|
if ( lines[i].ox < l )
|
|
l = lines[i].ox;
|
|
|
|
if ( lines[i].ox + lines[i].width > r )
|
|
r = lines[i].ox + lines[i].width;
|
|
}
|
|
|
|
*left = l;
|
|
*right = r;
|
|
|
|
} else
|
|
{
|
|
*left = 0.0f;
|
|
*right = 0.0f;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
int CL_RenderText_ExtractLines( fontInfo_t * font, float fontScale, float startColor[4], float baseColor[4], const char * text,
|
|
int limit, int align, float width, lineInfo_t * lines, int line_offset, int line_limit )
|
|
{
|
|
lineInfo_t dummyLine;
|
|
|
|
int lineCount = 0;
|
|
const char* s = text;
|
|
float rgba[4];
|
|
qboolean clipToWidth;
|
|
|
|
Vec4_Cpy( rgba, startColor );
|
|
|
|
if( align & TEXT_ALIGN_NOCLIP )
|
|
{
|
|
clipToWidth = qfalse;
|
|
align &= ~TEXT_ALIGN_NOCLIP;
|
|
}
|
|
else
|
|
clipToWidth = qtrue;
|
|
|
|
for( ; ; )
|
|
{
|
|
int spaceCount = 0;
|
|
int lastSpace = 0;
|
|
int widthAtLastSpace = 0;
|
|
int count = 0;
|
|
lineInfo_t* line = lines ? lines + lineCount : &dummyLine;
|
|
|
|
line->text = s;
|
|
line->sa = 0.0f;
|
|
line->ox = 0.0f;
|
|
line->width = 0.0f;
|
|
line->height = 0.0f;
|
|
|
|
Vec4_Cpy( line->defaultColor, baseColor );
|
|
|
|
Vec4_Cpy( line->startColor, rgba );
|
|
Vec4_Cpy( line->endColor, rgba );
|
|
|
|
if( lines && lineCount > 0 )
|
|
Vec4_Cpy( lines[lineCount - 1].endColor, rgba );
|
|
|
|
for( ; ; )
|
|
{
|
|
int c = s[ count ];
|
|
glyphInfo_t* g = font->glyphs + c;
|
|
|
|
// line has ended
|
|
if ( c == '\n' || c == '\0' )
|
|
break;
|
|
|
|
if ( Q_IsColorString( s + count) )
|
|
{
|
|
count++;
|
|
|
|
if( s[count] == '-' )
|
|
Vec4_Cpy( rgba, baseColor );
|
|
else
|
|
Vec4_Cpy( rgba, g_color_table[ColorIndex( s[count] )] );
|
|
|
|
count++;
|
|
|
|
continue;
|
|
}
|
|
|
|
// record white space
|
|
if ( c == ' ' )
|
|
{
|
|
lastSpace = count;
|
|
widthAtLastSpace = line->width;
|
|
spaceCount++;
|
|
}
|
|
|
|
// line is too long
|
|
if( clipToWidth && (line->width + (g->xSkip*fontScale) > width) )
|
|
{
|
|
if ( limit == -2 )
|
|
break;
|
|
|
|
if ( spaceCount > 0 )
|
|
{
|
|
count = lastSpace;
|
|
line->width = widthAtLastSpace;
|
|
spaceCount--;
|
|
|
|
if ( align == TEXT_ALIGN_JUSTIFY )
|
|
line->sa = (width-line->width) / (float)spaceCount;
|
|
}
|
|
|
|
if ( count == 0 ) // width is less than 1 character?
|
|
{
|
|
line->width = g->xSkip*fontScale;
|
|
count = 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// record height
|
|
if ( g->height > line->height )
|
|
line->height = g->height;
|
|
|
|
// move along
|
|
if ( c == '\t')
|
|
{
|
|
} else
|
|
line->width += ((float)g->xSkip*fontScale);
|
|
|
|
count++;
|
|
|
|
if ( limit>0 && count >= limit )
|
|
break;
|
|
}
|
|
|
|
line->count = count;
|
|
|
|
if ( align == TEXT_ALIGN_RIGHT )
|
|
{
|
|
line->ox = width-line->width;
|
|
|
|
} else if ( align == TEXT_ALIGN_CENTER )
|
|
{
|
|
line->ox = (width-line->width)/2;
|
|
}
|
|
|
|
if ( line_offset > 0 ) {
|
|
line_offset--;
|
|
} else
|
|
lineCount++;
|
|
|
|
if( line_limit > 0 && lineCount >= line_limit )
|
|
break;
|
|
|
|
s += count;
|
|
|
|
if ( limit == -2 )
|
|
break;
|
|
|
|
if ( *s == '\0' )
|
|
break;
|
|
|
|
if ( limit>0 )
|
|
{
|
|
if ( count >= limit )
|
|
break;
|
|
|
|
limit -= count;
|
|
}
|
|
|
|
if ( *s == '\n' )
|
|
s++;
|
|
|
|
if ( *s == ' ' )
|
|
s++;
|
|
}
|
|
|
|
return lineCount;
|
|
}
|
|
|
|
|
|
#define ITEM_TEXTSTYLE_SHADOWED 2 // drop shadow ( need a color for this )
|
|
#define ITEM_TEXTSTYLE_OUTLINED 4 // drop shadow ( need a color for this )
|
|
#define ITEM_TEXTSTYLE_BLINK 8 // fast blinking
|
|
#define ITEM_TEXTSTYLE_ITALIC 16
|
|
#define ITEM_TEXTSTYLE_MULTILINE 32
|
|
#define ITEM_TEXTSTYLE_SMALLCAPS 64
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// R e n d e r T e x t
|
|
//-----------------------------------------------------------------------------
|
|
#define MAX_LINES 128
|
|
void CL_RenderText( float* _rect,
|
|
float scale,
|
|
vec4_t color,
|
|
const char* text,
|
|
int limit,
|
|
int horiz,
|
|
int vert,
|
|
int style,
|
|
int cursor,
|
|
int cursorChar,
|
|
qhandle_t fontSet,
|
|
float* _textRect
|
|
)
|
|
{
|
|
lineInfo_t lines[ MAX_LINES ];
|
|
int lineCount;
|
|
int i, totalChars;
|
|
rectDef_t* rect = (rectDef_t*)_rect;
|
|
rectDef_t* textRect = (rectDef_t*)_textRect;
|
|
fontInfo_t* font = re.GetFontFromFontSet( fontSet, scale );
|
|
float fontScale = scale * font->glyphScale;
|
|
float oy = 0.0f;
|
|
float h;
|
|
float lineHeight = font->glyphs[ 'A' ].height * fontScale;
|
|
float lineSpace;
|
|
float italic;
|
|
int line_offset = 0;
|
|
int line_limit = lengthof( lines );
|
|
|
|
if( (unsigned int)text < 0x1000 )
|
|
text = "<null>";
|
|
else if( !text[0] && (cursor < 0) )
|
|
return;
|
|
|
|
cursor = ((int)(cls.realtime >> 8) & 1) ? -1 : cursor;
|
|
|
|
if ( style & ITEM_TEXTSTYLE_MULTILINE ) {
|
|
line_offset = horiz;
|
|
line_limit = vert;
|
|
horiz = TEXT_ALIGN_LEFT;
|
|
vert = TEXT_ALIGN_LEFT;
|
|
}
|
|
|
|
//
|
|
// extract lines
|
|
//
|
|
lineCount = CL_RenderText_ExtractLines( font, fontScale, color, color, text, limit, horiz, rect->w, lines, line_offset, line_limit );
|
|
|
|
totalChars = 0;
|
|
//lineHeight = 0.0f;
|
|
for( i = 0; i < lineCount; i++ )
|
|
{
|
|
totalChars += lines[i].count;
|
|
//lineHeight = max( lineHeight, lines[i].height * fontScale );
|
|
}
|
|
if( cursor >= 0 )
|
|
{
|
|
//if ( totalChars == 0 )
|
|
// lineHeight = font->glyphs[ 'A' ].height * fontScale;
|
|
totalChars++;
|
|
}
|
|
|
|
lineSpace = lineHeight * 0.4f;
|
|
|
|
h = lineCount * lineHeight + ((lineCount-1)*lineSpace);
|
|
|
|
#ifdef CPU_TEXT_SHADOW
|
|
if( style & ITEM_TEXTSTYLE_SHADOWED )
|
|
totalChars *= 2; //account for shadow
|
|
#endif
|
|
|
|
//
|
|
// vertically justify text
|
|
//
|
|
switch ( vert )
|
|
{
|
|
case TEXT_ALIGN_CENTER: oy = (rect->h - h)*0.5f; break; // middle
|
|
case TEXT_ALIGN_RIGHT: oy = (rect->h - h); break; // bottom
|
|
case TEXT_ALIGN_JUSTIFY: lineHeight += (rect->h - h) / (float)lineCount; break;
|
|
}
|
|
|
|
//
|
|
// compute bounds
|
|
//
|
|
if ( textRect || style & ITEM_TEXTSTYLE_OUTLINED )
|
|
{
|
|
float left, right;
|
|
CL_RenderText_FindBounds( lines, lineCount, &left, &right );
|
|
|
|
if ( textRect )
|
|
{
|
|
textRect->x = rect->x + left;
|
|
textRect->y = rect->y + oy;
|
|
textRect->w = right - left;
|
|
textRect->h = h;
|
|
}
|
|
|
|
if ( style & ITEM_TEXTSTYLE_OUTLINED )
|
|
{
|
|
float b = lineHeight * 0.5f;
|
|
SCR_FillRect( rect->x + left - b*0.5f, rect->y + oy - b*0.5f, right - left + b, h + b, 0, cls.menu );
|
|
}
|
|
}
|
|
|
|
if ( style == -1 )
|
|
return; // style null, just getting text rect
|
|
|
|
italic = (style&ITEM_TEXTSTYLE_ITALIC)?4.0f:0.0f;
|
|
|
|
re.TextBeginBlock( totalChars );
|
|
|
|
//
|
|
// Render Text Shadow
|
|
//
|
|
#ifdef CPU_TEXT_SHADOW
|
|
if ( style & ITEM_TEXTSTYLE_SHADOWED )
|
|
{
|
|
const float bx = font->glyphs[ 'A' ].height * fontScale * (1.0f/6.0f);
|
|
const float by = font->glyphs[ 'A' ].height * fontScale * (1.0f/8.0f);
|
|
vec4_t shadowColor = { 0.0f, 0.0f, 0.0f, 0.5f };
|
|
shadowColor[ 3 ] *= color[ 3 ];
|
|
re.SetColor( shadowColor );
|
|
|
|
for ( i=0; i<lineCount; i++ )
|
|
{
|
|
float y = rect->y + oy + (lineHeight * (float)(i+1)) + (lineSpace * (float)i);
|
|
re.TextDrawLine( lines + i, font, italic, rect->x+bx, y+by, fontScale, 1.1f, 1.15f, style&ITEM_TEXTSTYLE_SMALLCAPS, -1, 0, qfalse );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Render Normal Text
|
|
//
|
|
|
|
for( i=0; i<lineCount; i++ )
|
|
{
|
|
float y = rect->y + oy + (lineHeight * (i+1)) + (lineSpace * i);
|
|
|
|
re.SetColor( lines[i].startColor );
|
|
re.TextDrawLine( lines + i, font, italic, rect->x, y, fontScale, 1.0f, 1.0f, style&ITEM_TEXTSTYLE_SMALLCAPS, cursor - (lines[i].text-text), cursorChar, qtrue );
|
|
}
|
|
|
|
re.TextEndBlock();
|
|
}
|