CreateFont + GetGlyphOutline
-
Hallo,
folgendes Problem:
Mittles CreateFont erstelle ich mir einen TrueType-Font. Von diesem möchte ich mir die Glyphmetrics zurückgeben lassen.
In der Schleife sammele ich die GLYPHMETRICS-Objekte und packe sie in ein Array.GLYPHMETRICS gmf[256]; int pPointSize = 8; HFONT font = CreateFont(-MulDiv(pPointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE | DEFAULT_PITCH, TEXT("Tahoma")); HFONT oldfont = (HFONT)SelectObject(hDC, font); for (int i = 0; i < 256; i++){ GetGlyphOutline(hDC, i, GGO_METRICS, &gmf[i], NULL, NULL, NULL); }
Wenn ich mir allerdings den Inhalt von gmf im Debugger ansehe, sind alle GLYPHMETRICS-Elemente bzw. deren Member
mit 0 initialisiert. Scheinbar werden die Metrics nicht richtig ausgelesen.Hat jemand eine Idee, woran das liegen könnte bzw. was man ändern könnte?
grüße
Martin
-
Kann dir da nur raten dir mal den freetype source anzuschaun.
-
Danke für den Tip, mal sehen, was da so abgeht.
Vielleicht noch mein Anliegen:
Ich möchte zu einem String die maximale Höhe und die Länge errechnen, abhängig vom gewählten Font plus Größe.Z.B:
"Hello World", 8pt, Arial => 53 Pixel lang, 8 Pixel hoch.Habs halt über die GLYPHMETRICS probiert, aber scheinbar will das nicht. Die GDI ist aber auch so unstrukturiert und schlecht dokumentiert, dass mir bis jetzt noch keine Lösung aufgefallen ist.
gruß
Martin
-
ich hab dass selbst noch nicht gemacht aber ich hätte zuhause code der das erledigt, den kann ich dir nachher nachm schaffen ausgraben.
-
Im Spiele Forum isn recht aktueller Thread. Zwar im OpenGL context aber vielleicht hilfts.
http://www.c-plusplus.net/forum/viewtopic-var-p-is-1342077.html#1342077
-
@mad_martin
Ich empfehle dir mal die Rückgabewerte der einzelnen Funktionen zu prüfen. Bei GetGlyphOutline auf GDI_ERROR überprüfen und ggf. GetLastError aufzurufen. Denke mal, dass es am 2. Parameter von GetGlyphOutline liegen wird. Hier wird ein 'Zeichen' verlangt. Allerdings sind nicht alle Zeichen darstellbar (Steuerzeichen), wie z.B. '\0' welches den Wert 0 hat (spiele damit auf deine for-Schleife an).Die Breite und Höhe einer Zeichenkette, abhängig vom 'gewählten' Font bekommst du über die Funktion GetTextExtentPoint32
-
So, habs ausgegraben, zwar auch für opengl und bisl mehr als nötig aber ich dachte ich poste mal den ganzen source, may freut sich jemand
Is auf jeden funktionierender Sample Source, kann leider wenig zu Tribes.h und opengl.h sagen aber ich geh davon aus das die uninteressant sind und opengl.h etc. nur gewrappte funktionalität für genau dieses bietet da der code in nem reverse engeneered sourrounding läuft um das beste game der welt n bisschen aufzupeppenHab den Code selbst nicht nachvollzogen aber wenn er ein bisschen komisch wirken mag liegt dass daran das die Font Funktionen in die Scripting Oberflche von Starsiege::Tribes eingebunden wurde und deswegen eventuell noch zusätzlichen weirdo kram enthält.
and for the guys that find this via google. THIS IS NO TRIBES SOURCE CODE! It's part of andrews scriptgl extension in hudbot 0.5
.cpp
#include "Tribes.h" #include "Font.h" #include "OpenGL.h" #include "Profile.h" #pragma warning( disable : 4244 ) // conversion from 'double' to 'float', possible loss of data void Font::Close( ) { if ( !mCreatedDC ) return; DeleteObject( mFont ); DeleteObject( mDib ); DeleteDC( mDC ); mDC = ( NULL ); mFont = ( NULL ); mDib = ( NULL ); mLoaded = ( false ); } // get the font information and create windows handles bool Font::Create( const char *name, int pixel_height, Rendering mode, int glow_radius ) { Close( ); // rendering mMode = ( mode ); mGlowAdd = ( glow_radius > 16 ) ? 16 : glow_radius; mBlurRadius = ( glow_radius ); mDC = ( CreateCompatibleDC( NULL ) ); if ( !mDC ) return ( false ); SetMapMode( mDC, MM_TEXT ); mFont = CreateFont( -pixel_height, 0, /* width */ 0, /* escapement */ 0, /* orientation */ FW_NORMAL, /* weight */ 0, /* italic */ 0, /* underline */ 0, /* strikeout */ ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, name ); if ( !mFont ) { DeleteDC( mDC ); mDC = ( NULL ); return ( false ); } else { SelectObject( mDC, mFont ); } // Font height TEXTMETRIC metric; GetTextMetrics( mDC, &metric ); mHeight = ( metric.tmHeight ); mAscent = ( metric.tmAscent ); mMaxWidth = ( metric.tmMaxCharWidth ); mTrueType = ( ( metric.tmPitchAndFamily & TMPF_TRUETYPE ) != 0 ); // Char widths ABC widths[ 256 ]; if ( !mTrueType || !GetCharABCWidths( mDC, 0, 255, widths ) ) { int widths2[ 256 ]; GetCharWidth( mDC, 0, 255, widths2 ); for ( int i = 0; i < 256; i++ ) { widths[ i ].abcA = ( 0 ); widths[ i ].abcB = ( widths2[ i ] ); widths[ i ].abcC = ( 0 ); } } // DibSection mDibSize = Texture::CeilPow2( max( mMaxWidth, mHeight ) ); BITMAPINFO header; memset( &header, 0, sizeof( header ) ); header.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); header.bmiHeader.biWidth = ( mDibSize ); header.bmiHeader.biHeight = -( mDibSize ); // negative means top down header.bmiHeader.biPlanes = ( 1 ); header.bmiHeader.biBitCount = ( 32 ); header.bmiHeader.biCompression = ( BI_RGB ); mDib = CreateDIBSection( mDC, &header, DIB_RGB_COLORS, (void **)&mDibPixels, 0, 0 ); SelectObject( mDC, mDib ); // Color SetTextColor( mDC, (COLORREF )0x00FFFFFF ); SetBkColor( mDC, (COLORREF )0x00000000 ); SetBkMode( mDC, TRANSPARENT ); // Init character widths for ( int i = 0, x = 0, y = 0; i < 256; i++ ) { Letter &l = ( mLetters[ i ] ); ABC &abc = ( widths[ i ] ); l.mWidth = LetterWidth( abc.abcA, abc.abcB, abc.abcC ); } mCreatedDC = ( true ); mLoaded = ( true ); return ( true ); } // render a letter for use bool Font::CreateLetter( Letter &l, char ch ) { if ( l.mTexture.Loaded( ) ) return ( true ); // dont draw these if ( ch < 31 || ch > 126 || ( l.GlyphWidth( ) <= 0 ) ) return ( false ); PROFILE_START( ) if ( mMode == Smooth ) RenderCharacterSmooth( l, ch ); else RenderCharacter( l, ch ); if ( mGlowAdd ) l.mTexture.AlphaBloom( mBlurRadius, l.mWidth.mB, mHeight ); PROFILE_STOP( ) return ( true ); } void Font::Draw( const char *str, int x, int y ) { if ( !mLoaded ) return; PROFILE_START( ) glDisable( GL_ALPHA_TEST ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); Vector2i start( x, y ); RGBA color( 255, 255, 255, 255 ); while ( *str ) { unsigned char ch = ( *str++ ); if ( ch == '\n' ) { x = ( start.x ); y += ( mHeight ); continue; } else if ( ch == '<' ) { if ( Tribes::Atoh( str, (char *)&color, 4 ) == 4 ) { str += ( 8 ); if ( *str ) str++; glColor4ub( color.r, color.g, color.b, color.a ); continue; } } Letter &l = ( mLetters[ ch ] ); if ( CreateLetter( l, ch ) ) { x += ( l.mWidth.mA ); l.Draw( x, y, mHeight, mGlowAdd ); x += ( l.mWidth.mB + l.mWidth.mC ); } } PROFILE_STOP( ) } /* Renders a character with no smoothing */ void Font::RenderCharacter( Letter &l, char ch ) { if ( !l.CreateTexture( mHeight, mGlowAdd ) ) return; memset( mDibPixels, 0, mDibSize * mDibSize * 4 ); TextOut( mDC, -l.mWidth.mA, 0, (char *)&ch, 1 ); for ( int y = 0; y < mHeight; y++ ) { for ( int x = 0; x < l.GlyphWidth( ); x++ ) { RGBA &quad = ( mDibPixels[ x + y * mDibSize ] ); if ( quad.r ) quad.a = 255; else quad = ( RGBA( 255, 255, 255, 0 ) ); l.mTexture( x + mGlowAdd, y + mGlowAdd ) = ( quad ); } } } /* Attempts to create an AA character with GetGlyphOutline */ void Font::RenderCharacterSmooth( Letter &l, char ch ) { GLYPHMETRICS metrics; MAT2 mat2 = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; int bytes = ( GetGlyphOutline( mDC, ch, GGO_GRAY8_BITMAP, &metrics, 0, NULL, &mat2 ) ); if ( bytes == 0 ) { // fallback to non-aa RenderCharacter( l, ch ); return; } unsigned char *gray = new unsigned char[ bytes ]; GetGlyphOutline( mDC, ch, GGO_GRAY8_BITMAP, &metrics, bytes, gray, &mat2 ); // character dimensions may change with anti-aliasing int pad = ( metrics.gmBlackBoxX - l.mWidth.mB ), half = ( pad >> 1 ); l.mWidth.mA -= ( pad - half ); l.mWidth.mB = ( metrics.gmBlackBoxX ); l.mWidth.mC -= ( half ); // resize the texture, it wont fail! l.CreateTexture( mHeight, mGlowAdd ); int width = ( metrics.gmBlackBoxX + 3 ) & ~3; // dword aligned int start_y = ( mAscent - metrics.gmptGlyphOrigin.y ); int start_x = 0; for ( unsigned int j = 0; j < metrics.gmBlackBoxY; j++ ) { for ( unsigned int i = start_x; i < metrics.gmBlackBoxX; i++ ) { int x = ( i - start_x ) + mGlowAdd; int y = ( j + start_y ) + mGlowAdd; if ( ( x >= l.mTexture.mWidth ) || ( y >= l.mTexture.mHeight ) ) continue; // format is 64 shades of gray l.mTexture( x, y ) = RGBA( 255, 255, 255, gray[ ( j * width ) + i ] * 255 / 64 ); } } delete[] gray; } Vector2i Font::StringDimensions( const char *str ) { Vector2i dimensions( 0, mHeight ); if ( !mLoaded ) return ( dimensions ); int x = 0; while ( *str ) { unsigned char ch = ( *str++ ); if ( ch == '\n' ) { dimensions.y += ( mHeight ); continue; } else if ( ch == '<' ) { RGBA color; if ( Tribes::Atoh( str, (char *)&color, 4 ) == 4 ) { str += ( 8 ); if ( *str ) str++; continue; } } Letter &l = ( mLetters[ ch ] ); x += ( l.mWidth.mA + l.mWidth.mB + l.mWidth.mC ); dimensions.x = ( max( dimensions.x, x ) ); } return ( dimensions ); }
.h
#ifndef __FONT_H__ #define __FONT_H__ #include "Texture.h" #include "OpenGL.h" #include "Profile.h" class LetterWidth { public: LetterWidth() {} LetterWidth( int a, int b, int c ) : mA(a), mB(b), mC(c) {} int mA, mB, mC; }; class Letter { public: Letter( ) { } // letter loads itself to opengl on demand void Draw( int x, int y, int font_height, int glow_radius ) { PROFILE_START( ) if ( !mTexture.Loaded( ) || !mTexture.BindToGraphicsCard( ) ) { PROFILE_STOP( ) return; } x -= ( glow_radius ); y -= ( glow_radius ); int glow = ( glow_radius * 2 ); int width = ( mWidth.mB + glow ); int height = ( font_height + glow ); glBegin( GL_QUADS ); glTexCoord2f( 0, 0 ); glVertex2i( x, y ); glTexCoord2f( mExtent.x, 0 ); glVertex2i( x + width, y ); glTexCoord2f( mExtent.x, mExtent.y ); glVertex2i( x + width, y + height ); glTexCoord2f( 0, mExtent.y ); glVertex2i( x, y + height ); glEnd( ); PROFILE_STOP( ) /* glDisable( GL_TEXTURE_2D ); glBegin( GL_LINE_STRIP ); glVertex2f( x, y ); glVertex2f( x + width, y ); glVertex2f( x + width, y + height ); glVertex2f( x, y + height ); glVertex2f( x, y ); glEnd( ); glEnable( GL_TEXTURE_2D ); */ } // size of actual character, not trailing/leading rendering space int GlyphWidth( ) const { return ( mWidth.mB ); } bool CreateTexture( int font_height, int glow_radius ) { int width = ( GlyphWidth( ) + glow_radius * 2 ); int height = ( font_height + glow_radius * 2 ); mTexture.New( Texture::CeilPow2( width ), Texture::CeilPow2( height ) ); if ( !mTexture.Loaded( ) ) return ( false ); // clear to pure white, no alpha mTexture.Clear( 255, 255, 255, 0 ); mExtent.x = ( (float )width / (float )mTexture.mWidth ); mExtent.y = ( (float )height / (float )mTexture.mHeight ); return ( true ); } Texture mTexture; Vector2f mExtent; LetterWidth mWidth; }; class Font { public: enum Rendering { Pixel, Smooth, }; Font( ) : mLoaded(false), mLastUsed(GetTickCount()), mCreatedDC(false) { } ~Font( ) { Close( ); } void Close( ); bool Create( const char *name, int pixel_height, Rendering mode, int glow_radius ); void Draw( const char *str, int x, int y ); int LastUsed( ) { return ( mLastUsed ); } Vector2i StringDimensions( const char *str ); void UpdateLastUsed( ) { mLastUsed = ( GetTickCount( ) ); } private: bool CreateLetter( Letter &l, char ch ); void RenderCharacter( Letter &l, char ch ); void RenderCharacterSmooth( Letter &l, char ch ); // font info Letter mLetters[ 256 ]; int mHeight; bool mLoaded; int mLastUsed; // only for rendering int mGlowAdd, mBlurRadius; Rendering mMode; bool mCreatedDC; RGBA *mDibPixels; HDC mDC; HFONT mFont; HBITMAP mDib; int mDibSize, mAscent, mMaxWidth; bool mTrueType; }; #endif // __FONT_H__
-
Vergiss den Kram mit OpenGL und verwende Analog Bit's Vorschlag: GetTextExtentPoint32 bzw. GetTabbedTextExtent. Dafür sind diese Funktionen schließlich da
.
-
Danke an alle. Werde das jetzt mal probieren.
grüße
Martin
-
Yeah, passt. Macht genau das, was ich will. Danke nochmal.
@Analog-Bit:
Zu der Schleife meines ersten Versuchs:
Das es bei einzelnen Zeichen Probleme gibt, vor allem bei den ersten 30, war mir klar. Aber selbst wenn ich die Steuerzeichen rauslasse und nur z.B. das normale Alphabet nutze, kommt nichts dabei rum. Ist aber jetzt auch egal@Greyhound:
Woher genau kommt das denn? Ein Mod für Tribes?gruß
Martin
-
mad_martin schrieb:
@Greyhound:
Woher genau kommt das denn? Ein Mod für Tribes?http://www.team5150.com/~andrew/project.hudbot/
Is ne dll die von tribes mitgeladen wird und support für 32 bit texturen anbietet etc. und unter anderem auch ein paar gl functionen freigibt sodass man sich huds usw. auf diese art zeichnen kann.
-
Aha. Das werd ich mir mal anschauen.
gruß
Martin