You are not logged in.
Hello,
Has anybody has a working example with us freetype (font render engine) library in combination with GRRLIB.
The below source code is working but it doesn't perform very well. If you call each video frame update the
freeTypePrintf methode the video frame rate per second is dropping a lot. Resulting in a very poor ir pointer
response.
Any comments are welcome?
wplaat
-------------------------------
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Library library=NULL;
FT_Face face=NULL;
FT_GlyphSlot slot;
FT_Bitmap *FT_image;
void freeTypeInit(void)
{
bool error;
if (library==NULL)
{
error = FT_Init_FreeType( &library );
if ( error )
{
printf("Init FT_Init_FreeType failed");
}
}
if (face==NULL)
{
error = FT_New_Face( library, "hotpizza.ttf", 0, &face );
if ( error == FT_Err_Unknown_File_Format )
{
printf("the font file could be opened and read, but it appears that its font format is unsupported");
}
else if ( error )
{
printf("another error code means that the font file could not ... be opened or read, or simply that it is broken");
}
slot = face->glyph;
}
}
void freeTypePrintf(int pen_x, int pen_y, u32 color, int fontSize, char *text,...)
{
bool error;
int n;
char tmp[512];
int size;
if (face!=NULL)
{
FT_Set_Pixel_Sizes(face, 0, fontSize);
va_list argp;
va_start(argp, text);
vsprintf(tmp, text, argp);
va_end(argp);
size = strlen(tmp);
for ( n = 0; n < size; n++ )
{
int pix = 0;
int row, col;
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, tmp[n], FT_LOAD_RENDER );
if ( error ) continue; /* ignore errors */
FT_image=&slot->bitmap;
/* now, draw to our target surface */
for (row=0; row<FT_image->rows; row++)
{
for (col=0; col<FT_image->width; col++)
{
if (FT_image->buffer[pix]>0)
{
// color format is ARGB
color=(FT_image->buffer[pix]<<24)+0xffffff; // Set white color
GRRLIB_Plot(pen_x+slot->bitmap_left+col,pen_y-slot->bitmap_top+row, color);
}
pix++;
}
}
/* increment pen position */
pen_x += slot->advance.x >> 6;
}
}
}
Offline
The problem is that every frame you are rendering the text string using GRRLIB_Plot. You need to render the text string to an image buffer *once*, then draw it to the screen when required.
Offline
or even better you precalculate all charsets, then use them when desired i think i will be very very faster.
Offline
Do you have a example how the created this image buffer and display in on the screen. I all ready did some experiments, did not succeed. The freetype library function generates a ARGB pixel information. How can i convert it to a PNG image with can be display in the GRRLib? Looking forward to your reaction.
Offline
What I'll do over the next couple of days (when time permits!) is "backport **" my freetype code to grrlib 3.0.1a for you and whoever else is interested. I'll also include a small demo program so you can see how it works.
** I say "backport" as the graphics library used by WiiMP (libwiigfx) is derived from grrlib, but I've modified it so much I can't just copy/paste the freetype portion and expect it to work in standard grrlib.
Offline
I'm uploading it now, look for it in the contributions forum in a few minutes Have fun!
Offline
Hi DrTwow,
Your source code is working fine which some text on the screen, but when i call the printfGRRLIB_TextToTexture function around 15 times each video frame the text output is garbages. Therefor i try to change the code to the following idea.
I have adapted your source code a little bit to make more efficient us of the video memory (i allocate 640x480x4 only wants) and then write many text calls (GRRLIB_Printf2) on different location in the same video buffer. Then show this video frame on the screen. But for some unknown reason i do not see any text on the screen. Do you have a idea what i do wrong. See source code below.
wplaat
-------------------------------------------------------------
extern void GRRLIB_InitFreetype(void)
{
unsigned int error = FT_Init_FreeType(&ftLibrary);
if (error) {
exit(0);
}
error = FT_New_Memory_Face(ftLibrary, font_ttf, font_ttf_size, 0, &ftFace);
/* Note: You could also directly load a font from the SD card like this:
error = FT_New_Face(ftLibrary, "fat3:/apps/myapp/font.ttf", 0, &ftFace); */
if (error == FT_Err_Unknown_File_Format) {
exit(0);
} else if (error) {
/* Some other error */
exit(0);
}
}
// Init video buffer
extern void GRRLIB_initTexture2(void)
{
fontTempLayer = (void*) malloc(640 * 480 * 4);
if (fontTempLayer == NULL)
{
/* Oops! Something went wrong! */
exit(0);
}
unsigned int *p = fontTempLayer;
unsigned int loop = 0;
for (loop = 0; loop < (640 * 480); ++loop)
{
*(p++) = 0x00000000;
}
}
// Write text in video buffer
extern void GRRLIB_Printf2(int x, int y, const char *string, unsigned int fontSize, int color)
{
unsigned int error = 0;
int penX = 0;
int penY = fontSize;
FT_GlyphSlot slot = ftFace->glyph;
FT_UInt glyphIndex = 0;
FT_UInt previousGlyph = 0;
FT_Bool hasKerning = FT_HAS_KERNING(ftFace);
/* Convert the string to UTF32 */
size_t length = strlen(string);
wchar_t *utf32 = (wchar_t*)malloc(length * sizeof(wchar_t));
length = mbstowcs(utf32, string, length);
/* Loop over each character, drawing it on to the 4, until the
* end of the string is reached, or until the pixel width is too wide */
unsigned int loop = 0;
for (loop = 0; loop < length; ++loop)
{
glyphIndex = FT_Get_Char_Index(ftFace, utf32[ loop ]);
/* To the best of my knowledge, none of the other freetype
* implementations use kerning, so my method ends up looking
* slightly better */
if (hasKerning && previousGlyph && glyphIndex)
{
FT_Vector delta;
FT_Get_Kerning(ftFace, previousGlyph, glyphIndex, FT_KERNING_DEFAULT, &delta);
penX += delta.x >> 6;
}
error = FT_Load_Glyph(ftFace, glyphIndex, FT_LOAD_RENDER);
if (error) {
/* Whoops, something went wrong trying to load the glyph
* for this character... you should handle this better */
continue;
}
if (BlitGlyph2(&slot->bitmap, penX + slot->bitmap_left+x, penY - slot->bitmap_top+y, color) == true)
{
/* The glyph was successfully blitted to the buffer, move the pen forwards */
penX += slot->advance.x >> 6;
previousGlyph = glyphIndex;
}
else
{
/* BlitGlyph returned false, the line must be full */
free(utf32);
return;
}
}
free(utf32);
}
/* Returns true if the character was draw on to the buffer, false if otherwise */
static bool BlitGlyph2(FT_Bitmap *bitmap, int offset, int top, int color)
{
int bitmapWidth = bitmap->width;
int bitmapHeight = bitmap->rows;
if (offset + bitmapWidth > 640)
{
/* Drawing this character would over run the buffer, so don't draw it */
return false;
}
/* Draw the glyph onto the buffer, blitting from the bottom up */
/* CREDIT: Derived from a function by DragonMinded */
unsigned char *p = fontTempLayer;
unsigned int y = 0;
for (y = 0; y < bitmapHeight; ++y)
{
int sywidth = y * bitmapWidth;
int dywidth = (y + top) * 640;
unsigned int column = 0;
for (column = 0; column < bitmapWidth; ++column)
{
unsigned int srcloc = column + sywidth;
unsigned int dstloc = ((column + offset) + dywidth) << 2;
/* Copy the alpha value for this pixel into the texture buffer */
p[ dstloc + 0 ] = (unsigned char) (color & 0xff);
p[ dstloc + 1 ] = (unsigned char) ((color >> 8) & 0xff);
p[ dstloc + 2 ] = (unsigned char) ((color >> 16) & 0xff);
p[ dstloc + 3 ] = (bitmap->buffer[ srcloc ]);
}
}
return true;
}
/* Render the text string to a 4x4RGBA texture, return a pointer to this texture */
extern void *GRRLIB_GetTexture2(void)
{
/* Create a new buffer, this time to hold the final texture
* in a format suitable for the Wii */
void *texture = memalign(32, 640 * 480 * 4);
/* Convert the RGBA temp buffer to a format usuable by GX */
BitmapTo4x4RGBA(fontTempLayer, texture, 640, 480);
DCFlushRange(texture, 640 * 480 * 4);
/* The temp buffer is no longer required */
free(fontTempLayer);
return texture;
}
int main()
{
// Init video mode .. code is not here
// Init Freetype font library
GRRLIB_InitFreetype();
while(true)
{
// Init text layer
GRRLIB_initTexture2();
GRRLIB_Printf2(10, 10, "Hello World", 8, 0xffffff);
GRRLIB_Printf2(10, 100, "Hello World", 12, 0xffffff);
GRRLIB_Printf2(10, 200, "Hello World", 24, 0xffffff);
GRRLIB_Printf2(10, 300, "Hello World", 48, 0xffffff);
// Draw text layer
void *textTexture = GRRLIB_GetTexture2();
GRRLIB_DrawImg(0, 0, 640, 480, textTexture, 0, 1.0, 1.0, 255);
free(textTexture);
}
}
Last edited by wplaat (2008-11-26 21:14:38)
Offline
I've just spent the last couple of hours trying to work out why your code doesn't work, but I couldn't find a reason... until I noticed the omission of this line:
error = FT_Set_Pixel_Sizes(ftFace, 0, fontSize);
You are never setting the fontSize!
Put that line into your GRRLIB_Printf2 function just after the declarations near the top and it works. (I tested it already!)
You can also simplify the GRRLIB_initTexture2 function like so:
extern void GRRLIB_initTexture2(void) { /* Use calloc instead of malloc if you want to set all the bytes to 0 */ fontTempLayer = (void*) calloc(1, 640 * 480 * 4); if (fontTempLayer == NULL) { /* Oops! Something went wrong! */ exit(0); } }
As a side note, my original example should work fine with as many strings as you want; I use it in my project, and I have many rendered strings on the screen at once, often moving about the screen. The proper way to use it is not to re-render every string every frame, that's a waste of processing resources. Instead, you should have a flag like "textHasChanged" and you re-render the text ONLY when the text needs updating. Anyway, good luck with your project - are you adding freetype support to BibleQuiz?
Offline
Hi DrTwox,
Many, Many thanks for your comments. I will adjust the source code this afternoon and retest it. If it is working stable i will add it in the next release of bibleQuiz (v0.6). Wants again, thanks.....
wplaat
Offline
Hi DrTwox,
I have released bibleQuiz v0.6 on www.wiibrew.org.
Now all texts are created with the freetype library.
I have added your nickname on the credits page in
the bibleQuiz game. Keep on the good work!
wplaat
Last edited by wplaat (2008-11-29 12:51:46)
Offline