Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Extending Gosu / Workaround to use Custom Fonts in LINUX
- By TheOm3ga Date 2010-03-07 15:37 Edited 2010-05-16 02:07
Hi there. As I wrote in the other forum post (http://www.libgosu.org/cgi-bin/mwf/topic_show.pl?tid=310) some time ago, I was needing custom fonts support ¡n Linux/C++. Looks like the problem in pango is not yet solved, so I started to think about something else.

Given I had some experience in SDL, I remembered that SDL_ttf was actually able to load custom ttf files so I thought I could write my text in a SDL_Surface, copy it to a Gosu::Bitmap and then create a Gosu::Image from it. And it works!!! It's nice!

I have written a class "customFont" with more or less the same members as Gosu::Font. The way to use it is very simple:

boost::scoped_ptr<customFont> myFont(new CustomFont(graphics(), "nameOfFile.ttf", 30)); // being 30 the font height
myFont -> draw ("Hello", x, y, z);

// members
myFont -> name(); // returns "nameOfFile.ttf"
myFont -> height(); // returns 30
myFont -> textWidth("text");

I've used std::string instead of std::wstring because I found no easy way to get a const char * from a wstring. The other two overloaded "draw" member functions are not implemented because I don't need them for now, but they're pretty straightforward to create if you happen to need them.

To compile it, you just need to link the sdl ttf lib: "g++ -o myExe ... -lSDL_ttf". Obviously you need to have installed libsdl-ttf2.0-dev or similar.

You can download the file here: https://forja.rediris.es/plugins/scmsvn/viewcvs.php/trunk/customFont.h?root=cusl4-oflute&view=log

Please comment any errors or sugestions you come across.
Thanks.
- By jlnr (dev) Date 2010-03-07 20:20
Hmm, good idea! Actually it's just a tiny bit of code in Gosu that would need to be replaced (TextPangoFT.cpp). Then, all of Gosu's text support could use the SDL. I will look into text quality and maybe just replace the old Pango bits with SDL_ttf.

BTW, you can attach files after posting, so you don't have to use external webspace :)
- By TheOm3ga Date 2010-03-08 00:32 Edited 2010-03-08 01:08
The quality is really good. In fact, there are three different methods in SDL ttf, depending on the quality you need. In my class I'm using the best one, TTF_RenderUTF8_Blended. It's fully anti-aliased, with alpha channel support and all. Also, looks like SDL ttf offers some options like built-in outline.

BTW, I hadn't seen the attach button until I had already posted the message, but it's good to know it :)
- By TheOm3ga Date 2010-03-08 01:08
Ouch, looks like SDL ttf lacks the ability to use a already installed font. Too bad.
- By erisdiscord Date 2010-03-09 23:02
I think that the ability to load a font by file name is more useful than loading a preinstalled system font since you are never guaranteed the presence of a font on any given system—especially working with cross-platform development.

There is always the option for Gosu to use a platform-appropriate method to find the file name of an installed font as well.
- By jlnr (dev) Date 2010-03-10 00:24
I think relying on web-safe fonts is pretty reasonable :)

But even keeping the old code for existing fonts and using the SDL_ttf code for custom fonts would be better than the current state of things.
- By erisdiscord Date 2010-03-10 01:10
Mm, true, but web safe fonts means the likes of Arial (gag). :)

Any chance of exposing some of SDL_ttf's rendering options through Gosu?
- By jlnr (dev) Date 2010-03-10 12:17
Oh, if you knew all the dreams I have about proper typography in Gosu… ;) It's even in the To Do (wiki page) in its right place, but I am pretty slow at getting down there lately.
- By TheOm3ga Date 2010-03-17 23:05
I'm improving the customFont class. Right now I've just added bold, italics and underline support. Also, latest SDL_ttf version features different types of pixel hinting and more.

I've attached a testcase for you to check it out. Just decompress it. In the same folder the program expects to find a gosu folder with a proper installation of gosu (check the makefile if you have doubts)
Attachment: testcase.tar.gz - Testcase for customFont class (198k)
- By lobo_tuerto Date 2010-03-31 02:08
I'd love to see this already implemented in Gosu/Ruby :) pls pls jlnr *nudge nudge wink wink*
- By jlnr (dev) Date 2010-03-31 03:54
Sure. but I just added it for OS X (64-bit, it worked for 32-bit before). One after another. ;)
- By TheOm3ga Date 2010-05-16 02:06 Edited 2010-05-16 02:08
Hey there.

I've been improving the customFont class, because it was giving me some segfaults from time to time. There had been some changes, so you should check it out. I think that performance is a little bit better now, but it could be definitely better. Also some doxygen comments have been added.

You can fetch it at
https://forja.rediris.es/plugins/scmsvn/viewcvs.php/trunk/customFont.h?root=cusl4-oflute&view=log

By the way, the class is part of my final degree project, it's a musical game called oflute. You can check it out (and svn checkout it, too ;) )  at http://oflute.wordpress.com/about/
- By jlnr (dev) Date 2010-05-16 04:03
Sweet, I have been meaning to steal your code into Gosu. Is this appreciated? I haven't yet looked for a license on your part.

Even more awesome that you use it for a degree project. I think you're the first male from Spain to do that ;), and the third or fourth instance in which I hear of Gosu being used for university stuff. The fact that you do something musical on Linux where all of Gosu's cool audio features don't work makes me curious now ^^" Be sure to add it to the showcase later!
- By TheOm3ga Date 2010-05-16 11:53
You can do what you want with the code, although some credit would be appreciated :D

About the project, I'm actually encouraging all of my mates to use Gosu instead of plain SDL for all of their projects. I think it's a huge step in comparison to using plain SDL. Sometimes people spend a lot of time dealing with SDL_Surfaces, SDL_Images, trying to create their own object oriented adaptation of SDL, instead of actually programming games. After being suffering SDL for all of these years, Gosu has been like a gift to me. I like it a lot.

My project is musical, but I haven't used Gosu's sound features yet, because I don't actually play any sounds (at least at this point of the project). I'm using PortAudio library to get the microphone input, the user has to play the recorder (flute). Then I analyse the input using FFT and calculate the sound pitch, to check if the user is playing the instrument properly. You can see it in action at http://www.youtube.com/watch?v=UU7YMdi-2bY
- By jlnr (dev) Date 2010-05-17 16:16
Wow, so it's basically SingStar for the flute, too bad I don't have mine from school anymore ^^

BTW: Because you asked on IRC, here is a vector version of the Gosu logo, I converted it from its proprietary format to PDF. It does not have the gloss effect on it that the website version has, that one is just a quick Photoshop.
Attachment: gosulogo.pdf (5k)
- By jlnr (dev) Date 2010-05-30 05:34 Edited 2010-05-30 05:46
I just integrated a SDL_TTF fallback based on your code into GosuImpl/Graphics/TextUnix.cpp. It just crashed on 1.9.2pre1, but lots of things crash on 1.9.2pre1, so I am now trying it with 1.8. Thanks!

Edit: Never mind, it works. The UnicodeTest.rb file crashes in various, seemingly unrelated ways, but that is stuff for later.
- - By TheOm3ga Date 2010-06-09 17:47 Edited 2010-06-09 19:34
Really nice!! I don't know how you solved it (I'll take a look at the code), but my customFont class was giving me a segfault if I tried something like this:
void draw(){
  wstring a = L"Perro";
  fuente -> draw(a, 10, 10, 1., 1,1, Gosu::Color(0xffffffff));
  fuente -> draw(a, 10, 30, 1., 1,1, Gosu::Color(255, 255,0,0));
    }


But with your implementation it works! Thanks a lot.

Edit: I looked at the code and see how you did it. It's more or less the same I was doing as a workaround, nice.

By the way, could you implement in Gosu::Font that uses TTF_FontLineSkip? It would be really very useful, specially when using Gosu::createText, the lineSpacing argument could be automatically calculated.
Parent - - By TheOm3ga Date 2010-08-07 01:57
Two months later, I've got stuck again on the same segmentation fault errors, even though I solved them some time ago, but I lost the files.
Julian, could you give it a look? I've been trying to debug the error but not been able to solve it. It only happens when I try to draw twice using the same font on the same game loop iteration. The debugger stack says that the error appears on the TexName method. It's quite strange because, even using new objects every time customFont::draw is called, it fails. Why? There is not supposed to be anything in common between each call.
Parent - - By jlnr (dev) Date 2010-08-07 04:34
If it crashes in texName then it sounds as if you are deleting the Font object before Window::draw is finished. That would cause the Font's glyphs to be waiting to be rendered, but when the window wants to render them (indirectly needing their GL texture name), it would crash.

Do you ever delete any Font instances? Can you provide a minimal C++ program that causes this so I can get a head start when looking at it?
Parent - - By TheOm3ga Date 2010-08-07 15:20
Watch out! I'm talking about my ancient customFont class, not Gosu::Font. The problem is that the font sizes in Windows and Linux render differently. In windows, some fonts appear smaller than they should, whereas in Linux they render properly.



Then, given I can't force Gosu to use SDLTTFRenderer under Windows, I thought I could use my customFont class to render the texts using SDL on both platforms. That's when the problem appears. If you create a customFont object (obviously keeping the object alive as a class member or something like that) and draw a line of text, there's no problem. BUT, if you try to draw two lines of text (calling customFont::draw twice) on the same game loop iteration, it fails. I think it's because the image that customFont creates with the first line of text gets overwritten when draw is called for the second time. Pretty obvious I think, but I can't think of a good way of being able to call customFont::draw as many times as I want on the same game loop iteration.

Somehow, when you implemented my sdl idea on Gosu two months ago, I discovered a solution for my problem based on your implementation, but now I forgot it. It must be the heat of the summer :(
Parent - - By jlnr (dev) Date 2010-08-07 16:54 Edited 2010-08-07 16:56
Wasn't there a problem with descenders (like the lower-case 'q') being clipped off on Linux though? Then actually the basic problem is SDLTTFRenderer rendering things too big. If you can find a way to shrink things so that nothing will be clipped off, things should look practically identical on Windows and Linux. And then we could port that fix from customFont into Gosu::Font, finally making the customFont/SDLTTFRenderer duality obsolete? :)
Parent - - By TheOm3ga Date 2010-08-07 17:50
Yes, there's a clipping problem, but I'm using a workaround (explained in this post)to avoid that clipping. Without using the workaround it looks like this:


In my opinion, it's not that SDLTTFRenderer is rendering things too big, because if you look at the other image, it's the one that fits the actual size (30px) passed to the Gosu::Font constructor, but the windows's rendering stuff too small. I don't know...
Parent - - By jlnr (dev) Date 2010-08-08 02:19 Edited 2010-08-08 02:23
Even in the comparison image I can see at least one pixel of the 'J' being lower than the 30px rect. I think it's that SDLTTFRenderer makes *most* glyphs 30px high, but reserves some extra space (for the ascender of d and descender of q, for example). In Gosu logic, the 30px should already cover all extra things that could possibly happen, so no clipping should ever happen. In your layout it's not that important if, but in general drawing at (x,y) with height=30 should never draw things above y and below y+30.
Parent - - By TheOm3ga Date 2010-08-09 15:08
When you tell SDL_ttf to open a font with size=30, it doesn't actually mean that all characters should fit in 30 px height. In fact, there's a function called TTF_FontHeight that will tell you the actual total height of the font, which in 99% of cases, it's higher than the font size you passed to TTF_OpenFont. For example, for the font I'm using (Lake Wobegon), it returns 41px for a 30px loaded font. But that does not happen just in SDL ttf, but in many other systems as well, like Photoshop: take a look at how Photoshop renders the same font at the same size.



Anyway, what I meant is that the appearance of font rendering should be the same across platforms. It's ok that the pixel hinting and such things be different, but it's a pain in the a$$ to have to use different font sizes for each platform, even though it happens with some fonts and not with others.

Given SDL ttf works the same across platforms, why not use it as a single good solution for all the platforms?
Parent - - By jlnr (dev) Date 2010-08-10 01:52
SDL_TTF only works for custom loaded .TTF files and it's a DLL (or .framework) more to include just for that. And from Gosu's perspective it is still the library that does the "wrong" thing, vs. all the others.

I agree that it might be nice if you could draw a bit of text and some parts of it would extend from that rectangle as necessary: ascenders, descenders umlaut marks ¨´`´, indented " etc., as in Photoshop. However, that breaks the whole logic of how an Image works now for Gosu#from_text because if you draw at (x,y) suddenly parts of the texture would be at (x-something, y-something), similar for Gosu::Font. On the upside, the current logic is perfect if you have a rectangular area (text field etc.) to fill and want to make sure that nothing bleeds out.

It should be pretty easy to just scale the font down so that TTF_FontHeight is the desired value, I do the same thing on iOS, i.e. create a temporary font, calculate the size->px factor and then create the real font based on that.
Parent - - By kyonides Date 2010-08-10 02:09
But isn't there any method or proposal that might let us test the size of a Font image without really creating one for testing purposes? I mean, something that might let us mathematically calculate it without ever displaying it on screen.
Parent - By jlnr (dev) Date 2010-08-10 04:47
Opening a TTF font, creating images and rendering something to screen are three different tasks and to get the pixel height, we only need to do the first one. There is obviously no way to get the height without opening the .TTF file, but it would be nice if SDL_TTF objects could just be resized without creating a new one, or were not tied to a size generally. Let's just hope nobody creates Font objects in a tight loop? ;)
Parent - - By TheOm3ga Date 2011-01-15 19:42
I guess with the new changes in 0.7.26 (http://code.google.com/p/gosu/source/detail?r=1711) now this works properly, doesn't it? I'll check it out tonight.
Parent - By jlnr (dev) Date 2011-01-16 02:57
Yep. My list still has two SDL_TTF related issues though: bold/italic/underline doesn't seem to work, and I got some crash with Unicode once that I wanted to look into.
- By TheOm3ga Date 2010-06-09 23:11
Hi,

I'm afraid I've found a bug. Take a look at the "g" letter:



The top image is using my customFont class, the bottom image is using the new Gosu::Font.

What happens?
When we use Font::draw, Gosu::Font::Impl::getChar is called. This method creates a bitmap (Font.cpp, line 59):

Bitmap bmp;
bmp.resize(charWidth, height);


Then, drawText is called. Depending on the font, drawText may call PangoRenderer's or SDLTTFRenderer's drawText.

The problem is that usually the surface created by SDLTTFRenderer.drawText has a bigger height than fontHeight. In my example, for instance, the height I passed as argument is 30, but the surface generated is 34 pixels tall, so the bottom pixels are cut off.

The only solution I've thought is checking if the bitmap is too small, and resizing it to fit the text; At TextUnix.cpp, line 235, I would add right before bmp.insert:

if(bmp.height() < temp.height()){
    bmp.resize(bmp.width(), temp.height());
}


so the height is enough to hold the text.
- By jlnr (dev) Date 2010-06-10 02:08
Well, that makes it a different height than on other operating systems then. I think I should try to figure out the pixel size of the whole string and then just render it again using a scaled size so it fits the height perfectly. Argh, how ugly. :)
- By jlnr (dev) Date 2010-06-13 04:13
Hey TheOm3ga, I am currently working on improving text support in Gosu. Right now, the SDL_TTF renderer respects neither the passed color's alpha channel nor the font flags passed to textWidth and drawText (ffBold and ffItalic, ffUnderline). Do you want to try your luck at updating the SDL_TTF class for that? :)
- By jlnr (dev) Date 2010-06-13 15:15
(SDL_TTF renderer also crashes with the Unicode text given in feature_tests/UnicodeTest.rb (definition of @loc_text).)
Up Topic Gosu / Extending Gosu / Workaround to use Custom Fonts in LINUX

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill