Font Rendering

roimatola

13-10-2007 18:44:08

Did anyone suceeded in rendering a font to a texture? I wanted to make a billboard with a timer, but i'm having troubles writing the time to a texture. I tried to port this method
http://www.ogre3d.org/wiki/index.php/HowTo:_Write_text_on_texture
like this

private unsafe void WriteToTexture(String str, TexturePtr destTexture, Box destRectangle, FontPtr font, ColourValue color)
{
char justify='1';
bool wordwrap = true;

if (destTexture.Height < destRectangle.bottom)
destRectangle.bottom = destTexture.Height;
if (destTexture.Width < destRectangle.right)
destRectangle.right = destTexture.Width;

if (!font.IsLoaded)
font.Load();

TexturePtr fontTexture = (TexturePtr) TextureManager.Singleton.GetByName(font.GetMaterial().GetTechnique(0).GetPass(0).GetTextureUnitState(0).TextureName);

HardwarePixelBufferSharedPtr fontBuffer = fontTexture.GetBuffer();
HardwarePixelBufferSharedPtr destBuffer = destTexture.GetBuffer();

PixelBox destPb = destBuffer.Lock(destRectangle, HardwareBuffer.LockOptions.HBL_NORMAL);

// The font texture buffer was created write only...so we cannot read it back :o). One solution is to copy the buffer instead of locking it. (Maybe there is a way to create a font texture which is not write_only ?)

// create a buffer
uint nBuffSize = fontBuffer.SizeInBytes;

IntPtr buffer = IntPtr.Zero;

buffer = Marshal.AllocHGlobal((int)nBuffSize);

// create pixel box using the copy of the buffer
PixelBox fontPb = new PixelBox(fontBuffer.Width, fontBuffer.Height,fontBuffer.Depth, fontBuffer.Format, buffer);
fontBuffer.BlitToMemory(fontPb);

IntPtr fontData = fontPb.data;
IntPtr destData = destPb.data;

uint fontPixelSize = PixelUtil.GetNumElemBytes(fontPb.format);
uint destPixelSize = PixelUtil.GetNumElemBytes(destPb.format);

uint fontRowPitchBytes = fontPb.rowPitch * fontPixelSize;
uint destRowPitchBytes = destPb.rowPitch * destPixelSize;

Box[] GlyphTexCoords = new Box[str.Length];

FloatRect glypheTexRect;
uint charheight = 0;
uint charwidth = 0;

for(int i = 0; i < str.Length; i++)
{
if ((str[i] != '\t') && (str[i] != '\n') && (str[i] != ' '))
{
glypheTexRect = font.GetGlyphTexCoords(str[i]);
GlyphTexCoords[i].left = (uint)glypheTexRect.left * fontTexture.SrcWidth;
GlyphTexCoords[i].top = (uint)glypheTexRect.top * fontTexture.SrcHeight;
GlyphTexCoords[i].right = (uint)glypheTexRect.right * fontTexture.SrcWidth;
GlyphTexCoords[i].bottom = (uint)glypheTexRect.bottom * fontTexture.SrcHeight;

if (GlyphTexCoords[i].Height > charheight)
charheight = GlyphTexCoords[i].Height;
if (GlyphTexCoords[i].Width > charwidth)
charwidth = GlyphTexCoords[i].Width;
}
}

uint cursorX = 0;
uint cursorY = 0;
uint lineend = destRectangle.Width;
bool carriagreturn = true;
for (int strindex = 0; strindex < str.Length; strindex++)
{
switch(str[strindex])
{
case ' ':
cursorX += charwidth;
break;
case '\t':
cursorX += charwidth * 3;
break;
case '\n':cursorY += charheight;
carriagreturn = true;
break;
default:
{
//wrapping
if ((cursorX + GlyphTexCoords[strindex].Width> lineend) && !carriagreturn )
{
cursorY += charheight;
carriagreturn = true;
}

//justify
if (carriagreturn)
{
int l = strindex;
uint textwidth = 0;
uint wordwidth = 0;

while( (l < str.Length ) && (str[l] != '\n'))
{
wordwidth = 0;

switch (str[l])
{
case ' ':
wordwidth = charwidth; ++l;
break;
case '\t':
wordwidth = charwidth *3;
++l;
break;
case '\n':
l = str.Length;
break;
}

if (wordwrap)
while((l < str.Length) && (str[l] != ' ') && (str[l] != '\t') && (str[l] != '\n'))
{
wordwidth += GlyphTexCoords[l].Width;
++l;
}
else
{
wordwidth += GlyphTexCoords[l].Width;
l++;
}

if ((textwidth + wordwidth) <= destRectangle.Width)
textwidth += (wordwidth);
else
break;
}

if ((textwidth == 0) && (wordwidth > destRectangle.Width))
textwidth = destRectangle.Width;

switch (justify)
{
case 'c':
cursorX = (destRectangle.Width - textwidth)/2;
lineend = destRectangle.Width - cursorX;
break;

case 'r':
cursorX = (destRectangle.Width - textwidth);
lineend = destRectangle.Width;
break;

default:
cursorX = 0;
lineend = textwidth;
break;
}

carriagreturn = false;
}

//abort - not enough space to draw
if ((cursorY + charheight) > destRectangle.Height)
goto stop;

//draw pixel by pixel
for (uint i = 0; i <= GlyphTexCoords[strindex].Height; i++ )
for (uint j = 0; j <= GlyphTexCoords[strindex].Width; j++)
{
int jalupa = (int)((i + GlyphTexCoords[strindex].top) * fontRowPitchBytes + (j + GlyphTexCoords[strindex].left) * fontPixelSize +1);
IntPtr farfe = Marshal.ReadIntPtr(fontData,jalupa);
float alpha = (float) (color.a * (farfe.ToInt32() / 255.0));
float invalpha = (float) 1.0 - alpha;
uint offset = (i + cursorY) * destRowPitchBytes + (j + cursorX) * destPixelSize;
ColourValue pix = ColourValue.Green;
void* destinyData = (void*)Marshal.ReadIntPtr(destData, (int)offset);
try
{
PixelUtil.UnpackColour(&pix, destPb.format, destinyData);
}
catch (AccessViolationException) { }
pix = (pix * invalpha) + (color * alpha);
try
{
PixelUtil.PackColour(pix, destPb.format, (void*)Marshal.ReadIntPtr(destData, (int)offset));
}
catch (AccessViolationException) { }
}

cursorX += GlyphTexCoords[strindex].Width;
}//default
break;
}//switch
}//for
stop:
destBuffer.Unlock();
// Free the memory allocated for the buffer
Marshal.FreeHGlobal(buffer);

}


but i have AcessViolationExceptions in both PixelUtil.UnpackColour() and PixelUtil.PackColour();

What am i doing wrong? Is there a easier way to write text on a texture? Did anyone managed to port this method?

Thanks in advanced

roimatola

14-10-2007 16:16:51

nobody? No one never needed to make a timer, or anything that involved rendering a font to a texture? I'm making a workaround with image files, but this is a bit stupid.

Vera

25-03-2008 17:21:43

I made some changes and it worked for me.
Here is the code i'm using:


private unsafe void WriteToTexture(String str, TexturePtr destTexture, Box destRectangle, FontPtr font, ColourValue color, char justify, bool wordwrap)
{
if (destTexture.Height < destRectangle.bottom)
destRectangle.bottom = destTexture.Height;
if (destTexture.Width < destRectangle.right)
destRectangle.right = destTexture.Width;

if (!font.IsLoaded)
font.Load();

TexturePtr fontTexture = (TexturePtr)TextureManager.Singleton.GetByName(font.GetMaterial().GetTechnique(0).GetPass(0).GetTextureUnitState(0).TextureName);

HardwarePixelBufferSharedPtr fontBuffer = fontTexture.GetBuffer();
HardwarePixelBufferSharedPtr destBuffer = destTexture.GetBuffer();

PixelBox destPb = destBuffer.Lock(destRectangle, HardwareBuffer.LockOptions.HBL_NORMAL);

// The font texture buffer was created write only...so we cannot read it back :o). One solution is to copy the buffer instead of locking it. (Maybe there is a way to create a font texture which is not write_only ?)

// create a buffer
uint nBuffSize = fontBuffer.SizeInBytes;

IntPtr buffer = Marshal.AllocHGlobal((int)nBuffSize);

// create pixel box using the copy of the buffer
PixelBox fontPb = new PixelBox(fontBuffer.Width, fontBuffer.Height, fontBuffer.Depth, fontBuffer.Format, buffer);
fontBuffer.BlitToMemory(fontPb);

byte* fontData = (byte*)fontPb.data;
byte* destData = (byte*)destPb.data;

uint fontPixelSize = PixelUtil.GetNumElemBytes(fontPb.format);
uint destPixelSize = PixelUtil.GetNumElemBytes(destPb.format);

uint fontRowPitchBytes = fontPb.rowPitch * fontPixelSize;
uint destRowPitchBytes = destPb.rowPitch * destPixelSize;

Box[] GlyphTexCoords = new Box[str.Length];

FloatRect glypheTexRect;
uint charheight = 0;
uint charwidth = 0;

for (int i = 0; i < str.Length; i++)
{
if ((str[i] != '\t') && (str[i] != '\n') && (str[i] != ' '))
{
glypheTexRect = font.GetGlyphTexCoords(str[i]);
GlyphTexCoords[i].left = (uint)(glypheTexRect.left * fontTexture.SrcWidth);
GlyphTexCoords[i].top = (uint)(glypheTexRect.top * fontTexture.SrcHeight);
GlyphTexCoords[i].right = (uint)(glypheTexRect.right * fontTexture.SrcWidth);
GlyphTexCoords[i].bottom = (uint)(glypheTexRect.bottom * fontTexture.SrcHeight);

if (GlyphTexCoords[i].Height > charheight)
charheight = GlyphTexCoords[i].Height;
if (GlyphTexCoords[i].Width > charwidth)
charwidth = GlyphTexCoords[i].Width;
}
}

uint cursorX = 0;
uint cursorY = 0;
uint lineend = destRectangle.Width;
bool carriagreturn = true;
for (int strindex = 0; strindex < str.Length; strindex++)
{
switch (str[strindex])
{
case ' ':
cursorX += charwidth;
break;
case '\t':
cursorX += charwidth * 3;
break;
case '\n': cursorY += charheight;
carriagreturn = true;
break;
default:
{
//wrapping
if ((cursorX + GlyphTexCoords[strindex].Width > lineend) && !carriagreturn)
{
cursorY += charheight;
carriagreturn = true;
}

//justify
if (carriagreturn)
{
int l = strindex;
uint textwidth = 0;
uint wordwidth = 0;

while ((l < str.Length) && (str[l] != '\n'))
{
wordwidth = 0;

switch (str[l])
{
case ' ':
wordwidth = charwidth; ++l;
break;
case '\t':
wordwidth = charwidth * 3;
++l;
break;
case '\n':
l = str.Length;
break;
}

if (wordwrap)
while ((l < str.Length) && (str[l] != ' ') && (str[l] != '\t') && (str[l] != '\n'))
{
wordwidth += GlyphTexCoords[l].Width;
++l;
}
else
{
wordwidth += GlyphTexCoords[l].Width;
l++;
}

if ((textwidth + wordwidth) <= destRectangle.Width)
textwidth += (wordwidth);
else
break;
}

if ((textwidth == 0) && (wordwidth > destRectangle.Width))
textwidth = destRectangle.Width;

switch (justify)
{
case 'c':
cursorX = (destRectangle.Width - textwidth) / 2;
lineend = destRectangle.Width - cursorX;
break;

case 'r':
cursorX = (destRectangle.Width - textwidth);
lineend = destRectangle.Width;
break;

default:
cursorX = 0;
lineend = textwidth;
break;
}

carriagreturn = false;
}

//abort - not enough space to draw
if ((cursorY + charheight) > destRectangle.Height)
{
destBuffer.Unlock();
Marshal.FreeHGlobal(buffer);
}

//draw pixel by pixel
for (uint i = 0; i <= GlyphTexCoords[strindex].Height; i++)
for (uint j = 0; j <= GlyphTexCoords[strindex].Width; j++)
{

float alpha = color.a * (fontData[(i + GlyphTexCoords[strindex].top) * fontRowPitchBytes + (j + GlyphTexCoords[strindex].left) * fontPixelSize + 1] / 255.0f);
float invalpha = (float)1.0 - alpha;
uint offset = (i + cursorY) * destRowPitchBytes + (j + cursorX) * destPixelSize;
ColourValue pix = ColourValue.Green;

try
{
PixelUtil.UnpackColour(&pix, destPb.format, &destData[offset]);
}
catch (AccessViolationException) { }

pix = (pix * invalpha) + (color * alpha);
try
{
PixelUtil.PackColour(pix, destPb.format, &destData[offset]);
}
catch (AccessViolationException) { }
}

cursorX += GlyphTexCoords[strindex].Width;
}
break;
}
}
}

bacardicola

14-12-2009 13:12:53

Hello,

i´m using the code snippet from Vera to write a text on another texture. And I generate a new material with the new texture.
To show the texture i set up a billboard with the new material.

My code to set up the texture:
public void WriteTextOnTexture()
{
TexturePtr background = TextureManager.Singleton.Load("Billboard512.png", "General");
FontPtr font = (FontPtr)FontManager.Singleton.GetByName("BlueHighway");

TexturePtr texture = TextureManager.Singleton.CreateManual("WriteTexture", "General", TextureType.TEX_TYPE_2D, 512, 512, 5, PixelFormat.PF_B8G8R8A8);

texture.GetBuffer().Blit(background.GetBuffer());

WriteToTexture("Hello World", texture, new Box(5, 5, 250, 250), font, new ColourValue(1, 1, 1), 'c', true);

MaterialPtr mat = MaterialManager.Singleton.Create("NewMat", "General");
mat.SetSceneBlending(SceneBlendType.SBT_TRANSPARENT_ALPHA);
mat.GetTechnique(0).GetPass(0).CreateTextureUnitState("WriteTexture");

BillboardSet bbset = win.SceneManager.CreateBillboardSet("bb1");
Billboard bb1 = bbset.CreateBillboard(new Vector3(0, 30, 0));
bb1.TexcoordRect = new FloatRect(0, 0, 1, 1);
bbset.MaterialName = "NewMat";
bb1.SetDimensions(30, 30);

SceneNode bb1Node = win.SceneManager.RootSceneNode.CreateChildSceneNode("bb1");
bb1Node.AttachObject(bbset);
bb1Node.Position = new Vector3(50, 50, -50);
}


My problem is when I´m using Direct3D9 to render, the code works fine, but when I´m using OpenGl nothing is displayed. Respectively a black texture is shown when SetSceneBlending is deactivated. And I have to use OpenGl! :(
I use the actual branch version of Mogre 1.4

I have allready read different topics in ogre and addons forum but i haven´t found a solution. :(
pls, help...

andyhebear1

21-05-2010 14:09:07

http://www.sirikata.com/blog/?p=115