Scan longer shaped glyphs, and try to split them into two strings if a space glyph is present. NOTE: Works only for LTR texts. Implementation should be mirrored for RTL.
| 136 | // Scan longer shaped glyphs, and try to split them into two strings if a space glyph is present. |
| 137 | // NOTE: Works only for LTR texts. Implementation should be mirrored for RTL. |
| 138 | buffer_vector<LineMetrics, 2> SplitText(bool forceNoWrap, float textScale, dp::GlyphFontAndId space, |
| 139 | dp::text::TextMetrics const & str) |
| 140 | { |
| 141 | // Add the whole line by default. |
| 142 | buffer_vector<LineMetrics, 2> lines{ |
| 143 | {str.m_glyphs.size(), textScale * str.m_lineWidthInPixels, textScale * str.m_maxLineHeightInPixels}}; |
| 144 | |
| 145 | size_t const count = str.m_glyphs.size(); |
| 146 | if (forceNoWrap || count <= 15) |
| 147 | return lines; |
| 148 | |
| 149 | auto const begin = str.m_glyphs.begin(); |
| 150 | auto const end = str.m_glyphs.end(); |
| 151 | |
| 152 | // Naive split on two parts using spaces as delimiters. |
| 153 | // Doesn't take into an account the width of glyphs/string. |
| 154 | auto const iMiddle = begin + count / 2; |
| 155 | |
| 156 | auto const isSpaceGlyph = [space](auto const & metrics) { return metrics.m_key == space; }; |
| 157 | // Find next delimiter after middle [m, e) |
| 158 | auto iNext = std::find_if(iMiddle, end, isSpaceGlyph); |
| 159 | |
| 160 | // Find last delimiter before middle [b, m) |
| 161 | auto iPrev = std::find_if(std::reverse_iterator(iMiddle), std::reverse_iterator(begin), isSpaceGlyph).base(); |
| 162 | // Don't split like this: |
| 163 | // xxxx |
| 164 | // xxxxxxxxxxxx |
| 165 | if (4 * (iPrev - begin) <= static_cast<long>(count)) |
| 166 | iPrev = end; |
| 167 | else |
| 168 | --iPrev; |
| 169 | |
| 170 | // Get the closest space to the middle. |
| 171 | if (iNext == end || (iPrev != end && iMiddle - iPrev < iNext - iMiddle)) |
| 172 | iNext = iPrev; |
| 173 | |
| 174 | if (iNext == end) |
| 175 | return lines; |
| 176 | |
| 177 | // Split string (actually, glyphs) into 2 parts. |
| 178 | ASSERT(iNext != begin, ()); |
| 179 | ASSERT(space == iNext->m_key, ()); |
| 180 | |
| 181 | auto const spaceIndex = iNext; |
| 182 | auto const afterSpace = iNext + 1; |
| 183 | ASSERT(afterSpace != end, ()); |
| 184 | |
| 185 | lines.push_back(LineMetrics{ |
| 186 | count, |
| 187 | textScale * std::accumulate(afterSpace, end, 0, [](auto acc, auto const & m) { return m.m_xAdvance + acc; }), |
| 188 | textScale * str.m_maxLineHeightInPixels}); |
| 189 | |
| 190 | // Update the first line too. |
| 191 | lines[0].m_nextLineStartIndex = afterSpace - begin; |
| 192 | auto const spaceWidth = textScale * spaceIndex->m_xAdvance; |
| 193 | lines[0].m_scaledLength -= lines[1].m_scaledLength + spaceWidth; |
| 194 | return lines; |
| 195 | } |