History: HowTo: Write text on texture
Source of version: 6 (current)
Copy to clipboard
{maketoc}
!Usage
__str__: String to write on the texture
__destTexture__: Texture to write on
__destRectangle__: The Area to write in
__font__: The font to use (the font's texture must be in PF_BYTE_LA (is usually))
__color__: The color to use
__justify__: 'l' = left aligned, 'c' = centered, 'r' = right aligned
__wordwrap__: if true the line will only be wrapped after a word
!Example usage
{CODE(wrap="1", colors="c++")}Texture* background = TextureManager::getSingleton().getByName("Background.png").getPointer();
Font* font = FontManager::getSingleton().getByName("BlueHighWay").getPointer();
// Make sure the texture is not WRITE_ONLY, we need to read the buffer to do the blending with the font (get the alpha for example)
Texture* texture = TextureManager::getSingleton().createManual("Write Texture","General",TEX_TYPE_2D, 512, 512, MIP_UNLIMITED , PF_X8R8G8B8, Ogre::TU_STATIC|Ogre::TU_AUTOMIPMAP).getPointer();
//Draw the background to the new texture
texture->getBuffer()->blit(background->getBuffer());
WriteToTexture("hello world",texture,Image::Box(25,275,370,500),font,ColourValue(1.0,1.0,1.0,1.0),'c');{CODE}
!Source
{CODE(wrap="1", colors="c++")}void WriteToTexture(const String &str, TexturePtr destTexture, Image::Box destRectangle, Font* font, const ColourValue &color, char justify = 'l', bool wordwrap = true)
{
using namespace Ogre;
if (destTexture->getHeight() < destRectangle.bottom)
destRectangle.bottom = destTexture->getHeight();
if (destTexture->getWidth() < destRectangle.right)
destRectangle.right = destTexture->getWidth();
if (!font->isLoaded())
font->load();
TexturePtr fontTexture = (TexturePtr) TextureManager::getSingleton().getByName(font->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName());
HardwarePixelBufferSharedPtr fontBuffer = fontTexture->getBuffer();
HardwarePixelBufferSharedPtr destBuffer = destTexture->getBuffer();
PixelBox destPb = destBuffer->lock(destRectangle,HardwareBuffer::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
size_t nBuffSize = fontBuffer->getSizeInBytes();
uint8* buffer = (uint8*)calloc(nBuffSize, sizeof(uint8));
// create pixel box using the copy of the buffer
PixelBox fontPb(fontBuffer->getWidth(), fontBuffer->getHeight(),fontBuffer->getDepth(), fontBuffer->getFormat(), buffer);
fontBuffer->blitToMemory(fontPb);
uint8* fontData = static_cast<uint8*>( fontPb.data );
uint8* destData = static_cast<uint8*>( destPb.data );
const size_t fontPixelSize = PixelUtil::getNumElemBytes(fontPb.format);
const size_t destPixelSize = PixelUtil::getNumElemBytes(destPb.format);
const size_t fontRowPitchBytes = fontPb.rowPitch * fontPixelSize;
const size_t destRowPitchBytes = destPb.rowPitch * destPixelSize;
Box *GlyphTexCoords;
GlyphTexCoords = new Box[str.size()];
Font::UVRect glypheTexRect;
size_t charheight = 0;
size_t charwidth = 0;
for(unsigned int i = 0; i < str.size(); i++)
{
if ((str[i] != '\t') && (str[i] != '\n') && (str[i] != ' '))
{
glypheTexRect = font->getGlyphTexCoords(str[i]);
GlyphTexCoords[i].left = glypheTexRect.left * fontTexture->getSrcWidth();
GlyphTexCoords[i].top = glypheTexRect.top * fontTexture->getSrcHeight();
GlyphTexCoords[i].right = glypheTexRect.right * fontTexture->getSrcWidth();
GlyphTexCoords[i].bottom = glypheTexRect.bottom * fontTexture->getSrcHeight();
if (GlyphTexCoords[i].getHeight() > charheight)
charheight = GlyphTexCoords[i].getHeight();
if (GlyphTexCoords[i].getWidth() > charwidth)
charwidth = GlyphTexCoords[i].getWidth();
}
}
size_t cursorX = 0;
size_t cursorY = 0;
size_t lineend = destRectangle.getWidth();
bool carriagreturn = true;
for (unsigned int strindex = 0; strindex < str.size(); 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].getWidth()> lineend) && !carriagreturn )
{
cursorY += charheight;
carriagreturn = true;
}
//justify
if (carriagreturn)
{
size_t l = strindex;
size_t textwidth = 0;
size_t wordwidth = 0;
while( (l < str.size() ) && (str[l] != '\n)'))
{
wordwidth = 0;
switch (str[l])
{
case ' ': wordwidth = charwidth; ++l; break;
case '\t': wordwidth = charwidth *3; ++l; break;
case '\n': l = str.size();
}
if (wordwrap)
while((l < str.size()) && (str[l] != ' ') && (str[l] != '\t') && (str[l] != '\n'))
{
wordwidth += GlyphTexCoords[l].getWidth();
++l;
}
else
{
wordwidth += GlyphTexCoords[l].getWidth();
l++;
}
if ((textwidth + wordwidth) <= destRectangle.getWidth())
textwidth += (wordwidth);
else
break;
}
if ((textwidth == 0) && (wordwidth > destRectangle.getWidth()))
textwidth = destRectangle.getWidth();
switch (justify)
{
case 'c': cursorX = (destRectangle.getWidth() - textwidth)/2;
lineend = destRectangle.getWidth() - cursorX;
break;
case 'r': cursorX = (destRectangle.getWidth() - textwidth);
lineend = destRectangle.getWidth();
break;
default: cursorX = 0;
lineend = textwidth;
break;
}
carriagreturn = false;
}
//abort - net enough space to draw
if ((cursorY + charheight) > destRectangle.getHeight())
goto stop;
//draw pixel by pixel
for (size_t i = 0; i < GlyphTexCoords[strindex].getHeight(); i++ )
for (size_t j = 0; j < GlyphTexCoords[strindex].getWidth(); j++)
{
float alpha = color.a * (fontData[(i + GlyphTexCoords[strindex].top) * fontRowPitchBytes + (j + GlyphTexCoords[strindex].left) * fontPixelSize +1 ] / 255.0);
float invalpha = 1.0 - alpha;
size_t offset = (i + cursorY) * destRowPitchBytes + (j + cursorX) * destPixelSize;
ColourValue pix;
PixelUtil::unpackColour(&pix,destPb.format,&destData[offset]);
pix = (pix * invalpha) + (color * alpha);
PixelUtil::packColour(pix,destPb.format,&destData[offset]);
}
cursorX += GlyphTexCoords[strindex].getWidth();
}//default
}//switch
}//for
stop:
delete[] GlyphTexCoords;
destBuffer->unlock();
// Free the memory allocated for the buffer
free(buffer); buffer = 0;
}{CODE}
---
Alias: (alias(HowTo:_Write_text_on_texture))
This is the same code with C# for Mogre
{CODE(wrap="1", colors="c#")} /// <summary>
/// Alignement du texte
/// </summary>
public enum TextAlignment
{
/// <summary>
/// Alignement à gauche
/// </summary>
Left,
/// <summary>
/// Alignement à droite
/// </summary>
Right,
/// <summary>
/// Alignement au centre
/// </summary>
Center
}
/// <summary>
/// Ecrit un texte dans une texture
/// </summary>
/// <param name="_sText">Texte à écrire</param>
/// <param name="_texturePtrDest">Texture destinataire</param>
/// <param name="_boxDest">Rectangle dans lequel on écrit</param>
/// <param name="_font">Font du texte à écrire</param>
/// <param name="_colourValue">Couleur du texte</param>
/// <param name="_textAlignment">Alignement du texte</param>
/// <param name="_bWordwrap">Retour automatique à la ligne</param>
public static unsafe void WriteToTexture(string _sText, TexturePtr _texturePtrDest, Box _boxDest, Font _font, ColourValue _colourValue, TextAlignment _textAlignment = TextAlignment.Left, bool _bWordwrap = true)
{
//using namespace Ogre;
if (_texturePtrDest.Height < _boxDest.bottom) _boxDest.bottom = _texturePtrDest.Height;
if (_texturePtrDest.Width < _boxDest.right) _boxDest.right = _texturePtrDest.Width;
if (!_font.IsLoaded) _font.Load();
TexturePtr textureFont = TextureManager.Singleton.GetByName(_font.GetMaterial().GetTechnique(0).GetPass(0).GetTextureUnitState(0).TextureName);
HardwarePixelBufferSharedPtr bufferFont = textureFont.GetBuffer();
HardwarePixelBufferSharedPtr bufferTextureDest = _texturePtrDest.GetBuffer();
PixelBox pixelBoxDest = bufferTextureDest.Lock(_boxDest, 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
byte[] byBufferFont = new byte[bufferFont.SizeInBytes];
GCHandle handle = GCHandle.Alloc(byBufferFont, GCHandleType.Pinned);
IntPtr intPtrBufferFont = handle.AddrOfPinnedObject();
// create pixel box using the copy of the buffer
PixelBox pixelBoxFont = new PixelBox(bufferFont.Width, bufferFont.Height, bufferFont.Depth, bufferFont.Format, intPtrBufferFont);
bufferFont.BlitToMemory(pixelBoxFont);
//IntPtr fontData = fontPb.data;
byte* byPtrFontData = (byte*)pixelBoxFont.data.ToPointer();
byte* byPtrDestData = (byte*)pixelBoxDest.data.ToPointer();
uint uiFontPixelSize = PixelUtil.GetNumElemBytes(pixelBoxFont.format);
uint uiDestPixelSize = PixelUtil.GetNumElemBytes(pixelBoxDest.format);
uint fontRowPitchBytes = pixelBoxFont.rowPitch * uiFontPixelSize;
uint destRowPitchBytes = pixelBoxDest.rowPitch * uiDestPixelSize;
Box[] boxGlyphTexCoords = new Box[_sText.Length];
uint uiCharHeight = 0;
uint uiCharWidth = 0;
for (int i = 0; i < _sText.Length; i++)
{
if ((_sText[i] != '\t') && (_sText[i] != '\n') && (_sText[i] != ' '))
{
FloatRect frGlypheText = _font.GetGlyphTexCoords(_sText[i]);
boxGlyphTexCoords[i].left = (uint)(frGlypheText.left * textureFont.SrcWidth);
boxGlyphTexCoords[i].top = (uint)(frGlypheText.top * textureFont.SrcHeight);
boxGlyphTexCoords[i].right = (uint)(frGlypheText.right * textureFont.SrcWidth);
boxGlyphTexCoords[i].bottom = (uint)(frGlypheText.bottom * textureFont.SrcHeight);
if (boxGlyphTexCoords[i].Height > uiCharHeight) uiCharHeight = boxGlyphTexCoords[i].Height;
if (boxGlyphTexCoords[i].Width > uiCharWidth) uiCharWidth = boxGlyphTexCoords[i].Width;
}
}
uint uiCursorX = 0;
uint uiCursorY = 0;
uint uiLineEnd = _boxDest.Width;
bool bCarriagReturn = true;
for (int iStrIndex = 0; iStrIndex < _sText.Length; iStrIndex++)
{
switch (_sText[iStrIndex])
{
case ' ': uiCursorX += uiCharWidth; break;
case '\t': uiCursorX += uiCharWidth * 3; break;
case '\n': uiCursorY += uiCharHeight; bCarriagReturn = true; break;
default:
{
//wrapping
if ((uiCursorX + boxGlyphTexCoords[iStrIndex].Width > uiLineEnd) && !bCarriagReturn)
{
uiCursorY += uiCharHeight;
bCarriagReturn = true;
}
//justify
if (bCarriagReturn)
{
int l = iStrIndex;
uint uiTextWidth = 0;
uint uiWordWidth = 0;
while ((l < _sText.Length) && (_sText[l] != '\n'))
{
uiWordWidth = 0;
switch (_sText[l])
{
case ' ': uiWordWidth = uiCharWidth; ++l; break;
case '\t': uiWordWidth = uiCharWidth * 3; ++l; break;
case '\n': l = _sText.Length; break;
}
if (_bWordwrap)
while ((l < _sText.Length) && (_sText[l] != ' ') && (_sText[l] != '\t') && (_sText[l] != '\n'))
{
uiWordWidth += boxGlyphTexCoords[l].Width;
++l;
}
else
{
uiWordWidth += boxGlyphTexCoords[l].Width;
l++;
}
if ((uiTextWidth + uiWordWidth) <= _boxDest.Width)
uiTextWidth += (uiWordWidth);
else
break;
}
if ((uiTextWidth == 0) && (uiWordWidth > _boxDest.Width))
uiTextWidth = _boxDest.Width;
switch (_textAlignment)
{
case TextAlignment.Center: uiCursorX = (_boxDest.Width - uiTextWidth) / 2;
uiLineEnd = _boxDest.Width - uiCursorX;
break;
case TextAlignment.Right: uiCursorX = (_boxDest.Width - uiTextWidth);
uiLineEnd = _boxDest.Width;
break;
default: uiCursorX = 0;
uiLineEnd = uiTextWidth;
break;
}
bCarriagReturn = false;
}
//abort - net enough space to draw
if ((uiCursorY + uiCharHeight) > _boxDest.Height)
goto STOP;
//draw pixel by pixel
for (uint i = 0; i < boxGlyphTexCoords[iStrIndex].Height; i++)
for (uint j = 0; j < boxGlyphTexCoords[iStrIndex].Width; j++)
{
float fAlpha = (float)(_colourValue.a * (byPtrFontData[(i + boxGlyphTexCoords[iStrIndex].top) * fontRowPitchBytes + (j + boxGlyphTexCoords[iStrIndex].left) * uiFontPixelSize + 1] / 255.0));
float fInvalpha = 1.0f - fAlpha;
uint uiOffset = (i + uiCursorY) * destRowPitchBytes + (j + uiCursorX) * uiDestPixelSize;
ColourValue colourValuePix;
PixelUtil.UnpackColour(&colourValuePix, pixelBoxDest.format, &byPtrDestData[uiOffset]);
colourValuePix = (colourValuePix * fInvalpha) + (_colourValue * fAlpha);
PixelUtil.PackColour(colourValuePix, pixelBoxDest.format, &byPtrDestData[uiOffset]);
}
uiCursorX += boxGlyphTexCoords[iStrIndex].Width;
}
break;
}
}
STOP:
bufferTextureDest.Unlock();
handle.Free();
}{CODE}