* new rendering system for volatile text

(symbols rendered to atlas texture)
deliver position via glRasterPos + glGet GL_CURRENT_RASTER_POSITION to avoid adding calculations of screen coords in many spots
solves problem: pango 1.44 switch freetype->harfbuzz = bitmap fonts aren't available, in particular no gdk_gl_font_use_pango_font()
text is rendered from font bottom (was from baseline)(latter is wanted less often)
wgl bitmap<->pango sizes do not match, thus realign all text positions, increase default font size
make 2D axes labels, view type text, sizing info adaptive to font size
#24
fixes #42
This commit is contained in:
Garux 2020-05-14 07:51:34 +03:00
parent 33f7e66b87
commit a0a47640b0
6 changed files with 178 additions and 56 deletions

View File

@ -93,25 +93,62 @@ void gray_to_texture( const unsigned int x_max, const unsigned int y_max, const
class GLFontCallList final : public GLFont
{
GLuint m_displayList;
GLuint m_atlas;
int m_pixelHeight;
int m_pixelAscent;
int m_pixelDescent;
PangoFontMap *m_fontmap;
PangoContext *m_ft2_context;
public:
GLFontCallList( GLuint displayList, int asc, int desc, int pixelHeight, PangoFontMap *fontmap, PangoContext *ft2_context ) :
m_displayList( displayList ), m_pixelHeight( pixelHeight ), m_pixelAscent( asc ), m_pixelDescent( desc ), m_fontmap( fontmap ), m_ft2_context( ft2_context ){
GLFontCallList( GLuint displayList, GLuint atlas, int asc, int desc, int pixelHeight, PangoFontMap *fontmap, PangoContext *ft2_context ) :
m_displayList( displayList ), m_atlas( atlas ), m_pixelHeight( pixelHeight ), m_pixelAscent( asc ), m_pixelDescent( desc ), m_fontmap( fontmap ), m_ft2_context( ft2_context ){
}
~GLFontCallList(){
glDeleteLists( m_displayList, 256 );
glDeleteLists( m_displayList, 128 );
glDeleteTextures( 1, &m_atlas );
if( m_ft2_context )
g_object_unref( G_OBJECT( m_ft2_context ) );
if( m_fontmap )
g_object_unref( G_OBJECT( m_fontmap ) );
}
void printString( const char *s ){
GLboolean rasterPosValid;
glGetBooleanv( GL_CURRENT_RASTER_POSITION_VALID, &rasterPosValid );
if( !rasterPosValid )
return;
GLfloat rasterPos[4];
glGetFloatv( GL_CURRENT_RASTER_POSITION, rasterPos );
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glOrtho( viewport[0], viewport[2], viewport[1], viewport[3], -1, 1 );
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
glTranslatef( rasterPos[0], rasterPos[1], 0 );
glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_POLYGON_BIT );
glDisable( GL_LIGHTING );
glEnable( GL_TEXTURE_2D );
glDisable( GL_DEPTH_TEST );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glBindTexture( GL_TEXTURE_2D, m_atlas );
GlobalOpenGL().m_glListBase( m_displayList );
GlobalOpenGL().m_glCallLists( GLsizei( strlen( s ) ), GL_UNSIGNED_BYTE, reinterpret_cast<const GLubyte*>( s ) );
glPopAttrib();
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW ); //! must leave GL_MODELVIEW mode, as renderer relies on this during Renderer.render()
glPopMatrix();
}
void renderString( const char *s, const GLuint& tex, const unsigned char colour[3], unsigned int& wid, unsigned int& hei ){
@ -669,55 +706,53 @@ GLFont *glfont_create( const char* font_string ){
#include <pango/pangoft2.h>
#include <pango/pango-utils.h>
#include <gtk/gtkglwidget.h>
#include <gtk/gtksettings.h>
PangoFont* tryFont( const char* font_string, PangoFontDescription*& font_desc, GLuint font_list_base ){
PangoFont* tryFont( const char* font_string, PangoFontMap *fontmap, PangoContext *context, PangoFontDescription*& font_desc ){
pango_font_description_free( font_desc );
font_desc = pango_font_description_from_string( font_string );
return gdk_gl_font_use_pango_font( font_desc, 0, 256, font_list_base );
return pango_font_map_load_font( fontmap, context, font_desc );
}
GLFont *glfont_create( const char* font_string ){
GLuint font_list_base = glGenLists( 256 );
GLuint font_list_base = glGenLists( 128 );
int font_height = 0, font_ascent = 0, font_descent = 0;
PangoFontDescription* font_desc = 0;
PangoFont* font = 0;
PangoFontMap *fontmap = pango_ft2_font_map_new();
pango_ft2_font_map_set_resolution( PANGO_FT2_FONT_MAP( fontmap ), 72, 72 );
PangoContext *ft2_context = pango_font_map_create_context( fontmap );
do
{
if( *font_string != '\0' )
if( ( font = tryFont( font_string, font_desc, font_list_base ) ) )
if( ( font = tryFont( font_string, fontmap, ft2_context, font_desc ) ) )
break;
#ifdef WIN32
if( ( font = tryFont( "arial 8", font_desc, font_list_base ) ) )
if( ( font = tryFont( "arial 12", fontmap, ft2_context, font_desc ) ) )
break;
if( ( font = tryFont( "fixed 8", font_desc, font_list_base ) ) )
if( ( font = tryFont( "fixed 12", fontmap, ft2_context, font_desc ) ) )
break;
if( ( font = tryFont( "courier new 8", font_desc, font_list_base ) ) )
if( ( font = tryFont( "courier new 12", fontmap, ft2_context, font_desc ) ) )
break;
#endif
GtkSettings *settings = gtk_settings_get_default();
gchar *fontname;
g_object_get( settings, "gtk-font-name", &fontname, NULL );
font = tryFont( fontname, font_desc, font_list_base );
font = tryFont( fontname, fontmap, ft2_context, font_desc );
g_free( fontname );
if( !font ){
const char* guessFonts[] = { "serif 8", "sans 8", "clean 8", "courier 8", "helvetica 8", "arial 8", "dejavu sans 8" };
const char* guessFonts[] = { "serif 12", "sans 12", "clean 12", "courier 12", "helvetica 12", "arial 12", "dejavu sans 12" };
for( const auto str : guessFonts ){
if( ( font = tryFont( str, font_desc, font_list_base ) ) )
if( ( font = tryFont( str, fontmap, ft2_context, font_desc ) ) )
break;
}
}
} while ( 0 );
PangoFontMap *fontmap = 0;
PangoContext *ft2_context = 0;
if ( font != 0 ) {
fontmap = pango_ft2_font_map_new();
pango_ft2_font_map_set_resolution( PANGO_FT2_FONT_MAP( fontmap ), 72, 72 );
ft2_context = pango_font_map_create_context( fontmap );
pango_context_set_font_description( ft2_context, font_desc );
PangoLayout *layout = pango_layout_new( ft2_context );
@ -732,13 +767,6 @@ GLFont *glfont_create( const char* font_string ){
g_object_unref( G_OBJECT( layout ) );
int font_descent_pango_units = log_rect.height - font_ascent_pango_units;
/* settings for render to tex; need to multiply size, so that bitmap callLists way size would be approximately = texture way */
gint fontsize = pango_font_description_get_size( font_desc );
fontsize *= 3;
fontsize /= 2; //*15/11 is equal to bitmap callLists size
pango_font_description_set_size( font_desc, fontsize );
pango_context_set_font_description( ft2_context, font_desc );
// globalOutputStream() << pango_font_description_get_family( font_desc ) << " " << pango_font_description_get_size( font_desc ) << "\n";
// g_object_unref( G_OBJECT( ft2_context ) );
@ -748,12 +776,103 @@ GLFont *glfont_create( const char* font_string ){
font_descent = PANGO_PIXELS_CEIL( font_descent_pango_units );
font_height = font_ascent + font_descent;
}
/* render displaylists */
GLuint atlas;
if ( font != 0 ) {
PangoLayout *layout;
PangoRectangle log_rect;
FT_Bitmap bitmap;
layout = pango_layout_new( ft2_context );
pango_layout_set_width( layout, -1 ); // -1 no wrapping. All text on one line.
GLint alignment;
glGetIntegerv( GL_UNPACK_ALIGNMENT, &alignment );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glGenTextures( 1, &atlas );
//Now we just setup some texture paramaters.
glBindTexture( GL_TEXTURE_2D, atlas );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0 );
//Here we actually create the texture itself
glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, font_height * 12, font_height * 12, // riskily assuming, that height >= max width
0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0 );
for( unsigned char c = 0; c < 128; ++c ){
pango_layout_set_text( layout, reinterpret_cast<const char*>( &c ), 1 );
pango_layout_get_extents( layout, NULL, &log_rect );
if ( log_rect.width > 0 && log_rect.height > 0 ) {
bitmap.rows = PANGO_PIXELS_CEIL( log_rect.height );
bitmap.width = PANGO_PIXELS_CEIL( log_rect.width );
bitmap.pitch = bitmap.width;
unsigned char *boo = (unsigned char *) malloc( bitmap.rows * bitmap.width );
memset( boo, 0, bitmap.rows * bitmap.width );
bitmap.buffer = boo;
bitmap.num_grays = 0xff;
bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
pango_ft2_render_layout_subpixel( &bitmap, layout, -log_rect.x, 0 );
/* convert grayscale byte per pixel to two bytes per pixel (white + alpha) */
unsigned char *buf = (unsigned char *) malloc( bitmap.rows * bitmap.width * 2 );
for( unsigned int i = 0; i < bitmap.rows * bitmap.width; ++i ){
buf[i * 2] = 255;
buf[i * 2 + 1] = boo[i];
}
glTexSubImage2D( GL_TEXTURE_2D,
0,
c % 12 * font_height,
c / 12 * font_height,
bitmap.width,
bitmap.rows,
GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE,
buf );
glNewList( font_list_base + c, GL_COMPILE );
glBegin( GL_QUADS );
const float x0 = 0;
const float x1 = bitmap.width + .01f;
const float y0 = 0;
const float y1 = bitmap.rows + .01f;
const float tx0 = ( c % 12 ) / 12.f;
const float tx1 = ( c % 12 + static_cast<float>( bitmap.width ) / font_height ) / 12.f;
const float ty0 = ( c / 12 ) / 12.f;
const float ty1 = ( c / 12 + static_cast<float>( bitmap.rows ) / font_height ) / 12.f;
glTexCoord2f( tx0, ty1 );
glVertex2f( x0, y0 );
glTexCoord2f( tx0, ty0 );
glVertex2f( x0, y1 );
glTexCoord2f( tx1, ty0 );
glVertex2f( x1, y1 );
glTexCoord2f( tx1, ty1 );
glVertex2f( x1, y0 );
glEnd();
glTranslatef( bitmap.width, 0, 0 );
glEndList();
free( boo );
free( buf );
}
}
glPixelStorei( GL_UNPACK_ALIGNMENT, alignment );
g_object_unref( G_OBJECT( layout ) );
}
pango_font_description_free( font_desc );
ASSERT_MESSAGE( font != 0, "font for OpenGL rendering was not created" );
return new GLFontCallList( font_list_base, font_ascent, font_descent, font_height, fontmap, ft2_context );
return new GLFontCallList( font_list_base, atlas, font_ascent, font_descent, font_height, fontmap, ft2_context );
}

View File

@ -2150,16 +2150,14 @@ void CamWnd::Cam_Draw(){
}
if ( g_camwindow_globals.m_showStats ) {
glRasterPos3f( 1.0f, static_cast<float>( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent(), 0.0f );
glRasterPos3f( 1.0f, static_cast<float>( m_Camera.height ), 0.0f );
extern const char* Renderer_GetStats();
GlobalOpenGL().drawString( Renderer_GetStats() );
StringOutputStream stream;
stream << " | f2f: " << m_render_time.elapsed_msec();
stream << Renderer_GetStats() << " | f2f: " << m_render_time.elapsed_msec();
GlobalOpenGL().drawString( stream.c_str() );
m_render_time.start();
glRasterPos3f( 1.0f, static_cast<float>( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent() - GlobalOpenGL().m_font->getPixelHeight(), 0.0f );
glRasterPos3f( 1.0f, static_cast<float>( m_Camera.height ) - GlobalOpenGL().m_font->getPixelHeight(), 0.0f );
extern const char* Cull_GetStats();
GlobalOpenGL().drawString( Cull_GetStats() );
}

View File

@ -467,6 +467,7 @@ class CellPos
{
const int m_cellSize; //half size of model square (radius)
const int m_fontHeight;
const int m_fontDescent;
const int m_plusWidth; //pre offset on the left
const int m_plusHeight; //above text
const int m_cellsInRow;
@ -475,8 +476,9 @@ class CellPos
public:
CellPos( int width, int cellSize, int fontHeight )
: m_cellSize( cellSize ), m_fontHeight( fontHeight ),
m_fontDescent( GlobalOpenGL().m_font->getPixelDescent() ),
m_plusWidth( 8 ),
m_plusHeight( 4 ),
m_plusHeight( 0 ),
m_cellsInRow( std::max( 1, ( width - m_plusWidth ) / ( m_cellSize * 2 + m_plusWidth ) ) ){
}
void operator++(){
@ -492,7 +494,7 @@ public:
}
Vector3 getTextPos( int index ) const {
const int x = ( index % m_cellsInRow ) * m_cellSize * 2 + ( index % m_cellsInRow + 1 ) * m_plusWidth;
const int z = ( index / m_cellsInRow ) * m_cellSize * 2 + ( index / m_cellsInRow + 1 ) * ( m_fontHeight + m_plusHeight ) - 1;
const int z = ( index / m_cellsInRow ) * m_cellSize * 2 + ( index / m_cellsInRow + 1 ) * ( m_fontHeight + m_plusHeight ) - 1 + m_fontDescent;
return Vector3( x, 0, -z );
}
Vector3 getTextPos() const {

View File

@ -4557,7 +4557,7 @@ public:
if( m_points[i].m_set ){
m_points[i].m_p.vertex = vertex3f_for_vector3( m_points[i].m_point );
renderer.addRenderable( m_points[i], g_matrix4_identity );
const Vector3 pos = vector4_projected( matrix4_transformed_vector4( proj, Vector4( m_points[i].m_point, 1 ) ) ) + Vector3( 3, 4, 0 );
const Vector3 pos = vector4_projected( matrix4_transformed_vector4( proj, Vector4( m_points[i].m_point, 1 ) ) ) + Vector3( 2, 0, 0 );
m_points[i].m_namePos = vector4_projected( matrix4_transformed_vector4( proj_inv, Vector4( pos, 1 ) ) );
}
}

View File

@ -421,7 +421,7 @@ int current_x, current_y, current_row;
void Texture_StartPos( TextureLayout& layout ){
layout.current_x = 8;
layout.current_y = -8;
layout.current_y = -4;
layout.current_row = 0;
}
@ -432,7 +432,7 @@ void Texture_NextPos( TextureBrowser& textureBrowser, TextureLayout& layout, qte
textureBrowser.getTextureWH( q, nWidth, nHeight );
if ( layout.current_x + nWidth > textureBrowser.width - 8 && layout.current_row ) { // go to the next row unless the texture is the first on the row
layout.current_x = 8;
layout.current_y -= layout.current_row + TextureBrowser_fontHeight( textureBrowser ) + 5;//+4
layout.current_y -= layout.current_row + TextureBrowser_fontHeight( textureBrowser ) + 1;//+4
layout.current_row = 0;
}
@ -1033,7 +1033,8 @@ void TextureBrowser_Selection_MouseUp( TextureBrowser& textureBrowser, guint32 f
*/
void Texture_Draw( TextureBrowser& textureBrowser ){
const int fontHeight = TextureBrowser_fontHeight( textureBrowser );
int originy = TextureBrowser_getOriginY( textureBrowser );
const int fontDescent = GlobalOpenGL().m_font->getPixelDescent();
const int originy = TextureBrowser_getOriginY( textureBrowser );
glClearColor( textureBrowser.color_textureback[0],
textureBrowser.color_textureback[1],
@ -1173,10 +1174,10 @@ void Texture_Draw( TextureBrowser& textureBrowser ){
glEnd();
// draw the texture name
glDisable( GL_TEXTURE_2D );
// glDisable( GL_TEXTURE_2D );
// glColor3f( 1, 1, 1 ); //already set
glRasterPos2i( x, y - fontHeight + 3 );//+5
glRasterPos2i( x, y - fontHeight - fontDescent + 3 );//+5
// don't draw the directory name
const char* name = shader->getName();

View File

@ -1456,15 +1456,17 @@ void XYWnd::XY_DrawAxis( void ){
glEnd();
glLineWidth( 1 );
// now print axis symbols
const int fontHeight = GlobalOpenGL().m_font->getPixelHeight();
const float fontWidth = fontHeight * .55f;
glColor3fv( vector3_to_array( colourX ) );
glRasterPos2f( m_vOrigin[nDim1] - w + 55 / m_fScale, m_vOrigin[nDim2] + h - 55 / m_fScale );
glRasterPos2f( m_vOrigin[nDim1] - w + ( 65 - 3 - fontWidth ) / m_fScale, m_vOrigin[nDim2] + h - ( 45 + 3 + fontHeight ) / m_fScale );
GlobalOpenGL().drawChar( g_AxisName[nDim1] );
glRasterPos2f( 28 / m_fScale, -10 / m_fScale );
glRasterPos2f( ( 32 - fontWidth / 2 ) / m_fScale, -( 0 + 3 + fontHeight ) / m_fScale );
GlobalOpenGL().drawChar( g_AxisName[nDim1] );
glColor3fv( vector3_to_array( colourY ) );
glRasterPos2f( m_vOrigin[nDim1] - w + 25 / m_fScale, m_vOrigin[nDim2] + h - 30 / m_fScale );
glRasterPos2f( m_vOrigin[nDim1] - w + ( 40 - 4 - fontWidth ) / m_fScale, m_vOrigin[nDim2] + h - ( 20 + 3 + fontHeight ) / m_fScale );
GlobalOpenGL().drawChar( g_AxisName[nDim2] );
glRasterPos2f( -10 / m_fScale, 28 / m_fScale );
glRasterPos2f( ( 0 - 3 - fontWidth ) / m_fScale, ( 32 - fontHeight / 2 ) / m_fScale );
GlobalOpenGL().drawChar( g_AxisName[nDim2] );
}
@ -1615,15 +1617,16 @@ void XYWnd::XY_DrawGrid( void ) {
// draw coordinate text if needed
if ( g_xywindow_globals_private.show_coordinates ) {
glColor4fv( vector4_to_array( Vector4( g_xywindow_globals.color_gridtext, 1.0f ) ) );
const float offx = m_vOrigin[nDim2] + h - ( 4 + GlobalOpenGL().m_font->getPixelAscent() ) / m_fScale;
const float offx = m_vOrigin[nDim2] + h - ( 1 + GlobalOpenGL().m_font->getPixelHeight() ) / m_fScale;
const float offy = m_vOrigin[nDim1] - w + 4 / m_fScale;
const float fontDescent = ( GlobalOpenGL().m_font->getPixelDescent() - 1 ) / m_fScale;
for ( x = xb - fmod( xb, stepx ); x <= xe ; x += stepx ) {
glRasterPos2f( x, offx );
sprintf( text, "%g", x );
GlobalOpenGL().drawString( text );
}
for ( y = yb - fmod( yb, stepy ); y <= ye ; y += stepy ) {
glRasterPos2f( offy, y );
glRasterPos2f( offy, y - fontDescent );
sprintf( text, "%g", y );
GlobalOpenGL().drawString( text );
}
@ -1635,7 +1638,7 @@ void XYWnd::XY_DrawGrid( void ) {
}
else{
glColor3fv( vector3_to_array( Active()? g_xywindow_globals.color_viewname : g_xywindow_globals.color_gridtext ) );
glRasterPos2f( m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale );
glRasterPos2f( m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - ( GlobalOpenGL().m_font->getPixelHeight() * 2 ) / m_fScale );
GlobalOpenGL().drawString( ViewType_getTitle( m_viewType ) );
}
@ -1808,22 +1811,24 @@ void XYWnd::PaintSizeInfo( const int nDim1, const int nDim2 ){
glVertex3fv( vector3_to_array( v ) );
glEnd();
const int fontHeight = GlobalOpenGL().m_font->getPixelHeight();
v[nDim1] = mid[nDim1];
v[nDim2] = min[nDim2] - 20.f / m_fScale;
v[nDim2] = min[nDim2] - ( 10 + 2 + fontHeight ) / m_fScale;
glRasterPos3fv( vector3_to_array( v ) );
dimensions << dimStrings[nDim1] << size[nDim1];
GlobalOpenGL().drawString( dimensions.c_str() );
dimensions.clear();
v[nDim1] = max[nDim1] + 16.f / m_fScale;
v[nDim2] = mid[nDim2];
v[nDim2] = mid[nDim2] - fontHeight / m_fScale / 2;
glRasterPos3fv( vector3_to_array( v ) );
dimensions << dimStrings[nDim2] << size[nDim2];
GlobalOpenGL().drawString( dimensions.c_str() );
dimensions.clear();
v[nDim1] = min[nDim1] + 4.f;
v[nDim2] = max[nDim2] + 8.f / m_fScale;
v[nDim2] = max[nDim2] + 5.f / m_fScale;
glRasterPos3fv( vector3_to_array( v ) );
dimensions << "(" << dimStrings[nDim1] << min[nDim1] << " " << dimStrings[nDim2] << max[nDim2] << ")";
GlobalOpenGL().drawString( dimensions.c_str() );
@ -2112,13 +2117,10 @@ void XYWnd::XY_Draw(){
glColor3fv( vector3_to_array( g_xywindow_globals.color_viewname ) );
glRasterPos3f( 2.f, GlobalOpenGL().m_font->getPixelDescent() + 1.f, 0.0f );
glRasterPos3f( 2.f, 0.f, 0.0f );
extern const char* Renderer_GetStats();
GlobalOpenGL().drawString( Renderer_GetStats() );
//globalOutputStream() << m_render_time.elapsed_msec() << "\n";
StringOutputStream stream;
stream << " | f2f: " << m_render_time.elapsed_msec();
stream << Renderer_GetStats() << " | f2f: " << m_render_time.elapsed_msec();
GlobalOpenGL().drawString( stream.c_str() );
m_render_time.start();
}