Changing the position of multiple fonts dynamically

Sep 23, 2013 at 5:40 PM
Hello and thanks for this awesome API !

I have one question. Is there any way to access the description of a certain DrawString after it has been called ? I am creating a class that dynamically renders the strings and want to write a function that updates the values of a certain string.
Coordinator
Sep 23, 2013 at 5:48 PM
I'm not quite clear on what you are trying to do. With "updates the values", do you mean move a string to a different position from one frame to the next?
For that, just call DrawString again with different x, y coordinates.
If you wish to change the actual text, again call DrawString with a different string.
The font-wrapper only renders text, and strings are not remembered after having been drawn. If you want to keep them around as objects you have to implement that in your application.

If you have many strings that only change position or transform while having static text, you can optimize performance by creating DirectWrite text-layouts instead. If you wish to treat strings as objects I recommend creating a wrapper class around a text-layout and render them with DrawTextLayout.

The samples on the Downloads page contain a few examples of text-layouts and some of them have strings that are moved around between frames.
Sep 23, 2013 at 6:10 PM
Thanks for the quick reply. What I am trying to do is to change the position and text of strings. Maybe I am doing this wrong but I am calling the DrawString function from my render loop. Then when I call it again with a different position or text it just creates another text on the screen. I will post the code for clearification. The Update Sentence function is the one I want to change the position with.

bool FW1Font::Initialize()
{
bool result;

// Initialize the graphics objects from the graphics class
m_hwnd = ServiceLocator::GetGraphicService()->GetHWND();
m_D3D = ServiceLocator::GetGraphicService()->GetDirectXDevice();

// Create the font wrapper and factory
HRESULT hResult = FW1CreateFactory(FW1_VERSION, &m_FW1Factory);
hResult = m_FW1Factory->CreateFontWrapper(m_D3D->GetDevice(), L"Calibri", &m_FontWrapper);
if(!SUCCEEDED(hResult))
{
    MessageBox(m_hwnd, L"The FW1Font object could not be initialized !", L"Error", MB_OK);
    return false;
}

m_FontWrapper->DrawString(
    m_D3D->GetDeviceContext(),
    L"Text",// String
    128.0f,// Font size
    100.0f,// X position
    50.0f,// Y position
    0xff0099ff,// Text color, 0xAaBbGgRr
    0// Flags (for example FW1_RESTORESTATE to keep context states unchanged)
);

return true;
}


void FW1Font::Shutdown()
{
m_FontWrapper->Release();
m_FW1Factory->Release();
}


bool FW1Font::NewSentence(std::string name, FontDesc fontDesc)
{
// Add the sentence to the map
m_Sentences.insert(std::pair<std::string, FontDesc>(name, fontDesc));

return true;
}


bool FW1Font::RenderSentence(std::string name)
{
// Find the sentence object
std::map<std::string, FontDesc>::const_iterator sentenceObject = m_Sentences.find(name);

if(sentenceObject == m_Sentences.end())
{
    MessageBox(m_hwnd, L"Cannot Render Sentence name not found !", L"Error", MB_OK);
    return false;
}

m_FontWrapper->DrawString(
    m_D3D->GetDeviceContext(),
    StringConverter::StringToWCHAR(sentenceObject->second.Text), // String
    StringConverter::StringToWCHAR(sentenceObject->second.FontFamily),
    sentenceObject->second.Size, // Font size
    sentenceObject->second.Position.x, // X position
    sentenceObject->second.Position.y, // Y position
    sentenceObject->second.Color, // Text color, 0xAaBbGgRr
    sentenceObject->second.Flags // Flags (for example FW1_RESTORESTATE to keep context states unchanged)
);

return true;
}

bool FW1Font::UpdatePosition(std::string name)
{
// Find the sentence object
std::map<std::string, FontDesc>::const_iterator sentenceObject = m_Sentences.find(name);

//Update position
}
Coordinator
Sep 23, 2013 at 6:17 PM
If I understand correctly it should work by using 'iterator' instead of 'const_iterator' in UpdatePosition and then do sentenceObject->second.Position.x = new_position, and same for sentenceObject->Text.
Sep 23, 2013 at 6:28 PM
Edited Sep 23, 2013 at 6:33 PM
The map is actually just holding the font descriptions for now. Say I scrap the vector part then the update function would looks like this:

bool FW1Font::UpdatePosition(int newPositionX, int newPositionY, WCHAR* newText)
{
//Update position
m_FontWrapper->DrawString(
    m_D3D->GetDeviceContext(),
    newText, // String
    "Arial",
    100.0f, // Font size
    newPositionX, // X position
    newPositionY, // Y position
    0xff0099ff, // Text color, 0xAaBbGgRr
    FW1_RESTORESTATE // Flags (for example FW1_RESTORESTATE to keep context states unchanged)
);
}

I then call it in my update loop and it will create a new text on the screen instead of updating the existing one created by the RenderSentence function.
So I am am trying to figure out how to change the existing text instead of creating a new one.
Coordinator
Sep 23, 2013 at 6:37 PM
DrawString basically "draws pixels on the screen", though through submitting vertices to D3D11.
Moving those pixels afterwards is not possible, so you need to only call DrawString once for each string per frame, with a D3D Clear between frames, the common double-buffered flow of redrawing everything and then presenting everything at once.
I think you should keep the vector and draw all strings when their final positions for the current frame is determined.

If you're not working with frames with clear in between, but rather rely on old pixels to still be there in the next frame, then you would have to erase the previous text before drawing the new one..
Sep 23, 2013 at 7:22 PM
Edited Sep 23, 2013 at 7:28 PM
I got it ! Had to think about it for a moment :) You were right about adding the new position to the map it was all I had to do. The RenderSentence function then uses the updated values.

Thank you very much for the help and the API ! It's so much easier than writing your own font system or accessing DirectWrite over GDI manually :)
Coordinator
Sep 23, 2013 at 7:25 PM
Great! Thanks! Glad it is useful :)