Table of contents
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
Copy to clipboard
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');
Source
Copy to clipboard
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; }
Alias: HowTo:_Write_text_on_texture
This is the same code with C# for Mogre
Copy to clipboard
/// <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(); }