Sign in to follow this  

Font Rendering

This topic is 3865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm currently working on my own font rendering method, but I have problems getting the font sharp and clear. I'm mapping every character on a seperate Quad, using different texture coordinates, etc.. The Bitmap has no alpha channel, because I just copied some windows font and pasted it to a picture. The scale 'texture pixel' to 'screen pixel' should be 1 to 1 (I'm not using any filter for the font). The alpha value is created by: (pseudo code) diff = color_at_pixel; rgb = 1 - diff; // Since the actual font is black (0, 0, 0) diff.rgb = rgb * FontColor; diff.a = (rgb.r + rgb.g + rgb.b) / 3; I'm using the color value of each pixel, and then calculate the alpha with the arithmetic average of the 3 color channels. However, this results in a blurry font: (both images are scaled by 400%) Do you know a better way to sharpen the result?

Share this post


Link to post
Share on other sites
The main problem is that the bitmap font you're working with being rendered using sub-pixel precision; thats why you see a shade of red on the left edges and a shade of blue on the right. This sub-pixel preceision is a method of rendering which fools your eyes into seeing a more detailed high-contrast image (ie - black and white) image by treating each color component's sub-pixel element as its own pixel. Without going into too much detail, using the red color component as an example, your eyes will tend to blend a bright red value into the bright color (white) and a dim red value into the dark color (black). The same goes for blue and green.

You can actually see this effect in the sample bitmap you posted if you back away from your monitor; as you back away, the red/blue shades will begin to merge as I've explained. In particular, look at the vertical stroke on the "1" character. You'll notice that there are actually no black pixels in it what-so-ever, but as you back away, it will appear black! Its really a neat trick, it just ruins your sampling pattern [grin]


Try to use a font that doesn't have this enabled, you'll be able to tell because it won't have these red and blue edges. Shades of grey are fine, since it still remains at the pixel level, rather than sub-pixel.

If you cannot seem to find one, you may have to disable this feature in your operating system control panel. In Windows XP its under Performance and Maintainance -> System -> Advanced Tab -> Performance Settings in a setting called "Smooth edges of screen fonts" -- don't forget to select the option to over-ride what the system thinks is best.


The second, less problematic problem, is that you seem to have stretched your rendered font taller than the source font, probably in an attempt to correct for the above problem. Once you fix the first problem I mentioned the visual quality will be greatly improved. I don't think that stretching the image larger will have a negative effect on visual quality (except pixelation if you stretch it too much.) but you'll likely be able to return it to its native size.

The final thing I'll add is that if your engine supports multiple resolutions you have to be careful about maintaining aspect ratio and relative sizing. Its no fun trying to read a tiny 16x16 font on a 1920x1440 monitor. So make sure to keep the relative point-size appropriate for each supported resolution and use a source bitmap that renders text to the largest resoution you support (if not double that). You may also have to provide intermediate-level fonts for extremely low resolutions if readability suffers with scaling.

[Edited by - ravyne2001 on May 10, 2007 4:19:32 PM]

Share this post


Link to post
Share on other sites
Thanks very much for your answer :)
I will have create a new character map without anti aliasing. I'll do that after school. I'll post the new results in a view hours. Hope it works :)

Thangs for showing me this stretching problem. Somehow, I tried to stretch one character from 10 to 14 pixels, don't know why. I've corrected it.

My new bitmap now looks like this:



And the results:


This is definately a great improvement, thanks very much.
I'll be trying some different methods to calculate the alpha value for the font.

*edit* Something funny. I've added an alpha channel to my font. However, I can't see anything if I just use the alpha channel from the source. The font just doesn't appear. Maybee I did something wrong..

[Edited by - SiS-Shadowman on May 11, 2007 6:26:54 AM]

Share this post


Link to post
Share on other sites
Thats definately looking much better.

I think the issue you're seeing now is due to the coordinates not being exactly aligned and/or a texture sampling issue.

Are you using pre-transformed coordinates for the text vertices or are you sending them through any transformations? My best guess is that the vertices are being transformed to slightly different coordinates than you might expect and so the rasterizer is performing a very small scaling operation in the output.


There are a few things you can try to see if it gives better results:

1 - Try offsetting the text vertices by -0.5, -0.5, this may align the texels better with the framebuffer pixels.

2 - Try rendering your source bitmap at twice the current size, but keep rendering it at its current size in your application. Make sure Mip-mapping is enabled.


Looking at your new samples, I suspect that its a coordinate issue because the characters appear different in differnt words. For example, look at the 'a' in "Please" vs the 'a' in "Equivalant" those two quads are definately being sampled differently. The artifacts attached to the 'r' and 'i' characters are a little concerning... perhaps they're due to errors in how you calculate the alpha?

Also, I don't think there's a need "calculate the alpha" any more, at least not with any complex algorithm. It should be a matter of subtracting the source pixels from a constant value (just to invert alpha from the color level) so essentially, assuming 8bit color components, you have: alpha = 255 - greyscale;

Share this post


Link to post
Share on other sites
Quote:
Original post by ravyne2001
I think the issue you're seeing now is due to the coordinates not being exactly aligned and/or a texture sampling issue.

Are you using pre-transformed coordinates for the text vertices or are you sending them through any transformations?


I am using pre-transformed coordinates for the vertices.

Quote:
2 - Try rendering your source bitmap at twice the current size, but keep rendering it at its current size in your application. Make sure Mip-mapping is enabled.

Sorry, but I don't understand what you mean with that. Do you mean I should scale the texture by 2, but still have it rendered in its original size?

Quote:
Looking at your new samples, I suspect that its a coordinate issue because the characters appear different in differnt words. For example, look at the 'a' in "Please" vs the 'a' in "Equivalant" those two quads are definately being sampled differently. The artifacts attached to the 'r' and 'i' characters are a little concerning... perhaps they're due to errors in how you calculate the alpha?


I also thought that. But the coordinates are right. I think this is because of some filtering ( It also appeared when I disabled Mip-, Min- and Magfilter ) "problem". I tweaked the font bitmap a bit, removing some parts of the characters, that were too near to their neighbouring character. This resulted in a better quality with much less artifacts.
Although the neighbouring pixel were NOT inside the current character coordinates, however their texels are influencing the current character texels in some way ( maybe because of filtering )

Quote:
Also, I don't think there's a need "calculate the alpha" any more, at least not with any complex algorithm. It should be a matter of subtracting the source pixels from a constant value (just to invert alpha from the color level) so essentially, assuming 8bit color components, you have: alpha = 255 - greyscale;

Yep, I used that. I tried also some others ways, but the result did not get any better.

At last, I tried some different filters. Point and none weren't satisfying, but anisotrophic did a great job:

Anisotropic (real size/400%):
/

No Filtering (real size/400%):
/

Share this post


Link to post
Share on other sites
Although it looks pretty good, it still isn't great. I strongly suggest using the library FreeType to load font files and the dynamically create the font sheet textures yourself. I did this for my GUI and the fonts are perfect.

Here is an example pic:

DGUI Example

I think that for regular fonts the quality here is about as good as possible. Granted, the spacing needs a little work but the quality of the lettering is good.

What do you think?

Dave

Share this post


Link to post
Share on other sites
Your font looks neat and clean, I can't deny that ;)
I've downloaded the newest release of freetype and I'll test it. From what I've read, freetype can output a Bitmap from a truetype font ( or others ).
I'll post some screens when I'm done :)
But it could take some days or a week.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiS-Shadowman
Quote:
Original post by ravyne2001
2 - Try rendering your source bitmap at twice the current size, but keep rendering it at its current size in your application. Make sure Mip-mapping is enabled.

Sorry, but I don't understand what you mean with that. Do you mean I should scale the texture by 2, but still have it rendered in its original size?

Yep, thats exactly what I mean. Also, did you try #1 at all? what were the results? Oh, and do you have FSAA or any other filtering enabled?

The Aniso is definately better but I'm honestly not sure what level of quality you can eventually expect from this without using an external library like freeType. I've never actually done this in OpenGL before, so I'm basically pulling bits together from what I know of software rendering, OpenGL, and what I can see in your problem.

Share this post


Link to post
Share on other sites
Quote:
Original post by ravyne2001
Yep, thats exactly what I mean. Also, did you try #1 at all? what were the results? Oh, and do you have FSAA or any other filtering enabled?


I'll try it out. I have FSAA disabled, other filters are not enabled, at least I think so. And no, I didn't try #1, because the quality of the Font improved after manipulating the bitmap.

Quote:

The Aniso is definately better but I'm honestly not sure what level of quality you can eventually expect from this without using an external library like freeType. I've never actually done this in OpenGL before, so I'm basically pulling bits together from what I know of software rendering, OpenGL, and what I can see in your problem.

Sorry, I didn't mention: I use DX for my engine. But this shouldn't make a great difference, should it? But I'll try out using freetype when I've more time.

Share this post


Link to post
Share on other sites
ah, for some reason I thought you were using OpenGL, probably because I got my wires crossed with another thread I was reading. At any rate, no that doesn't matter the "OpenGL" pointers I've been giving you are really 3D API pointers, since they apply equally to Direct3D or elsewhere.

But you should definately give #1 a shot, should be super easy to try out, and it may fix the problem possibly.

Share this post


Link to post
Share on other sites
Just to be shure, you mean the texture coordinates, do you?

*edit* One other thing. I'm not shure anymore if I calculate the texture offsets right any more.
In DX, tv and tu lie between 0 and 1. My Bitmap has 36 chars per line, so the x offset is nbr_of_char / 36.
My Quad has the texture coordinates 0 and 1, so it would normally have a whole texture textured "on" it. I divide the x offset by the nbr_of_chars, so one char would be drawn onto the quad. Then I just add the calculated offset for the respective char to the coordniates. voilà.

But this way, the "right" x coordinate of the char a, and the "left" coordinate of the char b are the SAME. But this is not desired, the coordinates should differ by exactly 1 pixel. However, I'm not able to do this. At least my code doesn't work.
I'm not shure If you know HLSL, but this is how I tried it:

float4 PS_Font(float2 Tex0 : TEXCOORD0) : COLOR
{
float2 tex = {0, 0}; // This is the base coordinate. Zero'd
if(Tex0.x == 0.0f) { // If the incomming texture coordinate is zero, I recieved the "left" vertex coordinate
tex.x += 1.0f / (max_chars * char_size); // This coordinate needs to be shifted by 1 pixel
}
// TextureOffset is the variable, pre-calculated by the programm for each char respectively: number_of_char / max_chars
tex.x += Tex0.x / max_chars + TextureOffset.x; // Since normally a whole texture would be mapped onto the quad, I need to divide the Standard coordinate by max_chars or max_lines, to recieve the coordinates for one character
tex.y += Tex0.y / max_lines + TextureOffset.y;

...
}


Somehow this doesn't work. I still have artifacts on some fonts.

[Edited by - SiS-Shadowman on May 13, 2007 5:42:12 AM]

Share this post


Link to post
Share on other sites

This topic is 3865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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

Sign in to follow this