Advertisement

Creating a Font Atlas

Started by April 03, 2019 10:56 PM
5 comments, last by datboi 5 years, 10 months ago

Not too long ago I made a thread requesting help with rendering fonts in FreeType. After a while, I got everything to work perfectly and had a pretty good font renderer. The problem that I ignored for a while was that I was making a texture for each glyph, which made the program's memory usage insanely high. Today I decided to just create a single texture instead, and place each glyph in a single section (font atlas). Unfortunately, I'm getting terrible results and haven't figured this out in the past 6 hours I've been working on it.

xUv2BcZ.png

Above is the result when I draw the entire texture.. I'm not sure exactly why this happens, I am using the same code I was before to rasterize the glyphs into the texture.


case FT_PIXEL_MODE_GRAY:
		{
			for ( int j = 0; j < glyph_h; j++ )
			{
				uchar *psrc = bitmap->buffer + ( j * bitmap->pitch );

				for ( int i = 0; i < glyph_w; i++ )
				{
					uchar *pdst = dst + ( 4 * i + j * stride );

					*pdst++ = 0xFF;
					*pdst++ = 0xFF;
					*pdst++ = 0xFF;
					*pdst++ = *psrc++;
				}				    
			}

			break;
		}

The only difference is how it is loaded into the buffer (which just adds the position "(y * texture_size) + x")


// dst - a unsigned char buffer
// x/y - the current x/y position in the texture 
ft_RenderGlyphToBuffer( dst + ( y * texsize ) + x );

Is my logic or approach wrong here? Appreciate any help, thank you.

I'm not sure this is enough information to really you. There could be bugs elsewhere. It does look like perhaps your color components are flipped, and it also looks like either incorrect UVs, and/or incorrect stride when copying pixels.

When I had similar problems I would first verify which pieces are absolutely known to be correct, and try to isolate problems. Is your atlas constructed properly, and can you open it in an image editor? Can you copy out one image from your atlas into an isolated image, and open it properly in an image editor? Can you copy out one image from your atlas, and then render it on screen?

Advertisement

Is that how it looks in the graphics debugger?

 

Also you can use a single channel texture as your using single colour fonts and not doing subpixel.

 

 

31 minutes ago, Randy Gaul said:

I'm not sure this is enough information to really you. There could be bugs elsewhere. It does look like perhaps your color components are flipped, and it also looks like either incorrect UVs, and/or incorrect stride when copying pixels.

When I had similar problems I would first verify which pieces are absolutely known to be correct, and try to isolate problems. Is your atlas constructed properly, and can you open it in an image editor? Can you copy out one image from your atlas into an isolated image, and open it properly in an image editor? Can you copy out one image from your atlas, and then render it on screen?

At this point, I believe it has to be the function "ft_RenderGlyphToBuffer".. I was using this function perfectly fine before when I was creating a texture for every glyph rather than creating one for all.

31 minutes ago, Randy Gaul said:

Can you copy out one image from your atlas, and then render it on screen?

Not sure what you mean. I was rendering the entire atlas onto the screen. All of them are skewed.
 


VDisplayImage dimg( /*font atlas texture*/ m_pTexture, /*area*/ FloatRect( 0, 0, m_lTexSize, m_lTexSize ), /*offset*/ FloatVector2( 0, 0 ) );
dimg.Display( 0, 0, 1280, 720, CRgbaColor::Intensity( 255 ) );

This resulted in the picture above.

19 minutes ago, SyncViews said:

Is that how it looks in the graphics debugger?

I am drawing them with DirectX 9.

 

--- Edit

I should note that this is how I am storing each glyph into the atlas


int gw = bitmap->width + GLYPH_PADDING;
int gh = bitmap->rows + GLYPH_PADDING;

// Check if glyph right margin does not exceed texture size
uint x_next = x + gw;
if ( x_next > texsize )
{
	x = GLYPH_PADDING;
	x_next = x + gw;
	y = yb;
}

// Check if glyph bottom margin does not exceed texture size
uint y_bot = y + gh;
if ( y_bot > texsize )
	break;


ft_RenderGlyphToBuffer( dst + ( y * texsize ) + x, texsize );

FloatRect area
(
	static_cast< float >( x ),
	static_cast< float >( y ),
	static_cast< float >( gw - GLYPH_PADDING ),
	static_cast< float >( gh - GLYPH_PADDING )
);
FloatVector2 offset
(
	glyphit->second.offsetX,
	-glyphit->second.offsetY
);

glyphit->second.image = new VDisplayImage( m_pTexture, area, offset );

// Advance to next position
x = x_next;
if ( y_bot > yb )
{
	yb = y_bot;
}

 

33 minutes ago, datboi said:

This resulted in the picture above.

51 minutes ago, SyncViews said:

Is that how it looks in the graphics debugger?

I am drawing them with DirectX 9.

Well check with VS/PIX just in case. Its really strange that you have multiple colours in that image when you wrote 0xFF to 3 of 4 bytes.

 

EDIT: You might also step through in the debugger to make sure say a line or two gets rendered properly. Should be able to watch something like "(unsigned*)p,[100]".

1 hour ago, SyncViews said:

Well check with VS/PIX just in case. Its really strange that you have multiple colours in that image when you wrote 0xFF to 3 of 4 bytes.

 

EDIT: You might also step through in the debugger to make sure say a line or two gets rendered properly. Should be able to watch something like "(unsigned*)p,[100]".

Its 100% due to the fact that this code is wrong (for this scenario atleast)


case FT_PIXEL_MODE_GRAY:
{
	for ( int j = 0; j < glyph_height; j++ )
	{
		uchar *psrc = bitmap->buffer + ( j * bitmap->pitch );

		for ( int i = 0; i < glyph_width; i++ )
		{
                        // stride = texture_size * 4
			uchar *pdst = buffer + ( 4 * i + j * stride );

			*pdst++ = 0xFF;
			*pdst++ = 0xFF;
			*pdst++ = 0xFF;
			*pdst++ = *psrc++;
		}
	}  
	break;
}

It worked for when I used single textures for each glyph, but now that I use one texture, it screws up. I'm not sure why, I thought that accounting for it by using


texture_buffer + ( y * texture_size ) + x

as the parameter for 'buffer' would be enough to allow this code to still work. I'm not sure how it doesn't... but I am very tired so I will take a look at this again tomorrow.

Thanks.

This topic is closed to new replies.

Advertisement