Jump to content
  • Advertisement
datboi

2D [DX9] Displaying font loaded by FreeType

Recommended Posts

On 1/17/2019 at 3:46 PM, ddlox said:

~snip~

Hi.

I didn't see your reply, but I had some extra time to work on it today. Pretty happy I fixed some of the issues I was having but still have some slight problems.

hOpI7qH.png

As you can see it seems to be rendering fine until the letter 'r'... Not sure why this is happening. Especially how the letter 'g' seems to be clipping out on the top.

float CDisplayFont::DisplayText( const char * szText, int textCount, const SVector2f& pos, const SColorRect & colors, const SRectf * pClipRect )
{
	SVector2f tmp ( pos.x, pos.y );	  

	IRender * render = g_pCore->GetGraphics()->GetRender();
	ISurface * surface = g_pCore->GetGraphics()->GetSurface();
	
	float sh = 0.f;

	for ( int i = 0; i < textCount; ++i )
	{
		const Codepoint_t cp = static_cast< Codepoint_t >( szText[ i ] );
		if ( GlyphInfo_t * glyph = GetGlyphInfo( cp ) )
		{
			float sx = tmp.x + glyph->offsetX * m_fScaleHoriz;	   
			/**
			- m_fMaxYOffset is actually the y bearing of character 'T'
			- (0,0) is the top left of the screen so this is the "correct" way to get the y coordinate
			**/
			float sy = tmp.y + ( m_fMaxYOffset - glyph->offsetY ) * m_fScaleVert;

			float w	 = glyph->width * m_fScaleHoriz;
			float h  = glyph->height * m_fScaleVert;
			
			float tx1 = glyph->texcoords[ 0 ]; // left	  
			float tx2 = glyph->texcoords[ 2 ]; // right
			float ty1 = glyph->texcoords[ 1 ]; // top
			float ty2 = glyph->texcoords[ 3 ]; // bottom
	    		  
			if ( szText[ i ] != ' ' )
			{   
				// only using to get the height for drawing the line below..
				if ( sh == 0.f )
					sh = h;

				SVertex vtx[ ] =
				{
					{ ( sx ),     ( sy + h ), 0.0f, 1.f, colors.BottomLeft, tx1, ty2 },
					{ ( sx + w ), ( sy ),     0.0f, 1.f, colors.TopRight,   tx2, ty1 },
					{ ( sx ),	  ( sy ),	  0.0f, 1.f, colors.TopLeft,    tx1, ty1 },

					{ ( sx ),	  ( sy + h ), 0.0f, 1.f, colors.BottomLeft,	tx1, ty2 },
					{ ( sx + w ), ( sy + h ), 0.0f, 1.f, colors.BottomRight,tx2, ty2 },
					{ ( sx + w ), ( sy ),     0.0f, 1.f, colors.TopRight,	tx2, ty1 }
				};						
 
				//   arguments ->    ( rl, vertex data, vertex count, topology, texture (IDirect3DTexture9) )
				render->PushVertices( NULL, vtx, 6, D3DPT_TRIANGLELIST, glyph->texture->GetInternalPtr() );	  
			}	 
			 
			// advance the x position
			tmp.x += static_cast< float >( glyph->advanceX >> 6 ) * m_fScaleHoriz;	
		}
	}

	// just testing, sh seems to be correct lol
	surface->DrawLine( pos.x, pos.y + sh, tmp.x, tmp.y + sh );

	return tmp.x;
}


Here is how I am loading each character

bool CDisplayFont::LoadCodepoint( Codepoint_t cp, GlyphInfo_t * out )
{
	FT_Error err = 0;
	if ( ( err = FT_Load_Char( m_fontFace, cp, FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT ) ) != FT_Err_Ok )
	{														 
		g_pCore->GetConsole()->Errorf( "Error loading codepoint %d: %d\n", cp, err );
		return false;
	}

	out->width = m_fontFace->glyph->bitmap.width;
	out->height = m_fontFace->glyph->bitmap.rows;
	out->offsetX = m_fontFace->glyph->bitmap_left;
	out->offsetY = m_fontFace->glyph->bitmap_top;
	out->advanceX = m_fontFace->glyph->advance.x;
	out->advanceY = m_fontFace->glyph->advance.y;
	out->texture = NULL; // we're not creating the texture yet

	float x0 = out->offsetX;
	float y0 = out->height - out->offsetY;
	float x1 = x0 + out->width;
	float y1 = y0 + out->height;
	float itw = 1.f / static_cast< float >( m_lTexSize );
	float ith = 1.f / static_cast< float >( m_lTexSize );

	out->texcoords[ 0 ] = x0 * itw; // left
	out->texcoords[ 2 ] = x1 * itw; // right

	out->texcoords[ 1 ] = y0 * ith; // top
	out->texcoords[ 3 ] = y1 * ith; // bottom

	return true;
}



Still pretty satisfied, considering just 1 hour ago, only weird small blocks were being displayed on the screen haha.

Appreciate any help.

 

Share this post


Link to post
Share on other sites
Advertisement

Variations in thickness often result from numerical errors in scaling. Please make 110% sure you're not scaling anything, from font description to bitmap, or from bitmap to screen. One way is to get a handle on it is to dump the bitmap you get from the font as rows of text characters, and use a non-proportional font to check. EDIT: You may want to disable anti-aliasing here.

The "g" is drawn incorrectly. The full circle of the letter should rest at the baseline, and the extension at the bottom should be below the baseline. Just look how the "g" is rendered here relative to the other text. Not sure of the details of FreeType any more, but iirc you could have a negative offset wrt to the baseline if you had to start below it. As you can see at https://www.freetype.org/freetype2/docs/tutorial/step2.html the bottom of the letter can be below "origin"

Edited by Alberth

Share this post


Link to post
Share on other sites
On 1/20/2019 at 5:37 AM, Alberth said:

The "g" is drawn incorrectly. The full circle of the letter should rest at the baseline, and the extension at the bottom should be below the baseline. Just look how the "g" is rendered here relative to the other text. Not sure of the details of FreeType any more, but iirc you could have a negative offset wrt to the baseline if you had to start below it. As you can see at https://www.freetype.org/freetype2/docs/tutorial/step2.html the bottom of the letter can be below "origin"

Yes, I realize that.

Hence the code here
 

float sy = tmp.y + ( m_nMaxYOffset - glyph->offsetY ) * m_fScaleVert;

And it seems that I fixed the issue with the various widths, until I increased the font size and the same thing happened again.

22 font size
4aba842dab9fe8267ae01d68623ecfea.png

23 font size

d473a47bf02336a1cf91ee8914e160d7.png

 

Someone else had the same issue and it seemed he fixed it by subtracting the offset from the character T's offset.

 

Thanks.

Share this post


Link to post
Share on other sites

The "g" looks like you assume row 0 of the bitmap is always at the baseline, and the bitmap has the same number of rows as the height of the letter. For letters that end below the baseline, this cannot be true.

Instead of randomly trying weird fixes, why not debug your problem? Take the "g", as that's clearly wrong, and look at the numbers you get from the freetype library. Take a piece of paper, and draw where the bitmap box is, according to the documentation and the numbers that you get.

Share this post


Link to post
Share on other sites
21 hours ago, Alberth said:

The "g" looks like you assume row 0 of the bitmap is always at the baseline, and the bitmap has the same number of rows as the height of the letter. For letters that end below the baseline, this cannot be true.

Instead of randomly trying weird fixes, why not debug your problem? Take the "g", as that's clearly wrong, and look at the numbers you get from the freetype library. Take a piece of paper, and draw where the bitmap box is, according to the documentation and the numbers that you get.

I was able to calculate the baseline with this line of code.

// top of the glyph
int yMax = m_face->bbox.yMax;
// bottom of the glyph	
int yMin = m_face->bbox.yMin;

m_nBaseLine = size * yMax / ( yMax - yMin );

After debugging for hours and going to MS Paint to draw it out and make sure my logic was 100% correct render wise, I figured it was probably the texture coords. that were wrong. I am now left with this.

7ad58dee49ce8a263f2fb9bd865fe0ae.png

Note: the line on top of the 'g' is offsetY

Not sure how precise that is but its definitely a step in the right direction! I think there is still some issues with the texture coordinates because I can see some slight clipping, but would appreciate any feedback.

Thanks!

Share this post


Link to post
Share on other sites

I am not sure what you mean with "computing baseline" from a glyph. Normally you pick an Y coordinate as baseline, and use that as base to compute position of all the letter bitmaps. In other words, the other way around.

I'd start with throwing out as much "float" as possible. Bitmap coordinates and font coordinates are all integers, no reason to have floats in there and it eliminates rounding errors. Even in the mapping to screen coordinates, don't scale, only convert the integer pixel coordinates of the corners at the screen to float, since the renderer wants floats.

Letters are specifically designed to be displayed at their original point-size. If you want a bigger or smaller letter, use a different size font. Freetype has anti-aliasing to smooth the edges if you enable it. The renderer can only make things worse if it's not pixel-perfect plotted onto the screen.

As for clipping, hack the bitmap returned by the library, and add a few additional recognizable pixels in the corners. After rendering to the screen take a screenshot, and verify their position.

Share this post


Link to post
Share on other sites

Don't mean to necro this or go off topic, but does anyone have any experience rendering colored emoji's?

I can render emoji's just fine (using char32_t)

image.png.346c879954a55d45439c2a54c8ccf5b9.png

The problem is actually when trying to load a colored font, which I expected this one to be. I've tried 3-4 different fonts that are labeled as colored but FT_HAS_COLOR() seems to always return false on the font..

I am supplying FT_LOAD_COLOR into FT_Load_Char flags, but it doesn't seem to do much. I am expecting pixel_mode to be set to FT_PIXEL_MODE_BGRA as it says in the documentation, but I always get FT_PIXEL_MODE_GRAY.

Any help would be greatly appreciated! Thanks

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!