HowTo: Write text on texture         How to write text onto a texture

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

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

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

/// <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();

    }