2014-10-14 13:11:37 +00:00
/*
* This file is part of the Colobot : Gold Edition source code
2021-09-11 13:52:34 +00:00
* Copyright ( C ) 2001 - 2021 , Daniel Roux , EPSITEC SA & TerranovaTeam
2015-08-22 14:40:02 +00:00
* http : //epsitec.ch; http://colobot.info; http://github.com/colobot
2014-10-14 13:11:37 +00:00
*
* This program 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 3 of the License , or
* ( at your option ) any later version .
*
* This program 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 this program . If not , see http : //gnu.org/licenses
*/
2012-06-22 14:31:55 +00:00
2012-07-26 20:26:19 +00:00
# include "graphics/engine/text.h"
2012-06-22 14:31:55 +00:00
2012-08-03 21:23:13 +00:00
# include "app/app.h"
2013-02-16 21:37:43 +00:00
2018-07-24 22:44:06 +00:00
# include "common/font_loader.h"
2012-08-03 21:23:13 +00:00
# include "common/image.h"
# include "common/logger.h"
# include "common/stringutils.h"
2015-08-02 11:09:48 +00:00
2014-06-20 21:41:38 +00:00
# include "common/resources/resourcemanager.h"
2013-02-16 21:37:43 +00:00
2021-06-16 00:33:15 +00:00
# include "graphics/core/renderers.h"
2015-10-03 20:05:14 +00:00
# include "graphics/engine/engine.h"
2012-08-03 21:23:13 +00:00
# include "math/func.h"
2012-06-22 14:31:55 +00:00
2019-01-02 00:18:45 +00:00
# include <algorithm>
2013-03-22 17:19:53 +00:00
# include <SDL.h>
# include <SDL_ttf.h>
2012-08-03 21:23:13 +00:00
2012-09-19 21:50:28 +00:00
// Graphics module namespace
2015-08-02 09:40:47 +00:00
namespace Gfx
{
2012-09-19 21:50:28 +00:00
2015-08-12 19:07:16 +00:00
/**
* \ struct MultisizeFont
* \ brief Font with multiple possible sizes
*/
struct MultisizeFont
{
std : : string fileName ;
std : : map < int , std : : unique_ptr < CachedFont > > fonts ;
explicit MultisizeFont ( const std : : string & fn )
: fileName ( fn ) { }
} ;
2016-03-18 01:01:06 +00:00
/**
* \ struct FontTexture
* \ brief Single texture filled with character textures
*/
struct FontTexture
{
unsigned int id = 0 ;
Math : : IntPoint tileSize ;
int freeSlots = 0 ;
} ;
2012-08-03 21:23:13 +00:00
/**
2012-09-19 21:50:28 +00:00
* \ struct CachedFont
* \ brief Base TTF font with UTF - 8 char cache
*/
2012-08-03 21:23:13 +00:00
struct CachedFont
{
2016-02-11 15:12:16 +00:00
std : : unique_ptr < CSDLMemoryWrapper > fontFile ;
2015-08-12 19:07:16 +00:00
TTF_Font * font = nullptr ;
2012-09-19 21:50:28 +00:00
std : : map < UTF8Char , CharTexture > cache ;
2012-08-03 21:23:13 +00:00
2016-02-11 15:12:16 +00:00
CachedFont ( std : : unique_ptr < CSDLMemoryWrapper > fontFile , int pointSize )
2015-08-12 19:07:16 +00:00
: fontFile ( std : : move ( fontFile ) )
{
font = TTF_OpenFontRW ( this - > fontFile - > GetHandler ( ) , 0 , pointSize ) ;
}
~ CachedFont ( )
{
if ( font ! = nullptr )
TTF_CloseFont ( font ) ;
}
2012-08-03 21:23:13 +00:00
} ;
2015-08-12 19:07:16 +00:00
namespace
{
2013-01-03 23:29:19 +00:00
const Math : : IntPoint REFERENCE_SIZE ( 800 , 600 ) ;
2016-03-18 01:01:06 +00:00
const Math : : IntPoint FONT_TEXTURE_SIZE ( 256 , 256 ) ;
2015-08-12 19:07:16 +00:00
} // anonymous namespace
2012-08-03 21:23:13 +00:00
2018-04-27 10:16:45 +00:00
/// The QuadBatch is responsible for collecting as many quad (aka rectangle) draws as possible and
/// sending them to the CDevice in one big batch. This avoids making one CDevice::DrawPrimitive call
/// for every CText::DrawCharAndAdjustPos call, which makes text rendering much faster.
/// Currently we only collect textured quads (ie. ones using Vertex), not untextured quads (which
/// use VertexCol). Untextured quads are only drawn via DrawHighlight, which happens much less often
/// than drawing textured quads.
class CText : : CQuadBatch
{
public :
explicit CQuadBatch ( CEngine & engine )
: m_engine ( engine )
{
m_quads . reserve ( 1024 ) ;
}
/// Add a quad to be rendered.
/// This may trigger a call to Flush() if necessary.
2021-06-16 00:33:15 +00:00
void Add ( Vertex2D vertices [ 4 ] , unsigned int texID , EngineRenderState renderState , Color color )
2018-04-27 10:16:45 +00:00
{
if ( texID ! = m_texID | | renderState ! = m_renderState | | color ! = m_color )
{
Flush ( ) ;
m_texID = texID ;
m_renderState = renderState ;
m_color = color ;
}
m_quads . emplace_back ( Quad { { vertices [ 0 ] , vertices [ 1 ] , vertices [ 2 ] , vertices [ 3 ] } } ) ;
}
/// Draw all pending quads immediately.
void Flush ( )
{
if ( m_quads . empty ( ) ) return ;
m_engine . SetState ( m_renderState ) ;
m_engine . GetDevice ( ) - > SetTexture ( 0 , m_texID ) ;
2021-06-16 00:33:15 +00:00
m_engine . SetUITexture ( Texture { m_texID } ) ;
2018-04-27 10:16:45 +00:00
assert ( m_firsts . size ( ) = = m_counts . size ( ) ) ;
if ( m_firsts . size ( ) < m_quads . size ( ) )
{
// m_firsts needs to look like { 0, 4, 8, 12, ... }
// m_counts needs to look like { 4, 4, 4, 4, ... }
// and both need to be the same length as m_quads
m_counts . resize ( m_quads . size ( ) , 4 ) ;
std : : size_t begin = m_firsts . size ( ) ;
m_firsts . resize ( m_quads . size ( ) ) ;
for ( std : : size_t i = begin ; i < m_firsts . size ( ) ; + + i )
{
m_firsts [ i ] = static_cast < int > ( 4 * i ) ;
}
}
2021-06-16 00:33:15 +00:00
for ( const auto & quad : m_quads )
{
2021-12-05 11:26:34 +00:00
m_engine . GetDevice ( ) - > DrawPrimitive ( PrimitiveType : : TRIANGLE_STRIP , quad . vertices , 4 ) ;
2021-06-16 00:33:15 +00:00
}
m_engine . GetDevice ( ) - > GetUIRenderer ( ) - > Flush ( ) ;
//const Vertex* vertices = m_quads.front().vertices;
2021-12-05 11:26:34 +00:00
//m_engine.GetDevice()->DrawPrimitives(PrimitiveType::TRIANGLE_STRIP, vertices, m_firsts.data(),
2021-06-16 00:33:15 +00:00
// m_counts.data(), static_cast<int>(m_quads.size()), m_color);
2018-04-27 10:16:45 +00:00
m_engine . AddStatisticTriangle ( static_cast < int > ( m_quads . size ( ) * 2 ) ) ;
m_quads . clear ( ) ;
}
private :
CEngine & m_engine ;
2021-06-16 00:33:15 +00:00
struct Quad { Vertex2D vertices [ 4 ] ; } ;
2018-04-27 10:16:45 +00:00
std : : vector < Quad > m_quads ;
std : : vector < int > m_firsts ;
std : : vector < int > m_counts ;
Color m_color ;
unsigned int m_texID { } ;
EngineRenderState m_renderState { } ;
} ;
2012-09-19 21:50:28 +00:00
2013-02-16 21:37:43 +00:00
CText : : CText ( CEngine * engine )
2012-08-03 21:23:13 +00:00
{
m_device = nullptr ;
m_engine = engine ;
m_defaultSize = 12.0f ;
2013-05-12 16:38:01 +00:00
m_tabSize = 4 ;
2012-08-03 21:23:13 +00:00
2017-10-11 15:10:04 +00:00
m_lastFontType = FONT_COMMON ;
2012-08-03 21:23:13 +00:00
m_lastFontSize = 0 ;
m_lastCachedFont = nullptr ;
2018-04-27 10:16:45 +00:00
m_quadBatch = MakeUnique < CQuadBatch > ( * engine ) ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
CText : : ~ CText ( )
2012-08-03 21:23:13 +00:00
{
m_device = nullptr ;
m_engine = nullptr ;
}
2012-09-19 21:50:28 +00:00
bool CText : : Create ( )
2012-08-03 21:23:13 +00:00
{
if ( TTF_Init ( ) ! = 0 )
{
m_error = std : : string ( " TTF_Init error: " ) + std : : string ( TTF_GetError ( ) ) ;
return false ;
}
2020-07-19 14:07:27 +00:00
if ( ! ReloadFonts ( ) )
{
return false ;
}
return true ;
}
bool CText : : ReloadFonts ( )
{
CFontLoader fontLoader ;
if ( ! fontLoader . Init ( ) )
{
2020-08-21 16:21:08 +00:00
GetLogger ( ) - > Debug ( " Error on parsing fonts config file: failed to open file \n " ) ;
2020-07-19 14:07:27 +00:00
}
// Backup previous fonts
auto fonts = std : : move ( m_fonts ) ;
m_fonts . clear ( ) ;
2017-10-28 20:22:22 +00:00
for ( auto type : { FONT_COMMON , FONT_STUDIO , FONT_SATCOM } )
{
2018-07-24 22:44:06 +00:00
m_fonts [ static_cast < Gfx : : FontType > ( type ) ] = MakeUnique < MultisizeFont > ( fontLoader . GetFont ( type ) ) ;
m_fonts [ static_cast < Gfx : : FontType > ( type | FONT_BOLD ) ] = MakeUnique < MultisizeFont > ( fontLoader . GetFont ( static_cast < Gfx : : FontType > ( type | FONT_BOLD ) ) ) ;
m_fonts [ static_cast < Gfx : : FontType > ( type | FONT_ITALIC ) ] = MakeUnique < MultisizeFont > ( fontLoader . GetFont ( static_cast < Gfx : : FontType > ( type | FONT_ITALIC ) ) ) ;
2017-10-28 20:22:22 +00:00
}
2012-08-03 21:23:13 +00:00
for ( auto it = m_fonts . begin ( ) ; it ! = m_fonts . end ( ) ; + + it )
{
2012-09-19 21:50:28 +00:00
FontType type = ( * it ) . first ;
2012-08-03 21:23:13 +00:00
CachedFont * cf = GetOrOpenFont ( type , m_defaultSize ) ;
if ( cf = = nullptr | | cf - > font = = nullptr )
2020-07-19 14:07:27 +00:00
{
m_fonts = std : : move ( fonts ) ;
2012-08-03 21:23:13 +00:00
return false ;
2020-07-19 14:07:27 +00:00
}
2012-08-03 21:23:13 +00:00
}
return true ;
}
2012-09-19 21:50:28 +00:00
void CText : : Destroy ( )
2012-08-03 21:23:13 +00:00
{
m_fonts . clear ( ) ;
m_lastCachedFont = nullptr ;
2017-10-11 15:10:04 +00:00
m_lastFontType = FONT_COMMON ;
2015-08-12 19:07:16 +00:00
m_lastFontSize = 0 ;
2012-08-03 21:23:13 +00:00
TTF_Quit ( ) ;
}
2012-09-19 21:50:28 +00:00
void CText : : SetDevice ( CDevice * device )
2012-08-03 21:23:13 +00:00
{
m_device = device ;
}
2012-09-19 21:50:28 +00:00
std : : string CText : : GetError ( )
2012-08-03 21:23:13 +00:00
{
return m_error ;
}
2012-09-19 21:50:28 +00:00
void CText : : FlushCache ( )
2012-08-03 21:23:13 +00:00
{
2016-03-18 01:01:06 +00:00
for ( auto & fontTexture : m_fontTextures )
{
Texture tex ;
tex . id = fontTexture . id ;
m_device - > DestroyTexture ( tex ) ;
}
m_fontTextures . clear ( ) ;
2015-08-12 19:07:16 +00:00
for ( auto & multisizeFont : m_fonts )
2012-08-03 21:23:13 +00:00
{
2015-08-12 19:07:16 +00:00
for ( auto & cachedFont : multisizeFont . second - > fonts )
2012-08-03 21:23:13 +00:00
{
2015-08-12 19:07:16 +00:00
cachedFont . second - > cache . clear ( ) ;
2012-08-03 21:23:13 +00:00
}
}
2013-01-03 23:29:19 +00:00
2020-07-19 14:07:27 +00:00
m_lastCachedFont = nullptr ;
m_lastFontType = FONT_COMMON ;
m_lastFontSize = 0 ;
2012-08-03 21:23:13 +00:00
}
2013-05-12 16:38:01 +00:00
int CText : : GetTabSize ( )
{
return m_tabSize ;
}
void CText : : SetTabSize ( int tabSize )
{
m_tabSize = tabSize ;
}
2013-02-09 22:49:38 +00:00
void CText : : DrawText ( const std : : string & text , std : : vector < FontMetaChar > : : iterator format ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator end ,
2012-09-26 14:31:04 +00:00
float size , Math : : Point pos , float width , TextAlign align ,
2012-09-30 08:56:35 +00:00
int eol , Color color )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
float sw = 0.0f ;
2012-09-19 21:50:28 +00:00
if ( align = = TEXT_ALIGN_CENTER )
2012-08-06 18:20:50 +00:00
{
2013-02-24 00:40:55 +00:00
sw = GetStringWidth ( text , format , end , size ) ;
2012-08-06 18:20:50 +00:00
if ( sw > width ) sw = width ;
pos . x - = sw / 2.0f ;
}
2012-09-19 21:50:28 +00:00
else if ( align = = TEXT_ALIGN_RIGHT )
2012-08-06 18:20:50 +00:00
{
2013-02-24 00:40:55 +00:00
sw = GetStringWidth ( text , format , end , size ) ;
2012-08-06 18:20:50 +00:00
if ( sw > width ) sw = width ;
pos . x - = sw ;
}
2016-04-06 10:21:41 +00:00
Math : : IntPoint intPos = m_engine - > InterfaceToWindowCoords ( pos ) ;
int intWidth = width * m_engine - > GetWindowSize ( ) . x ;
DrawString ( text , format , end , size , intPos , intWidth , eol , color ) ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
void CText : : DrawText ( const std : : string & text , FontType font ,
2012-09-26 14:31:04 +00:00
float size , Math : : Point pos , float width , TextAlign align ,
2012-09-30 08:56:35 +00:00
int eol , Color color )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
float sw = 0.0f ;
2012-09-19 21:50:28 +00:00
if ( align = = TEXT_ALIGN_CENTER )
2012-08-06 18:20:50 +00:00
{
sw = GetStringWidth ( text , font , size ) ;
if ( sw > width ) sw = width ;
pos . x - = sw / 2.0f ;
}
2012-09-19 21:50:28 +00:00
else if ( align = = TEXT_ALIGN_RIGHT )
2012-08-06 18:20:50 +00:00
{
sw = GetStringWidth ( text , font , size ) ;
if ( sw > width ) sw = width ;
pos . x - = sw ;
}
2016-04-06 10:21:41 +00:00
Math : : IntPoint intPos = m_engine - > InterfaceToWindowCoords ( pos ) ;
int intWidth = width * m_engine - > GetWindowSize ( ) . x ;
DrawString ( text , font , size , intPos , intWidth , eol , color ) ;
2012-08-03 21:23:13 +00:00
}
2013-02-09 22:49:38 +00:00
void CText : : SizeText ( const std : : string & text , std : : vector < FontMetaChar > : : iterator format ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator endFormat ,
2012-09-26 14:31:04 +00:00
float size , Math : : Point pos , TextAlign align ,
Math : : Point & start , Math : : Point & end )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
start = end = pos ;
2013-02-24 00:40:55 +00:00
float sw = GetStringWidth ( text , format , endFormat , size ) ;
2012-08-06 18:20:50 +00:00
end . x + = sw ;
2012-09-19 21:50:28 +00:00
if ( align = = TEXT_ALIGN_CENTER )
2012-08-06 18:20:50 +00:00
{
start . x - = sw / 2.0f ;
end . x - = sw / 2.0f ;
}
2012-09-19 21:50:28 +00:00
else if ( align = = TEXT_ALIGN_RIGHT )
2012-08-06 18:20:50 +00:00
{
start . x - = sw ;
end . x - = sw ;
}
2017-10-11 15:10:04 +00:00
start . y - = GetDescent ( FONT_COMMON , size ) ;
end . y + = GetAscent ( FONT_COMMON , size ) ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
void CText : : SizeText ( const std : : string & text , FontType font ,
2012-09-26 14:31:04 +00:00
float size , Math : : Point pos , TextAlign align ,
Math : : Point & start , Math : : Point & end )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
start = end = pos ;
float sw = GetStringWidth ( text , font , size ) ;
end . x + = sw ;
2012-09-19 21:50:28 +00:00
if ( align = = TEXT_ALIGN_CENTER )
2012-08-06 18:20:50 +00:00
{
start . x - = sw / 2.0f ;
end . x - = sw / 2.0f ;
}
2012-09-19 21:50:28 +00:00
else if ( align = = TEXT_ALIGN_RIGHT )
2012-08-06 18:20:50 +00:00
{
start . x - = sw ;
end . x - = sw ;
}
start . y - = GetDescent ( font , size ) ;
end . y + = GetAscent ( font , size ) ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
float CText : : GetAscent ( FontType font , float size )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
CachedFont * cf = GetOrOpenFont ( font , size ) ;
2012-08-06 18:20:50 +00:00
assert ( cf ! = nullptr ) ;
2012-08-11 15:17:04 +00:00
Math : : IntPoint wndSize ;
wndSize . y = TTF_FontAscent ( cf - > font ) ;
Math : : Point ifSize = m_engine - > WindowToInterfaceSize ( wndSize ) ;
return ifSize . y ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
float CText : : GetDescent ( FontType font , float size )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
CachedFont * cf = GetOrOpenFont ( font , size ) ;
2012-08-06 18:20:50 +00:00
assert ( cf ! = nullptr ) ;
2012-08-11 15:17:04 +00:00
Math : : IntPoint wndSize ;
wndSize . y = TTF_FontDescent ( cf - > font ) ;
Math : : Point ifSize = m_engine - > WindowToInterfaceSize ( wndSize ) ;
return ifSize . y ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
float CText : : GetHeight ( FontType font , float size )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
CachedFont * cf = GetOrOpenFont ( font , size ) ;
2012-08-06 18:20:50 +00:00
assert ( cf ! = nullptr ) ;
2012-08-11 15:17:04 +00:00
Math : : IntPoint wndSize ;
wndSize . y = TTF_FontHeight ( cf - > font ) ;
Math : : Point ifSize = m_engine - > WindowToInterfaceSize ( wndSize ) ;
return ifSize . y ;
2012-08-03 21:23:13 +00:00
}
2016-04-06 10:21:41 +00:00
int CText : : GetHeightInt ( FontType font , float size )
{
assert ( font ! = FONT_BUTTON ) ;
CachedFont * cf = GetOrOpenFont ( font , size ) ;
assert ( cf ! = nullptr ) ;
return TTF_FontHeight ( cf - > font ) ;
}
2012-08-03 21:23:13 +00:00
2012-09-19 21:50:28 +00:00
float CText : : GetStringWidth ( const std : : string & text ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator format ,
std : : vector < FontMetaChar > : : iterator end , float size )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
float width = 0.0f ;
unsigned int index = 0 ;
unsigned int fmtIndex = 0 ;
while ( index < text . length ( ) )
{
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2013-02-24 00:40:55 +00:00
if ( format + fmtIndex ! = end )
font = static_cast < FontType > ( * ( format + fmtIndex ) & FONT_MASK_FONT ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
UTF8Char ch ;
2012-08-06 18:20:50 +00:00
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2012-08-06 18:20:50 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
width + = GetCharWidth ( ch , font , size , width ) ;
index + = len ;
Fix crashes in SatCom on some \button characters
The issue is that the \button fragments in SatCom are replaced with
invalid UTF-8 bytes, hence those bytes need to be handled as a special
case when checking for UTF-8 character length.
When the text is processed e.g. in `Gfx::CText::Justify`, there are
generally two arrays: `text` and `format`. The second one is interpreted
as a font for a given character, e.g. `text[i]` has font `format[i]`.
Now, in the loops the index for `text` is increased by the length of
the character, e.g. 1 to 4 bytes, whereas index for `format` is
only incremented. It seems there's an assumption that `format` treats
multibyte characters as one byte, which doesn't seem to be true by
looking at the process of filling up the `format` array. This can result
in using wrong font e.g. FONT_SATCOM for the \button character which
should be FONT_BUTTON, hence the `StrUtils::Utf8CharSizeAt` method
complains about unexpected continuation byte via exception, causing
the crash.
Incrementing the index by the UTF-8 length seems to have fixed the
issue. However, I am not sure if I haven't broken anything else by this
and if I analyzed the intended behaviour correctly.
Gfx::CText needs a major refactor IMHO.
2020-09-20 19:22:05 +00:00
fmtIndex + = len ;
2012-08-06 18:20:50 +00:00
}
return width ;
2012-08-03 21:23:13 +00:00
}
2013-05-12 16:38:01 +00:00
float CText : : GetStringWidth ( std : : string text , FontType font , float size )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
2013-05-12 16:38:01 +00:00
// Skip special chars
for ( char & c : text )
{
2014-03-02 00:15:59 +00:00
if ( c < 32 & & c > = 0 )
2013-05-12 16:38:01 +00:00
c = ' : ' ;
}
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
CachedFont * cf = GetOrOpenFont ( font , size ) ;
2012-08-06 18:20:50 +00:00
assert ( cf ! = nullptr ) ;
2012-08-11 15:17:04 +00:00
Math : : IntPoint wndSize ;
TTF_SizeUTF8 ( cf - > font , text . c_str ( ) , & wndSize . x , & wndSize . y ) ;
Math : : Point ifSize = m_engine - > WindowToInterfaceSize ( wndSize ) ;
return ifSize . x ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
float CText : : GetCharWidth ( UTF8Char ch , FontType font , float size , float offset )
2012-08-03 21:23:13 +00:00
{
2015-07-16 18:24:54 +00:00
if ( font = = FONT_BUTTON )
{
2015-04-07 10:26:44 +00:00
Math : : IntPoint windowSize = m_engine - > GetWindowSize ( ) ;
2017-10-11 15:10:04 +00:00
float height = GetHeight ( FONT_COMMON , size ) ;
2015-04-07 10:26:44 +00:00
float width = height * ( static_cast < float > ( windowSize . y ) / windowSize . x ) ;
return width ;
}
2012-08-06 18:20:50 +00:00
2013-05-12 16:38:01 +00:00
int width = 1 ;
2014-03-02 00:15:59 +00:00
if ( ch . c1 < 32 & & ch . c1 > = 0 )
2013-05-12 16:38:01 +00:00
{
if ( ch . c1 = = ' \t ' )
width = m_tabSize ;
// TODO: tab sizing at intervals?
ch . c1 = ' : ' ;
}
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
CachedFont * cf = GetOrOpenFont ( font , size ) ;
2012-08-06 18:20:50 +00:00
assert ( cf ! = nullptr ) ;
2013-10-12 19:12:54 +00:00
Math : : Point charSize ;
2012-08-06 18:20:50 +00:00
auto it = cf - > cache . find ( ch ) ;
if ( it ! = cf - > cache . end ( ) )
2013-10-12 19:12:54 +00:00
{
2015-09-25 09:11:35 +00:00
charSize = m_engine - > WindowToInterfaceSize ( ( * it ) . second . charSize ) ;
2013-10-12 19:12:54 +00:00
}
2012-08-06 18:20:50 +00:00
else
2013-10-12 19:12:54 +00:00
{
Math : : IntPoint wndSize ;
std : : string text ;
text . append ( { ch . c1 , ch . c2 , ch . c3 } ) ;
TTF_SizeUTF8 ( cf - > font , text . c_str ( ) , & wndSize . x , & wndSize . y ) ;
charSize = m_engine - > WindowToInterfaceSize ( wndSize ) ;
}
2012-08-06 18:20:50 +00:00
2013-10-12 19:12:54 +00:00
return charSize . x * width ;
2012-08-03 21:23:13 +00:00
}
2016-04-06 10:21:41 +00:00
int CText : : GetCharWidthInt ( UTF8Char ch , FontType font , float size , float offset )
{
if ( font = = FONT_BUTTON )
{
Math : : IntPoint windowSize = m_engine - > GetWindowSize ( ) ;
2017-10-11 15:10:04 +00:00
int height = GetHeightInt ( FONT_COMMON , size ) ;
2016-04-06 10:21:41 +00:00
int width = height * ( static_cast < float > ( windowSize . y ) / windowSize . x ) ;
return width ;
}
int width = 1 ;
if ( ch . c1 < 32 & & ch . c1 > = 0 )
{
if ( ch . c1 = = ' \t ' )
width = m_tabSize ;
// TODO: tab sizing at intervals?
ch . c1 = ' : ' ;
}
CachedFont * cf = GetOrOpenFont ( font , size ) ;
assert ( cf ! = nullptr ) ;
Math : : IntPoint charSize ;
auto it = cf - > cache . find ( ch ) ;
if ( it ! = cf - > cache . end ( ) )
{
charSize = ( * it ) . second . charSize ;
}
else
{
std : : string text ;
text . append ( { ch . c1 , ch . c2 , ch . c3 } ) ;
TTF_SizeUTF8 ( cf - > font , text . c_str ( ) , & charSize . x , & charSize . y ) ;
}
return charSize . x * width ;
}
2012-08-03 21:23:13 +00:00
2013-02-09 22:49:38 +00:00
int CText : : Justify ( const std : : string & text , std : : vector < FontMetaChar > : : iterator format ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator end ,
2012-09-26 14:31:04 +00:00
float size , float width )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
float pos = 0.0f ;
int cut = 0 ;
unsigned int index = 0 ;
unsigned int fmtIndex = 0 ;
while ( index < text . length ( ) )
{
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2013-02-24 00:40:55 +00:00
if ( format + fmtIndex ! = end )
font = static_cast < FontType > ( * ( format + fmtIndex ) & FONT_MASK_FONT ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
UTF8Char ch ;
2012-08-06 18:20:50 +00:00
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2012-08-06 18:20:50 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
2012-09-19 21:50:28 +00:00
if ( font ! = FONT_BUTTON )
2012-08-06 18:20:50 +00:00
{
if ( ch . c1 = = ' \n ' )
return index + 1 ;
if ( ch . c1 = = ' ' )
cut = index + 1 ;
}
pos + = GetCharWidth ( ch , font , size , pos ) ;
if ( pos > width )
{
if ( cut = = 0 ) return index ;
else return cut ;
}
index + = len ;
Fix crashes in SatCom on some \button characters
The issue is that the \button fragments in SatCom are replaced with
invalid UTF-8 bytes, hence those bytes need to be handled as a special
case when checking for UTF-8 character length.
When the text is processed e.g. in `Gfx::CText::Justify`, there are
generally two arrays: `text` and `format`. The second one is interpreted
as a font for a given character, e.g. `text[i]` has font `format[i]`.
Now, in the loops the index for `text` is increased by the length of
the character, e.g. 1 to 4 bytes, whereas index for `format` is
only incremented. It seems there's an assumption that `format` treats
multibyte characters as one byte, which doesn't seem to be true by
looking at the process of filling up the `format` array. This can result
in using wrong font e.g. FONT_SATCOM for the \button character which
should be FONT_BUTTON, hence the `StrUtils::Utf8CharSizeAt` method
complains about unexpected continuation byte via exception, causing
the crash.
Incrementing the index by the UTF-8 length seems to have fixed the
issue. However, I am not sure if I haven't broken anything else by this
and if I analyzed the intended behaviour correctly.
Gfx::CText needs a major refactor IMHO.
2020-09-20 19:22:05 +00:00
fmtIndex + = len ;
2012-08-06 18:20:50 +00:00
}
return index ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
int CText : : Justify ( const std : : string & text , FontType font , float size , float width )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
float pos = 0.0f ;
int cut = 0 ;
unsigned int index = 0 ;
while ( index < text . length ( ) )
{
2012-09-19 21:50:28 +00:00
UTF8Char ch ;
2012-08-06 18:20:50 +00:00
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2012-08-06 18:20:50 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
if ( ch . c1 = = ' \n ' )
2012-10-11 21:09:29 +00:00
{
2012-08-06 18:20:50 +00:00
return index + 1 ;
2012-10-11 21:09:29 +00:00
}
2012-08-06 18:20:50 +00:00
if ( ch . c1 = = ' ' )
cut = index + 1 ;
pos + = GetCharWidth ( ch , font , size , pos ) ;
if ( pos > width )
{
if ( cut = = 0 ) return index ;
else return cut ;
}
2012-10-11 21:09:29 +00:00
index + = len ;
2012-08-06 18:20:50 +00:00
}
return index ;
2012-08-03 21:23:13 +00:00
}
2013-02-09 22:49:38 +00:00
int CText : : Detect ( const std : : string & text , std : : vector < FontMetaChar > : : iterator format ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator end ,
2012-09-26 14:31:04 +00:00
float size , float offset )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
float pos = 0.0f ;
unsigned int index = 0 ;
unsigned int fmtIndex = 0 ;
while ( index < text . length ( ) )
{
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2013-02-24 00:40:55 +00:00
if ( format + fmtIndex ! = end )
font = static_cast < FontType > ( * ( format + fmtIndex ) & FONT_MASK_FONT ) ;
2012-08-06 18:20:50 +00:00
2012-09-19 21:50:28 +00:00
UTF8Char ch ;
2012-08-06 18:20:50 +00:00
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2012-08-06 18:20:50 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
if ( ch . c1 = = ' \n ' )
return index ;
float width = GetCharWidth ( ch , font , size , pos ) ;
if ( offset < = pos + width / 2.0f )
return index ;
pos + = width ;
index + = len ;
Fix crashes in SatCom on some \button characters
The issue is that the \button fragments in SatCom are replaced with
invalid UTF-8 bytes, hence those bytes need to be handled as a special
case when checking for UTF-8 character length.
When the text is processed e.g. in `Gfx::CText::Justify`, there are
generally two arrays: `text` and `format`. The second one is interpreted
as a font for a given character, e.g. `text[i]` has font `format[i]`.
Now, in the loops the index for `text` is increased by the length of
the character, e.g. 1 to 4 bytes, whereas index for `format` is
only incremented. It seems there's an assumption that `format` treats
multibyte characters as one byte, which doesn't seem to be true by
looking at the process of filling up the `format` array. This can result
in using wrong font e.g. FONT_SATCOM for the \button character which
should be FONT_BUTTON, hence the `StrUtils::Utf8CharSizeAt` method
complains about unexpected continuation byte via exception, causing
the crash.
Incrementing the index by the UTF-8 length seems to have fixed the
issue. However, I am not sure if I haven't broken anything else by this
and if I analyzed the intended behaviour correctly.
Gfx::CText needs a major refactor IMHO.
2020-09-20 19:22:05 +00:00
fmtIndex + = len ;
2012-08-06 18:20:50 +00:00
}
return index ;
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
int CText : : Detect ( const std : : string & text , FontType font , float size , float offset )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-06 18:20:50 +00:00
float pos = 0.0f ;
unsigned int index = 0 ;
while ( index < text . length ( ) )
{
2012-09-19 21:50:28 +00:00
UTF8Char ch ;
2012-08-06 18:20:50 +00:00
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2012-08-06 18:20:50 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
index + = len ;
if ( ch . c1 = = ' \n ' )
return index ;
float width = GetCharWidth ( ch , font , size , pos ) ;
if ( offset < = pos + width / 2.0f )
return index ;
pos + = width ;
}
return index ;
2012-08-03 21:23:13 +00:00
}
2013-05-12 16:38:01 +00:00
UTF8Char CText : : TranslateSpecialChar ( int specialChar )
{
UTF8Char ch ;
switch ( specialChar )
{
case CHAR_TAB :
ch . c1 = ' : ' ;
ch . c2 = 0 ;
ch . c3 = 0 ;
break ;
case CHAR_NEWLINE :
// Unicode: U+21B2
2016-11-26 12:48:12 +00:00
ch . c1 = static_cast < char > ( 0xE2 ) ;
ch . c2 = static_cast < char > ( 0x86 ) ;
ch . c3 = static_cast < char > ( 0xB2 ) ;
2013-05-12 16:38:01 +00:00
break ;
case CHAR_DOT :
// Unicode: U+23C5
2016-11-26 12:48:12 +00:00
ch . c1 = static_cast < char > ( 0xE2 ) ;
ch . c2 = static_cast < char > ( 0x8F ) ;
ch . c3 = static_cast < char > ( 0x85 ) ;
2013-05-12 16:38:01 +00:00
break ;
case CHAR_SQUARE :
// Unicode: U+25FD
2016-11-26 12:48:12 +00:00
ch . c1 = static_cast < char > ( 0xE2 ) ;
ch . c2 = static_cast < char > ( 0x97 ) ;
ch . c3 = static_cast < char > ( 0xBD ) ;
2013-05-12 16:38:01 +00:00
break ;
case CHAR_SKIP_RIGHT :
// Unicode: U+25B6
2016-11-26 12:48:12 +00:00
ch . c1 = static_cast < char > ( 0xE2 ) ;
ch . c2 = static_cast < char > ( 0x96 ) ;
ch . c3 = static_cast < char > ( 0xB6 ) ;
2013-05-12 16:38:01 +00:00
break ;
case CHAR_SKIP_LEFT :
// Unicode: U+25C0
2016-11-26 12:48:12 +00:00
ch . c1 = static_cast < char > ( 0xE2 ) ;
ch . c2 = static_cast < char > ( 0x97 ) ;
ch . c3 = static_cast < char > ( 0x80 ) ;
2013-05-12 16:38:01 +00:00
break ;
default :
ch . c1 = ' ? ' ;
ch . c2 = 0 ;
ch . c3 = 0 ;
break ;
}
return ch ;
}
2013-02-09 22:49:38 +00:00
void CText : : DrawString ( const std : : string & text , std : : vector < FontMetaChar > : : iterator format ,
2013-02-24 00:40:55 +00:00
std : : vector < FontMetaChar > : : iterator end ,
2016-04-06 10:21:41 +00:00
float size , Math : : IntPoint pos , int width , int eol , Color color )
2012-08-03 21:23:13 +00:00
{
2018-04-27 08:58:09 +00:00
m_engine - > SetWindowCoordinates ( ) ;
2012-08-06 18:20:50 +00:00
2016-04-06 10:21:41 +00:00
int start = pos . x ;
2012-08-06 18:20:50 +00:00
unsigned int fmtIndex = 0 ;
2012-09-24 21:55:52 +00:00
2012-09-26 14:31:04 +00:00
std : : vector < UTF8Char > chars ;
2015-04-07 10:26:44 +00:00
StringToUTFCharList ( text , chars , format , end ) ;
2012-09-26 14:31:04 +00:00
for ( auto it = chars . begin ( ) ; it ! = chars . end ( ) ; + + it )
{
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2013-02-24 00:40:55 +00:00
if ( format + fmtIndex ! = end )
font = static_cast < FontType > ( * ( format + fmtIndex ) & FONT_MASK_FONT ) ;
2012-08-06 18:20:50 +00:00
2012-09-24 21:55:52 +00:00
UTF8Char ch = * it ;
2012-08-06 18:20:50 +00:00
2016-04-06 10:21:41 +00:00
int offset = pos . x - start ;
int cw = GetCharWidthInt ( ch , font , size , offset ) ;
2012-08-06 18:20:50 +00:00
if ( offset + cw > width ) // exceeds the maximum width?
{
2013-05-27 20:26:44 +00:00
ch = TranslateSpecialChar ( CHAR_SKIP_RIGHT ) ;
2016-04-06 10:21:41 +00:00
cw = GetCharWidthInt ( ch , font , size , offset ) ;
2013-05-12 16:38:01 +00:00
pos . x = start + width - cw ;
color = Color ( 1.0f , 0.0f , 0.0f ) ;
DrawCharAndAdjustPos ( ch , font , size , pos , color ) ;
2012-08-06 18:20:50 +00:00
break ;
}
2015-08-18 14:00:44 +00:00
Color c = color ;
2012-09-19 21:50:28 +00:00
FontHighlight hl = static_cast < FontHighlight > ( format [ fmtIndex ] & FONT_MASK_HIGHLIGHT ) ;
2016-06-17 19:13:16 +00:00
if ( hl = = FONT_HIGHLIGHT_TOKEN )
2012-08-06 18:20:50 +00:00
{
2016-06-17 19:13:16 +00:00
c = Color ( 0.490f , 0.380f , 0.165f , 1.0f ) ; // #7D612A
}
else if ( hl = = FONT_HIGHLIGHT_TYPE )
{
c = Color ( 0.31f , 0.443f , 0.196f , 1.0f ) ; // #4F7132
}
else if ( hl = = FONT_HIGHLIGHT_CONST )
{
c = Color ( 0.882f , 0.176f , 0.176f , 1.0f ) ; // #E12D2D
}
else if ( hl = = FONT_HIGHLIGHT_THIS )
{
c = Color ( 0.545f , 0.329f , 0.608f , 1.0f ) ; // #8B549B
}
else if ( hl = = FONT_HIGHLIGHT_COMMENT )
{
c = Color ( 0.251f , 0.271f , 0.306f , 1.0f ) ; // #40454E
}
else if ( hl = = FONT_HIGHLIGHT_KEYWORD )
{
c = Color ( 0.239f , 0.431f , 0.588f , 1.0f ) ; // #3D6E96
}
else if ( hl = = FONT_HIGHLIGHT_STRING )
{
c = Color ( 0.239f , 0.384f , 0.341f , 1.0f ) ; // #3D6257
}
// draw highlight background or link underline
if ( font ! = FONT_BUTTON )
{
Math : : IntPoint charSize ;
charSize . x = GetCharWidthInt ( ch , font , size , offset ) ;
charSize . y = GetHeightInt ( font , size ) ;
2018-04-27 10:16:45 +00:00
// NB. for quad batching to improve highlight drawing performance, this code would have
// to be rearranged to draw all highlights before any characters are drawn.
2016-06-17 19:13:16 +00:00
DrawHighlight ( format [ fmtIndex ] , pos , charSize ) ;
2012-08-06 18:20:50 +00:00
}
2015-08-18 14:00:44 +00:00
DrawCharAndAdjustPos ( ch , font , size , pos , c ) ;
2012-08-06 18:20:50 +00:00
2014-03-25 20:56:40 +00:00
// increment fmtIndex for each byte in multibyte character
if ( ch . c1 ! = 0 )
fmtIndex + + ;
if ( ch . c2 ! = 0 )
fmtIndex + + ;
if ( ch . c3 ! = 0 )
fmtIndex + + ;
2012-08-06 18:20:50 +00:00
}
2013-05-12 16:38:01 +00:00
if ( eol ! = 0 )
{
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2013-05-12 16:38:01 +00:00
UTF8Char ch = TranslateSpecialChar ( eol ) ;
color = Color ( 1.0f , 0.0f , 0.0f ) ;
DrawCharAndAdjustPos ( ch , font , size , pos , color ) ;
}
2018-04-27 10:16:45 +00:00
m_quadBatch - > Flush ( ) ;
2018-04-27 08:58:09 +00:00
m_engine - > SetInterfaceCoordinates ( ) ;
2012-08-03 21:23:13 +00:00
}
2012-09-26 14:31:04 +00:00
void CText : : StringToUTFCharList ( const std : : string & text , std : : vector < UTF8Char > & chars )
{
unsigned int index = 0 ;
2012-09-29 08:40:11 +00:00
unsigned int totalLength = text . length ( ) ;
while ( index < totalLength )
2012-09-26 14:31:04 +00:00
{
UTF8Char ch ;
int len = StrUtils : : Utf8CharSizeAt ( text , index ) ;
if ( len > = 1 )
2012-09-29 08:40:11 +00:00
ch . c1 = text [ index ] ;
2012-09-26 14:31:04 +00:00
if ( len > = 2 )
2012-09-29 08:40:11 +00:00
ch . c2 = text [ index + 1 ] ;
2012-09-26 14:31:04 +00:00
if ( len > = 3 )
2012-09-29 08:40:11 +00:00
ch . c3 = text [ index + 2 ] ;
2012-09-26 14:31:04 +00:00
index + = len ;
chars . push_back ( ch ) ;
}
2012-09-24 21:55:52 +00:00
}
2015-08-12 19:07:16 +00:00
void CText : : StringToUTFCharList ( const std : : string & text , std : : vector < UTF8Char > & chars ,
std : : vector < FontMetaChar > : : iterator format ,
std : : vector < FontMetaChar > : : iterator end )
2015-04-07 10:26:44 +00:00
{
unsigned int index = 0 ;
unsigned int totalLength = text . length ( ) ;
while ( index < totalLength )
{
UTF8Char ch ;
2017-10-11 15:10:04 +00:00
FontType font = FONT_COMMON ;
2015-08-12 19:07:16 +00:00
if ( format + index ! = end )
2015-04-07 10:26:44 +00:00
font = static_cast < FontType > ( * ( format + index ) & FONT_MASK_FONT ) ;
2020-07-21 22:37:37 +00:00
int len = GetCharSizeAt ( font , text , index ) ;
2015-04-07 10:26:44 +00:00
if ( len > = 1 )
ch . c1 = text [ index ] ;
if ( len > = 2 )
ch . c2 = text [ index + 1 ] ;
if ( len > = 3 )
ch . c3 = text [ index + 2 ] ;
index + = len ;
chars . push_back ( ch ) ;
}
}
2020-07-21 22:37:37 +00:00
int CText : : GetCharSizeAt ( Gfx : : FontType font , const std : : string & text , unsigned int index ) const
{
int len = 0 ;
if ( font = = FONT_BUTTON )
{
len = 1 ;
}
else
{
len = StrUtils : : Utf8CharSizeAt ( text , index ) ;
}
return len ;
}
2012-09-19 21:50:28 +00:00
void CText : : DrawString ( const std : : string & text , FontType font ,
2016-04-06 10:21:41 +00:00
float size , Math : : IntPoint pos , int width , int eol , Color color )
2012-08-03 21:23:13 +00:00
{
2012-09-19 21:50:28 +00:00
assert ( font ! = FONT_BUTTON ) ;
2012-08-03 21:23:13 +00:00
2012-09-26 14:31:04 +00:00
std : : vector < UTF8Char > chars ;
StringToUTFCharList ( text , chars ) ;
2018-04-27 08:58:09 +00:00
m_engine - > SetWindowCoordinates ( ) ;
2012-09-26 14:31:04 +00:00
for ( auto it = chars . begin ( ) ; it ! = chars . end ( ) ; + + it )
{
2012-09-30 08:56:35 +00:00
DrawCharAndAdjustPos ( * it , font , size , pos , color ) ;
2012-08-03 21:23:13 +00:00
}
2018-04-27 10:16:45 +00:00
m_quadBatch - > Flush ( ) ;
2018-04-27 08:58:09 +00:00
m_engine - > SetInterfaceCoordinates ( ) ;
2012-08-03 21:23:13 +00:00
}
2016-06-17 19:13:16 +00:00
void CText : : DrawHighlight ( FontMetaChar hl , Math : : IntPoint pos , Math : : IntPoint size )
2012-08-03 21:23:13 +00:00
{
2012-08-06 18:20:50 +00:00
// Gradient colors
2012-09-19 21:50:28 +00:00
Color grad [ 4 ] ;
2012-08-06 18:20:50 +00:00
// TODO: switch to alpha factors
2012-08-03 21:23:13 +00:00
2016-06-17 19:13:16 +00:00
if ( ( hl & FONT_MASK_LINK ) ! = 0 )
2012-08-03 21:23:13 +00:00
{
2016-06-17 19:13:16 +00:00
grad [ 0 ] = grad [ 1 ] = grad [ 2 ] = grad [ 3 ] = Color ( 0.0f , 0.0f , 1.0f , 0.5f ) ;
}
else if ( ( hl & FONT_MASK_HIGHLIGHT ) = = FONT_HIGHLIGHT_KEY )
{
grad [ 0 ] = grad [ 1 ] = grad [ 2 ] = grad [ 3 ] =
Color ( 192.0f / 256.0f , 192.0f / 256.0f , 192.0f / 256.0f , 0.5f ) ;
}
else
{
return ;
2012-08-03 21:23:13 +00:00
}
2018-04-27 10:16:45 +00:00
m_quadBatch - > Flush ( ) ;
2012-08-11 15:17:04 +00:00
Math : : IntPoint vsize = m_engine - > GetWindowSize ( ) ;
2012-08-06 18:20:50 +00:00
float h = 0.0f ;
2012-08-11 15:17:04 +00:00
if ( vsize . y < = 768.0f ) // 1024x768 or less?
2016-04-06 10:21:41 +00:00
h = 1.01f ; // 1 pixel
2012-08-06 18:20:50 +00:00
else // more than 1024x768?
2016-04-06 10:21:41 +00:00
h = 2.0f ; // 2 pixels
2012-08-03 21:23:13 +00:00
Math : : Point p1 , p2 ;
p1 . x = pos . x ;
2016-04-08 09:23:11 +00:00
p1 . y = pos . y - size . y ;
2012-08-11 15:17:04 +00:00
p2 . x = pos . x + size . x ;
2016-04-08 09:23:11 +00:00
p2 . y = pos . y ;
2012-08-03 21:23:13 +00:00
2016-06-17 19:13:16 +00:00
if ( ( hl & FONT_MASK_LINK ) ! = 0 )
2012-08-03 21:23:13 +00:00
{
2016-04-08 09:23:11 +00:00
p1 . y = pos . y - h ; // just emphasized
2012-08-03 21:23:13 +00:00
}
2012-09-29 17:35:14 +00:00
m_device - > SetTextureEnabled ( 0 , false ) ;
2012-08-03 21:23:13 +00:00
2012-09-19 21:50:28 +00:00
VertexCol quad [ ] =
2012-08-03 21:23:13 +00:00
{
2016-04-08 09:23:11 +00:00
VertexCol ( Math : : Vector ( p1 . x , p2 . y , 0.0f ) , grad [ 3 ] ) ,
VertexCol ( Math : : Vector ( p1 . x , p1 . y , 0.0f ) , grad [ 0 ] ) ,
VertexCol ( Math : : Vector ( p2 . x , p2 . y , 0.0f ) , grad [ 2 ] ) ,
VertexCol ( Math : : Vector ( p2 . x , p1 . y , 0.0f ) , grad [ 1 ] )
2012-08-03 21:23:13 +00:00
} ;
2021-12-05 11:26:34 +00:00
m_device - > DrawPrimitive ( PrimitiveType : : TRIANGLE_STRIP , quad , 4 ) ;
2012-08-03 21:23:13 +00:00
m_engine - > AddStatisticTriangle ( 2 ) ;
2012-09-29 17:35:14 +00:00
m_device - > SetTextureEnabled ( 0 , true ) ;
2012-08-03 21:23:13 +00:00
}
2016-04-06 10:21:41 +00:00
void CText : : DrawCharAndAdjustPos ( UTF8Char ch , FontType font , float size , Math : : IntPoint & pos , Color color )
2012-08-03 21:23:13 +00:00
{
2016-04-06 10:21:41 +00:00
if ( font = = FONT_BUTTON )
2015-04-07 10:06:43 +00:00
{
Math : : IntPoint windowSize = m_engine - > GetWindowSize ( ) ;
2017-10-11 15:10:04 +00:00
int height = GetHeightInt ( FONT_COMMON , size ) ;
2016-04-06 10:21:41 +00:00
int width = height * ( static_cast < float > ( windowSize . y ) / windowSize . x ) ;
2012-08-06 18:20:50 +00:00
2016-04-06 10:21:41 +00:00
Math : : IntPoint p1 ( pos . x , pos . y - height ) ;
Math : : IntPoint p2 ( pos . x + width , pos . y ) ;
2012-08-03 21:23:13 +00:00
2015-04-07 10:06:43 +00:00
// For whatever reason ch.c1 is a SIGNED char, we need to fix that
2015-04-07 10:26:44 +00:00
unsigned char icon = static_cast < unsigned char > ( ch . c1 ) ;
2018-04-27 10:16:45 +00:00
2018-05-10 09:24:59 +00:00
// TODO: A bit of code duplication, see CControl::SetButtonTextureForIcon()
unsigned int texID = m_engine - > LoadTexture ( " textures/interface/button " + StrUtils : : ToString < int > ( ( icon / 64 ) + 1 ) + " .png " ) . id ;
icon = icon % 64 ;
2013-05-12 16:38:01 +00:00
2015-04-07 10:06:43 +00:00
Math : : Point uv1 , uv2 ;
uv1 . x = ( 32.0f / 256.0f ) * ( icon % 8 ) ;
uv1 . y = ( 32.0f / 256.0f ) * ( icon / 8 ) ;
uv2 . x = ( 32.0f / 256.0f ) + uv1 . x ;
uv2 . y = ( 32.0f / 256.0f ) + uv1 . y ;
2012-08-03 21:23:13 +00:00
2015-04-07 10:06:43 +00:00
float dp = 0.5f / 256.0f ;
uv1 . x + = dp ;
uv1 . y + = dp ;
uv2 . x - = dp ;
uv2 . y - = dp ;
2021-06-16 00:33:15 +00:00
glm : : u8vec4 col = { color . r * 255 , color . g * 255 , color . b * 255 , color . a * 255 } ;
Vertex2D quad [ 4 ] =
2015-04-07 10:06:43 +00:00
{
2021-06-16 00:33:15 +00:00
{ { p1 . x , p2 . y } , { uv1 . x , uv2 . y } , col } ,
{ { p1 . x , p1 . y } , { uv1 . x , uv1 . y } , col } ,
{ { p2 . x , p2 . y } , { uv2 . x , uv2 . y } , col } ,
{ { p2 . x , p1 . y } , { uv2 . x , uv1 . y } , col }
2015-04-07 10:06:43 +00:00
} ;
2018-04-27 10:16:45 +00:00
m_quadBatch - > Add ( quad , texID , ENG_RSTATE_TTEXTURE_WHITE , color ) ;
2015-04-07 10:06:43 +00:00
2015-04-07 10:26:44 +00:00
pos . x + = width ;
2012-08-03 21:23:13 +00:00
}
else
{
2015-04-07 10:06:43 +00:00
int width = 1 ;
if ( ch . c1 > 0 & & ch . c1 < 32 )
{
if ( ch . c1 = = ' \t ' )
2015-08-07 12:16:01 +00:00
{
color = Color ( 1.0f , 0.0f , 0.0f , 1.0f ) ;
2015-04-07 10:06:43 +00:00
width = m_tabSize ;
2015-08-07 12:16:01 +00:00
}
2012-08-03 21:23:13 +00:00
2015-04-07 10:06:43 +00:00
ch = TranslateSpecialChar ( ch . c1 ) ;
}
2012-08-06 18:20:50 +00:00
2015-08-07 11:24:45 +00:00
CharTexture tex = GetCharTexture ( ch , font , size ) ;
2015-04-07 10:06:43 +00:00
2016-04-06 10:21:41 +00:00
Math : : Point p1 ( pos . x , pos . y - tex . charSize . y ) ;
Math : : Point p2 ( pos . x + tex . charSize . x , pos . y ) ;
2016-03-18 01:01:06 +00:00
2016-04-03 04:45:13 +00:00
const float halfPixelMargin = 0.5f ;
Math : : Point texCoord1 ( static_cast < float > ( tex . charPos . x + halfPixelMargin ) / FONT_TEXTURE_SIZE . x ,
static_cast < float > ( tex . charPos . y + halfPixelMargin ) / FONT_TEXTURE_SIZE . y ) ;
2016-04-06 10:21:41 +00:00
Math : : Point texCoord2 ( static_cast < float > ( tex . charPos . x + tex . charSize . x - halfPixelMargin ) / FONT_TEXTURE_SIZE . x ,
static_cast < float > ( tex . charPos . y + tex . charSize . y - halfPixelMargin ) / FONT_TEXTURE_SIZE . y ) ;
2015-04-07 10:06:43 +00:00
2021-06-16 00:33:15 +00:00
glm : : u8vec4 col = { color . r * 255 , color . g * 255 , color . b * 255 , color . a * 255 } ;
Vertex2D quad [ 4 ] =
2015-04-07 10:06:43 +00:00
{
2021-06-16 00:33:15 +00:00
{ { p1 . x , p2 . y } , { texCoord1 . x , texCoord2 . y } , col } ,
{ { p1 . x , p1 . y } , { texCoord1 . x , texCoord1 . y } , col } ,
{ { p2 . x , p2 . y } , { texCoord2 . x , texCoord2 . y } , col } ,
{ { p2 . x , p1 . y } , { texCoord2 . x , texCoord1 . y } , col }
2015-04-07 10:06:43 +00:00
} ;
2018-04-27 10:16:45 +00:00
m_quadBatch - > Add ( quad , tex . id , ENG_RSTATE_TEXT , color ) ;
2015-04-07 10:06:43 +00:00
2016-04-06 10:21:41 +00:00
pos . x + = tex . charSize . x * width ;
2015-04-07 10:06:43 +00:00
}
2012-08-03 21:23:13 +00:00
}
2012-09-19 21:50:28 +00:00
CachedFont * CText : : GetOrOpenFont ( FontType font , float size )
2012-08-03 21:23:13 +00:00
{
2013-01-03 23:29:19 +00:00
Math : : IntPoint windowSize = m_engine - > GetWindowSize ( ) ;
int pointSize = static_cast < int > ( size * ( windowSize . Length ( ) / REFERENCE_SIZE . Length ( ) ) ) ;
2012-08-03 21:23:13 +00:00
2015-08-12 19:07:16 +00:00
if ( m_lastCachedFont ! = nullptr & &
m_lastFontType = = font & &
m_lastFontSize = = pointSize )
2012-08-03 21:23:13 +00:00
{
2015-08-12 19:07:16 +00:00
return m_lastCachedFont ;
2012-08-03 21:23:13 +00:00
}
auto it = m_fonts . find ( font ) ;
if ( it = = m_fonts . end ( ) )
{
m_error = std : : string ( " Invalid font type " ) + StrUtils : : ToString < int > ( static_cast < int > ( font ) ) ;
return nullptr ;
}
2015-08-12 19:07:16 +00:00
MultisizeFont * mf = it - > second . get ( ) ;
2012-08-03 21:23:13 +00:00
auto jt = mf - > fonts . find ( pointSize ) ;
if ( jt ! = mf - > fonts . end ( ) )
{
2015-08-12 19:07:16 +00:00
m_lastCachedFont = jt - > second . get ( ) ;
2012-08-03 21:23:13 +00:00
m_lastFontType = font ;
m_lastFontSize = pointSize ;
return m_lastCachedFont ;
}
2016-02-11 15:12:16 +00:00
auto file = CResourceManager : : GetSDLMemoryHandler ( mf - > fileName ) ;
2015-07-16 18:24:54 +00:00
if ( ! file - > IsOpen ( ) )
2014-08-06 10:27:17 +00:00
{
2016-02-18 17:07:29 +00:00
m_error = std : : string ( " Unable to open file ' " ) + mf - > fileName + " ' (font size = " + StrUtils : : ToString < float > ( size ) + " ) " ;
2014-08-06 10:27:17 +00:00
return nullptr ;
}
2016-02-18 17:07:29 +00:00
GetLogger ( ) - > Debug ( " Loaded font file %s (font size = %.1f) \n " , mf - > fileName . c_str ( ) , size ) ;
2015-07-16 18:24:54 +00:00
2015-08-12 19:07:16 +00:00
auto newFont = MakeUnique < CachedFont > ( std : : move ( file ) , pointSize ) ;
if ( newFont - > font = = nullptr )
2015-07-16 18:24:54 +00:00
{
2012-08-03 21:23:13 +00:00
m_error = std : : string ( " TTF_OpenFont error " ) + std : : string ( TTF_GetError ( ) ) ;
2015-07-16 18:24:54 +00:00
return nullptr ;
}
2012-08-03 21:23:13 +00:00
2015-08-12 19:07:16 +00:00
m_lastCachedFont = newFont . get ( ) ;
mf - > fonts [ pointSize ] = std : : move ( newFont ) ;
2012-08-03 21:23:13 +00:00
return m_lastCachedFont ;
}
2016-03-18 01:01:06 +00:00
CharTexture CText : : GetCharTexture ( UTF8Char ch , FontType font , float size )
{
CachedFont * cf = GetOrOpenFont ( font , size ) ;
if ( cf = = nullptr )
return CharTexture ( ) ;
auto it = cf - > cache . find ( ch ) ;
CharTexture tex ;
if ( it ! = cf - > cache . end ( ) )
{
tex = ( * it ) . second ;
}
else
{
tex = CreateCharTexture ( ch , cf ) ;
if ( tex . id = = 0 ) // invalid
return CharTexture ( ) ;
cf - > cache [ ch ] = tex ;
}
return tex ;
}
Math : : IntPoint CText : : GetFontTextureSize ( )
{
return FONT_TEXTURE_SIZE ;
}
2012-09-19 21:50:28 +00:00
CharTexture CText : : CreateCharTexture ( UTF8Char ch , CachedFont * font )
2012-08-03 21:23:13 +00:00
{
CharTexture texture ;
SDL_Surface * textSurface = nullptr ;
SDL_Color white = { 255 , 255 , 255 , 0 } ;
2012-08-06 18:20:50 +00:00
char str [ ] = { ch . c1 , ch . c2 , ch . c3 , ' \0 ' } ;
2012-08-03 21:23:13 +00:00
textSurface = TTF_RenderUTF8_Blended ( font - > font , str , white ) ;
if ( textSurface = = nullptr )
{
m_error = " TTF_Render error " ;
return texture ;
}
2016-04-03 04:45:13 +00:00
const int pixelMargin = 1 ;
2016-04-06 10:21:41 +00:00
Math : : IntPoint tileSize ( Math : : Max ( 16 , Math : : NextPowerOfTwo ( textSurface - > w ) ) + pixelMargin ,
Math : : Max ( 16 , Math : : NextPowerOfTwo ( textSurface - > h ) ) + pixelMargin ) ;
2012-08-03 21:23:13 +00:00
2016-03-18 01:01:06 +00:00
FontTexture * fontTexture = GetOrCreateFontTexture ( tileSize ) ;
2012-08-03 21:23:13 +00:00
2016-03-18 01:01:06 +00:00
if ( fontTexture = = nullptr )
2012-08-03 21:23:13 +00:00
{
m_error = " Texture create error " ;
}
2012-09-16 18:00:25 +00:00
else
{
2016-03-18 01:01:06 +00:00
texture . id = fontTexture - > id ;
texture . charPos = GetNextTilePos ( * fontTexture ) ;
2015-09-25 09:11:35 +00:00
texture . charSize = Math : : IntPoint ( textSurface - > w , textSurface - > h ) ;
2016-03-18 01:01:06 +00:00
ImageData imageData ;
imageData . surface = textSurface ;
Texture tex ;
tex . id = texture . id ;
m_device - > UpdateTexture ( tex , texture . charPos , & imageData , TEX_IMG_RGBA ) ;
imageData . surface = nullptr ;
- - fontTexture - > freeSlots ;
2012-09-16 18:00:25 +00:00
}
2012-08-03 21:23:13 +00:00
2012-09-16 18:00:25 +00:00
SDL_FreeSurface ( textSurface ) ;
2012-08-03 21:23:13 +00:00
return texture ;
}
2012-09-19 21:50:28 +00:00
2016-03-18 01:01:06 +00:00
FontTexture * CText : : GetOrCreateFontTexture ( Math : : IntPoint tileSize )
2015-08-07 11:24:45 +00:00
{
2016-03-18 01:01:06 +00:00
for ( auto & fontTexture : m_fontTextures )
2015-08-07 11:24:45 +00:00
{
2016-03-18 01:01:06 +00:00
if ( fontTexture . tileSize = = tileSize & & fontTexture . freeSlots > 0 )
return & fontTexture ;
2015-08-07 11:24:45 +00:00
}
2016-03-18 01:01:06 +00:00
FontTexture newFontTexture = CreateFontTexture ( tileSize ) ;
if ( newFontTexture . id = = 0 )
2015-08-07 11:24:45 +00:00
{
2016-03-18 01:01:06 +00:00
return nullptr ;
}
2015-08-07 11:24:45 +00:00
2016-03-18 01:01:06 +00:00
m_fontTextures . push_back ( newFontTexture ) ;
return & m_fontTextures . back ( ) ;
}
2015-08-07 11:24:45 +00:00
2016-03-18 01:01:06 +00:00
FontTexture CText : : CreateFontTexture ( Math : : IntPoint tileSize )
{
SDL_Surface * textureSurface = SDL_CreateRGBSurface ( 0 , FONT_TEXTURE_SIZE . x , FONT_TEXTURE_SIZE . y , 32 ,
0x00ff0000 , 0x0000ff00 , 0x000000ff , 0xff000000 ) ;
ImageData data ;
data . surface = textureSurface ;
TextureCreateParams createParams ;
createParams . format = TEX_IMG_RGBA ;
createParams . filter = TEX_FILTER_NEAREST ;
createParams . mipmap = false ;
Texture tex = m_device - > CreateTexture ( & data , createParams ) ;
data . surface = nullptr ;
SDL_FreeSurface ( textureSurface ) ;
FontTexture fontTexture ;
fontTexture . id = tex . id ;
fontTexture . tileSize = tileSize ;
int horizontalTiles = FONT_TEXTURE_SIZE . x / tileSize . x ;
int verticalTiles = FONT_TEXTURE_SIZE . y / tileSize . y ;
fontTexture . freeSlots = horizontalTiles * verticalTiles ;
return fontTexture ;
2015-08-07 11:24:45 +00:00
}
2016-03-18 01:01:06 +00:00
Math : : IntPoint CText : : GetNextTilePos ( const FontTexture & fontTexture )
{
2019-01-02 00:18:45 +00:00
int horizontalTiles = FONT_TEXTURE_SIZE . x / std : : max ( 1 , fontTexture . tileSize . x ) ; //this should prevent crashes in some combinations of resolution and font size, see issue #1128
int verticalTiles = FONT_TEXTURE_SIZE . y / std : : max ( 1 , fontTexture . tileSize . y ) ;
2016-03-18 01:01:06 +00:00
int totalTiles = horizontalTiles * verticalTiles ;
int tileNumber = totalTiles - fontTexture . freeSlots ;
2019-01-02 00:18:45 +00:00
int verticalTileIndex = tileNumber / std : : max ( 1 , horizontalTiles ) ;
2016-03-18 01:01:06 +00:00
int horizontalTileIndex = tileNumber % horizontalTiles ;
return Math : : IntPoint ( horizontalTileIndex * fontTexture . tileSize . x ,
verticalTileIndex * fontTexture . tileSize . y ) ;
}
2012-09-19 21:50:28 +00:00
} // namespace Gfx