Using Libtcod, how to console->print a string with a dynamic amount of colors? - c++

I've got a helper function that accepts a string and a vector of colors to use to format the string and right now my solution is to manually check the size of the color vector and call the console print with that same amount of colors.
Say I've got a color vector of 4, in the code it'd do something like:
void helper_func(TCODConsole* con, std::string msg_str, std::vector<TCOD_colctrl_t> color_vector)
{
char* message = msg_str.c_str();
//this is repeated 1 through 16, adding another color_vector.at(n) for each.
...
else if (color_vector.size() == 2)
//message might be "%cHello%c was in red"
console->print(x, y, message, color_vector.at(0), color_vector.at(1))
...
else if (color_vector.size() == 4)
//message might be "%cThe octopus%c shimmers at %cnight%c"
console->print(x, y, message, color_vector.at(0), color_vector.at(1), color_vector.at(2), color_vector.at(3))
...
}
While this works, it's awful and I was looking into different ways of pulling it off, allowing for more than 16 colors, etc.
I've tried doing a sprintf for each color in the vector, adding it to the out_string and repeating. I've tried doing the same with an ostringstream. I've tried splitting the msg_str on "%c" and then joining the resulting strings once I've added the color in to each. It never worked out, always either using the first color and then using random characters instead of colors from there on out.
I was hopeful that any of the above would work because simply sprintf(out_char, format_msg, TCOD_COLCTRL_1) prints to the console (using console->print(out_char)) just fine.
My question is: is there a good way to pass a varying number of colors to the console->print function and have it accurately display those colors, without severe code redundancy?
As a fallback, I could print out a section of the string up to the first color, calculate its size, move x over by that much and print the next section, but that's not ideal.
I suppose that this question could be generalized to asking the same thing about regular printf with substitutions too.

One possible alternative to a variadic functions might involve parsing msg_str for "%c" and iteratively printing each segment of the string in the correct color according to color_vector. I'm not sure if this code below will compile--I wrote it in notepad, so it might need some work. Hopefully you get the gist of what I'm suggesting.
void helper_func(TCODConsole* con, std::string msg_str, std::vector<TCOD_colctrl_t> color_vector)
{
std::string str2;
std::size_t pos;
std::size_t pos2;
pos = msg_str.find("%c");
if (pos != std::string::npos)
str2 = msg_str.substr(0,pos);
else
str2 = msg_str;
console->print(x, y, str2.c_str());
int n = 0;
while (pos != std::string::npos) {
pos2 = msg_str.find("%c",pos+1);
if (pos2 != std::string::npos)
str2 = msg_str.substr(pos+2,pos2);
else
str2 = msg_str.substr(pos2+2,msg_str.length()-pos2+2);
console->print(x, y, str2.c_str(),color_vector.at(n));
pos = pos2;
n++;
}
}
I thought I should mention, there is a problem in my code. The x value in the second print statement needs to be calculated each time through the while loop since x changes as a function of pos2. Otherwise, everything will just keep printing in the same spot. :) Should be an easy change...

Related

Float value with 2 decimal places causes infinite loop

I'm trying to write a method that changes font-size in a html string. When I click "minus" button, I multiply the current size with 0.8, when I click "plus" button, I multiply the current size with 1.25!
The problem is at the htmlStyle.replace(pos, length, newString); line, at the 'newString' parameter. I figured out, that if I pass a string which contains a number in string format which was multiplied with for example 1.2, that's ok. But if I pass a number in string format which was multiplied with 1.22 or 1.34 or any floating point number with two decimal places, it causes an infinite loop and the app crashes.
I really don't get what's the problem here, since these are strings, and problem occurs after the 'newString' was calculated successfully.
Any ideas? I tried to figure this for 2 days, but I'm still clueless...
Here's the full method:
QString RichTextSize::setSizes(QString htmlStyle, float multiplier)
{
QRegExp rx("(\\d+)pt");
int pos = 0, length = 0;
QString newString;
while ((pos = rx.indexIn(htmlStyle, pos)) != -1) {
length = rx.cap(1).length();
newString.setNum(rx.cap(1).toInt()*multiplier);
htmlStyle.replace(pos, length, newString);
pos += rx.matchedLength();
}
return htmlStyle;
}
EDIT: I got a notification about a possible duplicate question. Well, my problem is not floating point math, but it's realted to float somehow. Floating point operations work without problems. When the trouble happens, that's with a float number converted to string. It converts successfully, but in the 'replace()' method it causes infinite loop. It's realted to floating point decimal places somehow, but since that operation works and problem is with a string converted from that number, I don't understand what happens here.
You're using pos as an iterator on the string, yet by modifying the string you're invalidating the iterator. The loop is not guaranteed to terminate anymore. And, as you've found out, it doesn't.
The invalid assumption you're making, can be made explicit:
while ((pos = rx.indexIn(htmlStyle, pos)) != -1) {
length = rx.cap(1).length();
newString.setNum(rx.cap(1).toInt()*multiplier);
htmlStyle.replace(pos, length, newString);
Q_ASSERT(rx.matchedLength() == newString.size());
pos += rx.matchedLength();
}
As soon as that assertion doesn't hold, the loop might not terminate anymore, or you might be accessing the string past its length, etc.
You should ideally iterate a constant string, so that the iterator stays valid, and build a new output string. This will be faster, too.
Another alternative is to fix the iterator update so that it doesn't get invalidated. Hint: pos += rx.matchedLength() is based on an invalidated part of the string: it was true right before the htmlStyle.replace, but isn't anymore. Perhaps you meant pos += newString.size().

Split a even-numbered string in c++

I am very new to c++. I am trying to split a string that contains even numbered sub strings till there is no even numbered sub string left. For example, if I input AB ABCD ABC, the output should be A B A B C D ABC. I am trying to do it without tokens, because I don't know how to..
What I have so far only split the first even sub string and it doesn't work if I only have 1 sub string. Can someone please help me out?
Any advise will be much appreciated. Thank you!
string temp = "";
void check(string &str, int &i, int &flag)
{
int count = 0;
int reminder;
do
{
count++;
temp += str[i];
i++;
} while (str[i] != ' ');
i = i - temp.size();
reminder = count % 2;
if (reminder == 0)
flag = 1;
else
flag = 0;
}
void SplitEvenWord(string &str)
{
int i = 0;
int flag = 0;
for (i = 0; i < str.size(); i++)
{
check(str, i, flag);
if (flag == 1)
{
temp.insert(temp.size() / 2, " ");
str.replace(i, temp.size() - 1, temp);
}
}
}
There are two skills that are absolutely vital in software engineering (Well, more than two, but two for now): developing new functions in isolation, and testing things in the simplest possible way.
You say that the code fails if there is only one substring. You don't say how it fails (I should have mentioned clear error reports in the list) so I don't know whether to test your code with an even-length string which it ought to split ("ABCD" => "A B C D") or an odd-length string which it ought to leave alone ("ABC" => "ABC"). Before I try to code these up, I look at your first function:
void check(string &str, int &i, int &flag)
{
...
do
{
count++;
temp += str[i];
i++;
} while (str[i] != ' ');
...
}
Trouble already. The strings I have in mind do not contain any spaces, so the loop cannot terminate. This code will run past the end of the string into whatever happens to be in that memory space, which will cause undefined behavior. (If you don't know that term, it means that there's no telling what will happen, but if you're lucky the program will just crash.)
Fix that, try running that code on "ABC" and "ABCD" and "A" and "" and "ABC DEF", and get it working perfectly. Once it does, take a look at your other function. Don't test it with random typing, test it with short, clearly defined strings. Once it works perfectly, try longer, more complicated ones. If you find a string which causes it to fail, hold onto it! That string will lead you to a bug.
That should be enough to get you started.
I'm writing this as an answer because it was too long to fit as a comment.
I have a couple of suggestions that may help you to figure out what the problem is.
Separate "check" into at least two functions, one to split the string into individual words and check them and one to check the length of the string.
Test the "check" and "tokenize" functions by separately and see if they give you the expected answers. Work on them individually until they are correct.
Separate the formatting of the answers out of "SplitEvenWord" into a separate function.
"SplitEvenWord" should then be nothing more than calling the functions you created as a result of the steps above.
When I'm stuck, I always try to break the problem down into small bite sized pieces that I know I can get working. Eventually, the problem becomes assembling the already working pieces of the solution into a larger function that solves the original problem.

grabbing data sets from a file with an arbitrary amount of spaces

**No direct answers or code examples please, this is my homework which i need to learn from. I'm looking for help concerning the algorithm i need to develop.
I seem to be having a logic error in coming up with a solution for a portion of my class work, the program involves multiple files, but here is the only relevant portion:
I have a file PlayerStats that holds the stats for a basketball player in:
rebounds
points
assists
uniform #
my initial reaction would be to create a while loop and read these into a temporary struct that holds these values, then create a merge function that merges the values of the temp struct with the inital array of records, simple enough?
struct Baller
{
//other information on baller
int rebounds;
int assists;
int uniform;
int points;
void merge(Baller tmp); //merge the data with the array of records
}
//in my read function..
Baller tmp;
int j = 0;
inFile << tmp.uniform << tmp.assists << tmp.points << tmp.rebounds
while(inFile){
ArrayRecords[j].merge(tmp);
j++;
//read in from infile again
}
The catch:
The file can have an arbitrary number of spaces between the identifiers, and the information can be in any order(leaving out the uniform number, that is always first). e.g.
PlayerStats could be
11 p3 a12 r5 //uniform 11, 3 points 12 assists 5 rebounds
//other info
OR
11 p 3 r 5 a 12 //same exact values
What I've come up with
can't seem to think of an algorithm to grab these values from the file in the correct order, i was thinking of something along these lines:
inFile << tmp.uniform; //uniform is ALWAYS first
getline(inFile,str); //get the remaining line
int i = 0;
while(str[i] == " ") //keep going until i find something that isnt space
i++;
if(str[i] == 'p') //heres where i get stuck, how do i find that number now?
else if(str[i] == 'a')
eles if(str[i] = 'r'
If you're only going to check one letter, you could use a switch statement instead of if / else, that would make it easier to read.
You know where the number starts at that point, (hint: str[i+1]), so depending on what type your str[] is, you can either use atoi if its a char array, or std::stringstream if it's an std::string.
I'm tempted to give you some code, but you said not too. If you do want some, let me know and I'll edit the answer with some code.
Instead of using a 'merge' function, try using an std::vector so you can just push_back your structure instead of doing any 'merging'. Besides, your merge function is basically a copy assignment operator, which is created by the compiler by default (you don't need to create a 'merge' function), you just need to use = to copy the data across. If you wanted to do something special in your 'merge' function, then you should overload the copy assignment operator instead of a 'merge' function. Simples.
Do something like that:
int readNumber () {
while isdigit (nextchar) -> collect in numberstring or directly build number
return that number;
}
lineEater () {
Read line
skip over spaces
uniform=readNumber ();
haveNum=false;
haveWhat=false;
Loop until eol {
skip over spaces
if (isdigit)
number=readNumber ();
skip over spaces
haveNum=true;
else
char=nextChar;
haveWhat=true;
if (haveChar and haveNum) {
switch (char) {
case 'p' : points=number; break;
...
}
haveNum=false;
haveWhat=false;
}
}
or, if you are more ambitous, write a grammar for your input and use lex/yacc.

Better, or advantages in different ways of coding similar functions

I'm writing the code for a GUI (in C++), and right now I'm concerned with the organisation of text in lines. One of the problems I'm having is that the code is getting very long and confusing, and I'm starting to get into a n^2 scenario where for every option I add in for the texts presentation, the number of functions I have to write is the square of that. In trying to deal with this, A particular design choice has come up, and I don't know the better method, or the extent of the advantages or disadvantages between them:
I have two methods which are very similar in flow, i.e, iterate through the same objects, taking into account the same constraints, but ultimately perform different operations between this flow. For anyones interest, the methods render the text, and determine if any text overflows the line due to wrapping the text around other objects or simply the end of the line respectively.
These functions need to be copied and rewritten for left, right or centred text, which have different flow, so whatever design choice I make would be repeated three times.
Basically, I could continue what I have now, which is two separate methods to handle these different actions, or I could merge them into one function, which has if statements within it to determine whether or not to render the text or figure out if any text overflows.
Is there a generally accepted right way to going about this? Otherwise, what are the tradeoffs concerned, what are the signs that might indicate one way should be used over the other? Is there some other way of doing things I've missed?
I've edited through this a few times to try and make it more understandable, but if it isn't please ask me some questions so I can edit and explain. I can also post the source code of the two different methods, but they use a lot of functions and objects that would take too long to explain.
// EDIT: Source Code //
Function 1:
void GUITextLine::renderLeftShifted(const GUIRenderInfo& renderInfo) {
if(m_renderLines.empty())
return;
Uint iL = 0;
Array2t<float> renderCoords;
renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[0].s_x;
renderCoords.s_y = renderInfo.s_offset.s_y + m_y;
float remainingPixelsInLine = m_renderLines[0].s_y;
for (Uint iTO= 0;iTO != m_text.size();++iTO)
{
if(m_text[iTO].s_pixelWidth <= remainingPixelsInLine)
{
string preview = m_text[iTO].s_string;
m_text[iTO].render(&renderCoords);
remainingPixelsInLine -= m_text[iTO].s_pixelWidth;
}
else
{
FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();
float characterWidth = 0;
Uint iFirstCharacterOfRenderLine = 0;
for(Uint iC = 0;;++iC)
{
if(iC == m_text[iTO].s_string.size())
{
// wrap up
string renderPart = m_text[iTO].s_string;
renderPart.erase(iC, renderPart.size());
renderPart.erase(0, iFirstCharacterOfRenderLine);
m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
&renderCoords);
break;
}
characterWidth += m_text[iTO].s_font->getWidthOfGlyph(intData,
m_text[iTO].s_string[iC]);
if(characterWidth > remainingPixelsInLine)
{
// Can't push in the last character
// No more space in this line
// First though, render what we already have:
string renderPart = m_text[iTO].s_string;
renderPart.erase(iC, renderPart.size());
renderPart.erase(0, iFirstCharacterOfRenderLine);
m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
&renderCoords);
if(++iL != m_renderLines.size())
{
remainingPixelsInLine = m_renderLines[iL].s_y;
renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[iL].s_x;
// Cool, so now try rendering this character again
--iC;
iFirstCharacterOfRenderLine = iC;
characterWidth = 0;
}
else
{
// Quit
break;
}
}
}
}
}
// Done! }
Function 2:
vector GUITextLine::recalculateWrappingContraints_LeftShift()
{
m_pixelsOfCharacters = 0;
float pixelsRemaining = m_renderLines[0].s_y;
Uint iRL = 0;
// Go through every text object, fiting them into render lines
for(Uint iTO = 0;iTO != m_text.size();++iTO)
{
// If an entire text object fits in a single line
if(pixelsRemaining >= m_text[iTO].s_pixelWidth)
{
pixelsRemaining -= m_text[iTO].s_pixelWidth;
m_pixelsOfCharacters += m_text[iTO].s_pixelWidth;
}
// Otherwise, character by character
else
{
// Get some data now we don't get it every function call
FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();
for(Uint iC = 0; iC != m_text[iTO].s_string.size();++iC)
{
float characterWidth = m_text[iTO].s_font->getWidthOfGlyph(intData, '-');
if(characterWidth < pixelsRemaining)
{
pixelsRemaining -= characterWidth;
m_pixelsOfCharacters += characterWidth;
}
else // End of render line!
{
m_pixelsOfWrapperCharacters += pixelsRemaining; // we might track how much wrapping px we use
// If this is true, then we ran out of render lines before we ran out of text. Means we have some overflow to return
if(++iRL == m_renderLines.size())
{
return harvestOverflowFrom(iTO, iC);
}
else
{
pixelsRemaining = m_renderLines[iRL].s_y;
}
}
}
}
}
vector<GUIText> emptyOverflow;
return emptyOverflow; }
So basically, render() takes renderCoordinates as a parameter and gets from it the global position of where it needs to render from. calcWrappingConstraints figures out how much text in the object goes over the allocated space, and returns that text as a function.
m_renderLines is an std::vector of a two float structure, where .s_x = where rendering can start and .s_y = how large the space for rendering is - not, its essentially width of the 'renderLine', not where it ends.
m_text is an std::vector of GUIText objects, which contain a string of text, and some data, like style, colour, size ect. It also contains under s_font, a reference to a font object, which performs rendering, calculating the width of a glyph, ect.
Hopefully this clears things up.
There is no generally accepted way in this case.
However, common practice in any programming scenario is to remove duplicated code.
I think you're getting stuck on how to divide code by direction, when direction changes the outcome too much to make this division. In these cases, focus on the common portions of the three algorithms and divide them into tasks.
I did something similar when I duplicated WinForms flow layout control for MFC. I dealt with two types of objects: fixed positional (your pictures etc.) and auto positional (your words).
In the example you provided I can list out common portions of your example.
Write Line (direction)
bool TestPlaceWord (direction) // returns false if it cannot place word next to previous word
bool WrapPastObject (direction) // returns false if it runs out of line
bool WrapLine (direction) // returns false if it runs out of space for new line.
Each of these would be performed no matter what direction you are faced with.
Ultimately, the algorithm for each direction is just too different to simplify anymore than that.
How about an implementation of the Visitor Pattern? It sounds like it might be the kind of thing you are after.

Optimizing WordWrap Algorithm

I have a word-wrap algorithm that basically generates lines of text that fit the width of the text. Unfortunately, it gets slow when I add too much text.
I was wondering if I oversaw any major optimizations that could be made. Also, if anyone has a design that would still allow strings of lines or string pointers of lines that is better I'd be open to rewriting the algorithm.
Thanks
void AguiTextBox::makeLinesFromWordWrap()
{
textRows.clear();
textRows.push_back("");
std::string curStr;
std::string curWord;
int curWordWidth = 0;
int curLetterWidth = 0;
int curLineWidth = 0;
bool isVscroll = isVScrollNeeded();
int voffset = 0;
if(isVscroll)
{
voffset = pChildVScroll->getWidth();
}
int AdjWidthMinusVoffset = getAdjustedWidth() - voffset;
int len = getTextLength();
int bytesSkipped = 0;
int letterLength = 0;
size_t ind = 0;
for(int i = 0; i < len; ++i)
{
//get the unicode character
letterLength = _unicodeFunctions.bringToNextUnichar(ind,getText());
curStr = getText().substr(bytesSkipped,letterLength);
bytesSkipped += letterLength;
curLetterWidth = getFont().getTextWidth(curStr);
//push a new line
if(curStr[0] == '\n')
{
textRows.back() += curWord;
curWord = "";
curLetterWidth = 0;
curWordWidth = 0;
curLineWidth = 0;
textRows.push_back("");
continue;
}
//ensure word is not longer than the width
if(curWordWidth + curLetterWidth >= AdjWidthMinusVoffset &&
curWord.length() >= 1)
{
textRows.back() += curWord;
textRows.push_back("");
curWord = "";
curWordWidth = 0;
curLineWidth = 0;
}
//add letter to word
curWord += curStr;
curWordWidth += curLetterWidth;
//if we need a Vscroll bar start over
if(!isVscroll && isVScrollNeeded())
{
isVscroll = true;
voffset = pChildVScroll->getWidth();
AdjWidthMinusVoffset = getAdjustedWidth() - voffset;
i = -1;
curWord = "";
curStr = "";
textRows.clear();
textRows.push_back("");
ind = 0;
curWordWidth = 0;
curLetterWidth = 0;
curLineWidth = 0;
bytesSkipped = 0;
continue;
}
if(curLineWidth + curWordWidth >=
AdjWidthMinusVoffset && textRows.back().length() >= 1)
{
textRows.push_back("");
curLineWidth = 0;
}
if(curStr[0] == ' ' || curStr[0] == '-')
{
textRows.back() += curWord;
curLineWidth += curWordWidth;
curWord = "";
curWordWidth = 0;
}
}
if(curWord != "")
{
textRows.back() += curWord;
}
updateWidestLine();
}
There are two main things making this slower than it could be, I think.
The first, and probably less important: as you build up each line, you're appending words to the line. Each such operation may require the line to be reallocated and its old contents copied. For long lines, this is inefficient. However, I'm guessing that in actual use your lines are quite short (say 60-100 characters), in which case the cost is unlikely to be huge. Still, there's probably some efficiency to be won there.
The second, and probably much more important: you're apparently using this for a text-area in some sort of GUI, and I'm guessing that it's being typed into. If you're recomputing for every character typed, that's really going to hurt once the text gets long.
As long as the user is only adding characters at the end -- which is surely the most common case -- you can make effective use of the fact that with your "greedy" line-breaking algorithm changes never affect anything on earlier lines: so just recompute from the start of the last line.
If you want to make it fast even when the user is typing (or deleting or whatever) somewhere in the middle of the text, your code will need to do more work and store more information. For instance: whenever you build a line, remember "if you start a line with this word, it ends with that word and this is the whole resulting line". Invalidate this information when anything changes within that line. Now, after a little editing, most changes will not require very much recalculation. You should work out the details of this for yourself because (1) it's a good exercise and (2) I need to go to bed now.
(To save on memory, you might prefer not to store whole lines at all -- whether or not you implement the sort of trick I just described. Instead, just store here's-the-next-line-break information and build up lines as your UI needs to render them.)
It's probably more complication than you want to take on board right now, but you should also look up Donald Knuth's dynamic-programming-based line-breaking algorithm. It's substantially more complicated than yours but can still be made quite quick, and it produces distinctly better results. See, e.g., http://defoe.sourceforge.net/folio/knuth-plass.html.
Problems on algorithms often come with problem on data-structures.
Let's make a few observations, first:
paragraphs can be treated independently
editing at a given index only invalidates the current word and those that follow
it is unnecessary to copy the whole words when their index would suffice for retrieving them and only their length matter for the computation
Paragraph
I would begin by introducing the notion of paragraph, which are determined by user-introduced line-breaks. When an edition takes place, you need to locate which is the concerned paragraph, which requires a look-up structure.
The "ideal" structure here would be a Fenwick Tree, for a small text box however this seems overkill. We'll just have each paragraph store the number of displayed lines that make up its representation and you'll count from the beginning. Note that an access to the last displayed line is an access to the last paragraph.
The paragraphs are thus stored as a contiguous sequence, in C++ terms, well probably take the hit of an indirection (ie storing pointers) to save moving them around when a paragraph in the middle is removed.
Each paragraph will store:
its content, the simplest being a single std::string to represent it.
its display, in editable form (which we need to determine still)
Each paragraph will cache its display, this paragraph cache will be invalidated whenever an edit is made.
The actual rendering will be made for only a couple of paragraphs at a time (and better, a couple of displayed lines): those which are visible.
Displayed Line
A paragraph may be to displayed with at least one line, but there is no maximum. We need to store the "display" in editable form, that is a form suitable for edition.
A single chunk of characters with \n thrown in is not suitable. Changes imply moving lots of characters around, and users are supposed to be changing the text, so we need better.
Using lengths, instead of characters, we may actually only store a mere 4 bytes (if the string takes more than 3GB... I don't guarantee much about this algorithm).
My first idea was to use the character index, however in case of edition all subsequent indexes are changed, and the propagation is error prone. Lengths are offsets, so we have an index relative to the position of the previous word. It does pose the issue of what a word (or token) is. Notably, do you collapse multiple spaces ? How do you handle them ? Here I'll assume that words are separated from one another by a single whitespace.
For "fast" retrieval, I'll store the length of the whole displayed line as well. This allows quickly skipping the first displayed lines when an edit is made at character 503 of the paragraph.
A displayed line will thus be composed of:
a total length (inferior to the maximum displayed length of the box, once computation ended)
a sequence of words (tokens) length
This sequence should be editable efficiently at both ends (since for wrapping we'll push/pop words at both ends depending on whether an edit added or removed words). It's not so important if in the middle we're not that efficient, because only one line at a time is edited in the middle.
In C++, either a vector or deque should be fine. While in theory a list would be "perfect", in practice its poor memory locality and high memory overhead will offset its asymptotic guarantees. A line is composed of few words, so the asymptotic behavior does not matter and high constants do.
Rendering
For the rendering, pick up a buffer of already sufficient length (a std::string with a call to reserve will do). Normally, you'd clear and rewrite the buffer each time, so no memory allocation occurs.
You need not display what cannot be seen, but do need to know how many lines there are, to pick up the correct paragraph.
Once you get the paragraph:
set offset to 0
for each line hidden, increment offset by its length (+ 1 for the space after it)
a word is accessed as a substring of _content, you can use the insert method on buffer: buffer.insert(buffer.end(), _content[offset], _content[offset+length])
The difficulty is in maintaining offset, but that's what makes the algorithm efficient.
Structures
struct LineDisplay: private boost::noncopyable
{
Paragraph& _paragraph;
uint32_t _length;
std::vector<uint16_t> _words; // copying around can be done with memmove
};
struct Paragraph:
{
std::string _content;
boost::ptr_vector<LineDisplay> _lines;
};
With this structure, implementation should be straightforward, and should not slow down as much when the content grows.
General change to the algorithm -
work out if you need the scroll bar as cheap as you can, ie. count the number of \n in the text and if it's greater then the vheight turn on the scroll, check lengths so on.
prepare the text into appropriate lines for the control now that you know you need a scroll bar or not.
This allows you to remove/reduce the test if(!isVscroll && isVScrollNeeded()) as is run on almost every character - isVScroll is probably not cheep, the example code doesn't seem to pass knowledge of lines to the function so can't see how it tells if it is needed.
Assuming textRows is a vector<string> - textrows.back() += is kind of expensive, looking up the back not so much as += on string not being efficient for strings. I'd change to using a ostrstream for gathering the row and push it in when it is done.
getFont().getWidth() are likely to be expensive - is the font changing? how greatly does the width differ between smallest and largest, shortcuts for fixed width fonts.
Use native methods where possible to get the size of a word since you don't want to break them - GetTextExtentPoint32
Often the will be sufficient space to allow for the VScroll when you change between. Restarting from the beginning with measuring could cost you up to twice the time. Store the width of the line with each line so you can skip over the ones that still fit.
Or don't build the line strings directly, keep the words seperate with the size.
How accurate does it realy need to be? Apply some pragmatism...
Just assume VScroll will be needed, mostly wrapping won't change much even if it isn't (1 letter words at the end/start of a line)
try and work more with words than with letters - checking remaining space for each letter can waste time. assume each letter in the string is the longest letter, letters x longest < space then put it in.