The SDL forums have moved to discourse.libsdl.org.
This is just a read-only archive of the previous forums, to keep old links working.


SDL Forum Index
SDL
Simple Directmedia Layer Forums
work in progress: a simple library to render dynamic text
Trev


Joined: 07 Nov 2011
Posts: 63
Check out http://lackeyccg.com/code/ for a user-friendly way to render dynamic text.

http://lackeyccg.com/code/MainREADME.cpp explains how it works.

This is a work in progress and feedback is appreciated. You should be able to very easily incorporate the text drawing functions in any SDL/SDL_ttf equipped project, or at least that is the goal.
Trev


Joined: 07 Nov 2011
Posts: 63
Chris Bush wrote:
It's neat. I like the prospective alignment feature. One might go in the direction of creating something like a paragraph class that you can create, position on the plane, set the typeface and kerning and so on, and fill and refill with text. I think setting the color in the glyph drawing function might not be optimal, since for a given input to DrawText you must pass and set the colors to each letter. Anyway, it looks useful. Thanks for leaving the credit.

Chris

I guess you're right about the color. I think it would be better to set the color at the top level, before any individual glyph is rendered. This could be done like:

Code:
void FontRecordClass::DrawText(
   int FontId,            //Unique id for each entry in the font record
   const char* s,          // input C string
   float top, float left,  // upper left corner
   float lineheight,       // line height factor - 1 is normal
   float kerning,          // letter spacing factor - 1 is normal
   float width,             // column width
   int Alignment,         // text alignment, see TextAlignmentTypes
   float Red,float Green,float Blue,float Alpha //Color
)
{
   int Pos=FindPosBasedOnId(FontId);
   if(IsValidPos(Pos))
      {
      glColor4f(Red,Green,Blue,Alpha);
      Entries[Pos].DrawText(s,top,left,lineheight,kerning,width,Alignment);
      }
}

That provides the same interface, but a lot less function calls to glColor4f.
Regarding alignment, I also want to have the option of wrapping without breaking words in half, but properly breaking on spaces and appropriate punctuation.
I also want to have an option of rendering the text along a 3D plane, so you could draw text on the faces of a cube, for example.
Trev


Joined: 07 Nov 2011
Posts: 63
I added some changes so things work on the iPad, and it almost works.
Here is a screenshot showing the desktop and the iPad version side by side:
http://lackeyccg.com/code/ipadFontBug.jpg
The fonts are definitely loading on the iPad, but they aren't displaying properly for some reason. Still trying to figure that out.

I included another read me file specific to the iPad, at http://lackeyccg.com/code/READMEiPad.txt

The FontGlyphClass::Draw function in http://lackeyccg.com/code/FontGlyphClass.cpp currently has 2 sections, one for the iPad (OpenGL ES), and one for regular OpenGL. For regular OpenGL, the OpenGL ES code works too, and seems to yield identical results. At this point, I'm not completely sure which will give me better performance. I may end up using the OpenGL ES code for both.

If anyone has any suggestions for improvements, please let me know.
This is a work in progress.
Trev


Joined: 07 Nov 2011
Posts: 63
I added some changes to have more control over drawing text as a paragraph. Text should properly align now, rather than getting chopped off mid-word.



That paragraph is drawn with
Code:
gFontRecord.DrawParagraph(gFontIds[curfont],//font id
         (char*)Paragraph.c_str(),// string to draw
         5,   // bottom left corner X value
         150,      // bottom left corner Y value
         640,   //Maximum width for the paragraph to wrap around.
         1,0,0,1);//RGBA. This happens to be red.
Trev


Joined: 07 Nov 2011
Posts: 63
This is what things looked like before:http://lackeyccg.com/code/ipadFontBug.jpg
Regarding that iPad bug, where the text wasn't rendering properly, I made an unexpected discovery when I made the following singular change (marked in red):

FontGlyphClass::FontGlyphClass(SDL_Surface *surface)
{
SetWidth(surface->w);
SetHeight(surface->h);
texture[0]=-1;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nextPowerOfTwo(surface->w), nextPowerOfTwo(surface->h), 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

There are some odd things about this change. First, unlike before, now the text renders exactly the same on the Mac OSX target and on the iPad (simulator) target, as shown here: http://lackeyccg.com/code/ipadFontBug2.jpg
But now neither of them looks right. If I remove that nextPowerOfTwo() function, the OSX version looks perfect and the iPad version gets worse. I'm not sure what could cause this inconsistent behavior.
Trev


Joined: 07 Nov 2011
Posts: 63
Trev wrote:
This is what things looked like before:http://lackeyccg.com/code/ipadFontBug.jpg
Regarding that iPad bug, where the text wasn't rendering properly, I made an unexpected discovery when I made the following singular change (marked in red):

FontGlyphClass::FontGlyphClass(SDL_Surface *surface)
{
SetWidth(surface->w);
SetHeight(surface->h);
texture[0]=-1;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nextPowerOfTwo(surface->w), nextPowerOfTwo(surface->h), 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

There are some odd things about this change. First, unlike before, now the text renders exactly the same on the Mac OSX target and on the iPad (simulator) target, as shown here: http://lackeyccg.com/code/ipadFontBug2.jpg
But now neither of them looks right. If I remove that nextPowerOfTwo() function, the OSX version looks perfect and the iPad version gets worse. I'm not sure what could cause this inconsistent behavior.

I just fixed things so now text renders properly on iOS with OpenGL ES. Look at the new changes to FontGlyphClass::FontGlyphClass(SDL_Surface *surface) to see the changes that needed to be done to fix this issue.
work in progress: a simple library to render dynamic text
Forest Hale
Guest

I believe you need to set these before glTexImage2D:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // if using a format other than GL_RGBA
glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch); // not sure if surface->pitch is the best thing to put here
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, surface->h);

But I've never tried uploading an image this way before, I normally would pad the surface size to a power of 2 and then upload that, rather than pulling tricks with unpack settings - also I'm not sure
if these unpack settings exist on iOS.

It's best to create an atlas texture (large texture that you upload many small pieces into) and maintain it as a software surface, and when you have copied some additional text images into it, update
the texture - preferably no more than once a frame (mobile hardware is not fast!), so do this as two passes through your relevant code (preparing the needed text images, then uploading, then drawing
them all).

Note that all OpenGL drivers will greatly prefer that you two pass your render anyway, upload all vertex buffers, textures and anything else of that nature, before you issue your first glDrawElements
of the frame.

On 06/13/2012 03:57 AM, Trev wrote:
Quote:


Trev wrote:


This is what things looked like before:http://lackeyccg.com/code/ipadFontBug.jpg
Regarding that iPad bug, where the text wasn't rendering properly, I made an unexpected discovery when I made the following singular change (marked in red):

FontGlyphClass::FontGlyphClass(SDL_Surface *surface)
{
SetWidth(surface->w);
SetHeight(surface->h);
texture[0]=-1;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nextPowerOfTwo(surface->w), nextPowerOfTwo(surface->h), 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

There are some odd things about this change. First, unlike before, now the text renders exactly the same on the Mac OSX target and on the iPad (simulator) target, as shown here:
http://lackeyccg.com/code/ipadFontBug2.jpg
But now neither of them looks right. If I remove that nextPowerOfTwo() function, the OSX version looks perfect and the iPad version gets worse. I'm not sure what could cause this inconsistent behavior.


I just fixed things so now text renders properly on iOS with OpenGL ES. Look at the new changes to FontGlyphClass::FontGlyphClass(SDL_Surface *surface) to see the changes that needed to be done to fix
this issue.


_______________________________________________
SDL mailing list

http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


--
LordHavoc
Author of DarkPlaces Quake1 engine - http://icculus.org/twilight/darkplaces
Co-designer of Nexuiz - http://alientrap.org/nexuiz
"War does not prove who is right, it proves who is left." - Unknown
"Any sufficiently advanced technology is indistinguishable from a rigged demo." - James Klass
"A game is a series of interesting choices." - Sid Meier

_______________________________________________
SDL mailing list

http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org
Re: work in progress: a simple library to render dynamic tex
Trev


Joined: 07 Nov 2011
Posts: 63
Forest Hale wrote:
I believe you need to set these before glTexImage2D:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // if using a format other than GL_RGBA
glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch); // not sure if surface->pitch is the best thing to put here
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, surface->h);

But I've never tried uploading an image this way before, I normally would pad the surface size to a power of 2 and then upload that, rather than pulling tricks with unpack settings - also I'm not sure
if these unpack settings exist on iOS.
If you look at the latest http://lackeyccg.com/code/FontGlyphClass.cpp file, I got it to work without using any GL_UNPACK settings. I'm not sure if that method would work on iOS, but I tested that with the method I am currently using, it does (at least on the simulator which I am assuming implies it will work on the device). You think an GL_UNPACK method would be superior? One thing I am currently not sure of is if all of the code I just added to get things to work is needed or optimal. One of the things I am doing now is removing some of the new code and seeing if things still work on iOS.
Here is the code that loads the FontGlyph:
Code:
FontGlyphClass::FontGlyphClass(SDL_Surface *surface)
{
   int WidthInNearestPowOf2 = nextPowerOfTwo(surface->w);
   int HeightInNearestPowOf2 = nextPowerOfTwo(surface->h);
   SetTexMinX(0.0f);         /* Min X */
   SetTexMinY(0.0f);         /* Min Y */
   SetTexMaxX((GLfloat)surface->w / WidthInNearestPowOf2);   /* Max X */
   SetTexMaxY( (GLfloat)surface->h / HeightInNearestPowOf2);   /* Max Y */

   SetWidth(surface->w); //This is the actual width and height of the image, which is not necessarily a power of 2.
   SetHeight(surface->h);

   SDL_Surface *image = SDL_CreateRGBSurface(
      SDL_SWSURFACE,
      WidthInNearestPowOf2, HeightInNearestPowOf2,
      32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
      0x000000FF,
      0x0000FF00,
      0x00FF0000,
      0xFF000000
#else
      0xFF000000,
      0x00FF0000,
      0x0000FF00,
      0x000000FF
#endif
         );
   
/* Save the alpha blending attributes */
   //Uint8  saved_alpha;
   //SDL_GetSurfaceAlphaMod(surface, &saved_alpha);
   //SDL_SetSurfaceAlphaMod(surface, 0xFF);
   //SDL_BlendMode saved_mode;
   //SDL_GetSurfaceBlendMode(surface, &saved_mode);
   SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);//This is the only line relating to blending and alpha that seems to do anything I could notice.

/* Copy the surface into the GL texture image */
   SDL_Rect area;
   area.x = 0;
   area.y = 0;
   area.w = surface->w;
   area.h = surface->h;
   SDL_BlitSurface(surface, &area, image, &area);

/* Restore the alpha blending attributes */
   //SDL_SetSurfaceAlphaMod(surface, saved_alpha);
   //SDL_SetSurfaceBlendMode(surface, saved_mode);
   /////////////////////

   glGenTextures(1, &texture[0]);
   glBindTexture(GL_TEXTURE_2D, texture[0]);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WidthInNearestPowOf2, HeightInNearestPowOf2, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);

   if(image)
      SDL_FreeSurface(image); /* No longer needed since the data is retained as an OpenGL texture */
}


Forest Hale wrote:
It's best to create an atlas texture (large texture that you upload many small pieces into) and maintain it as a software surface, and when you have copied some additional text images into it, update
the texture - preferably no more than once a frame (mobile hardware is not fast!), so do this as two passes through your relevant code (preparing the needed text images, then uploading, then drawing
them all).

Regarding a software surface as opposed to an opengl texture, I don't see any reason to maintain (i.e., retain) anything besides the opengl textures. The opengl textures are the only things I actually need to render the glyphs, and I figure I might as well load all the glyphs once and be done loading. I suppose if I happened to never use a particular glyph, there would be some wasted memory, but I'm not sure that would make it worth it to maintain the software surface and dynamically load only those glyphs I need at the moment. But perhaps I am not understanding your point. You seem to be advocating updating the texture very frequently which I would think would result in worse performance.

Regarding a single atlas texture, currently how I am doing it is loading every glyph to an OpenGL texture via glBindTexture(GL_TEXTURE_2D, texture[0]);. This results in a lot of separate opengl texture IDs (which I am not sure is intrinsically bad). I agree that it might be better to load all glyphs to a single texture and have each glyph know its specific UV coordinates on the atlas texture. I did it how I did it because my code was based on http://codepad.org/QS26ciMD from Chris Bush, and that's how he did it. I'm curious why he decided to load all of the glyphs as separate opengl images. Perhaps he had a reason for it, or perhaps it was simply easier to implement.

Forest Hale wrote:
Note that all OpenGL drivers will greatly prefer that you two pass your render anyway, upload all vertex buffers, textures and anything else of that nature, before you issue your first glDrawElements
of the frame.
I'm not exactly sure what you mean by this. Is this a general comment, or did you look at the code at http://lackeyccg.com/code/ and determine I was doing things in a suboptimal way?