void CGUITextLayout::LineBreakText(const vecText &text, vector<CGUIString> &lines) { int nMaxLines = (m_maxHeight > 0 && m_font && m_font->GetLineHeight() > 0)?(int)ceilf(m_maxHeight / m_font->GetLineHeight()):-1; vecText::const_iterator lineStart = text.begin(); vecText::const_iterator pos = text.begin(); while (pos != text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines)) { // Get the current letter in the string character_t letter = *pos; // Handle the newline character if ((letter & 0xffff) == L'\n' ) { // push back everything up till now CGUIString string(lineStart, pos, true); lines.push_back(string); lineStart = pos + 1; } pos++; } // handle the last line if non-empty if (lineStart < text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines)) { CGUIString string(lineStart, text.end(), true); lines.push_back(string); } }
void CGUITextLayout::AppendToUTF32(const CStdStringW &utf16, character_t colStyle, vecText &utf32) { // NOTE: Assumes a single line of text utf32.reserve(utf32.size() + utf16.size()); for (unsigned int i = 0; i < utf16.size(); i++) utf32.push_back(utf16[i] | colStyle); }
void CRssReader::getFeed(vecText &text) { text.clear(); // double the spaces at the start of the set for (int j = 0; j < m_spacesBetweenFeeds; j++) text.push_back(L' '); for (unsigned int i = 0; i < m_strFeed.size(); i++) { for (int j = 0; j < m_spacesBetweenFeeds; j++) text.push_back(L' '); for (unsigned int j = 0; j < m_strFeed[i].size(); j++) { character_t letter = m_strFeed[i][j] | ((m_strColors[i][j] - 48) << 16); text.push_back(letter); } } }
void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, color_t shadowColor, const vecText &text, uint32_t alignment, float maxWidth, const CScrollInfo &scrollInfo) { if (!m_font) return; if (!shadowColor) shadowColor = m_shadowColor; if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment)) return; // nothing to render if (!scrollInfo.m_widthValid) { /* Calculate the pixel width of the complete string */ scrollInfo.m_textWidth = GetTextWidth(text); scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); scrollInfo.m_widthValid = true; } assert(scrollInfo.m_totalWidth != 0); float textPixelWidth = ROUND(scrollInfo.m_textWidth / g_graphicsContext.GetGUIScaleX()); float suffixPixelWidth = ROUND((scrollInfo.m_totalWidth - scrollInfo.m_textWidth) / g_graphicsContext.GetGUIScaleX()); float offset; if(scrollInfo.pixelSpeed >= 0) offset = scrollInfo.pixelPos; else offset = scrollInfo.m_totalWidth - scrollInfo.pixelPos; vecColors renderColors; for (unsigned int i = 0; i < colors.size(); i++) renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor)); bool scroll = !scrollInfo.waitTime && scrollInfo.pixelSpeed; if (shadowColor) { shadowColor = g_graphicsContext.MergeAlpha(shadowColor); vecColors shadowColors; for (unsigned int i = 0; i < renderColors.size(); i++) shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0); for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) { m_font->DrawTextInternal(x + dx + 1, y + 1, shadowColors, text, alignment, textPixelWidth, scroll); m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth + 1, y + 1, shadowColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); } } for (float dx = -offset; dx < maxWidth; dx += scrollInfo.m_totalWidth) { m_font->DrawTextInternal(x + dx, y, renderColors, text, alignment, textPixelWidth, scroll); m_font->DrawTextInternal(x + dx + scrollInfo.m_textWidth, y, renderColors, scrollInfo.suffix, alignment, suffixPixelWidth, scroll); } g_graphicsContext.RestoreClipRegion(); }
void CGUITextLayout::LineBreakText(const vecText &text, vector<CGUIString> &lines) { bool bUseHeight = (m_maxHeight > 0 && m_font && m_font->GetLineHeight() > 0); int nMaxLines = bUseHeight?(int)(m_maxHeight / m_font->GetLineHeight()):-1; if (bUseHeight && nMaxLines == 0) // hack. if height is not enough - force 1 line of text nMaxLines = 1; vecText::const_iterator lineStart = text.begin(); vecText::const_iterator pos = text.begin(); while (pos != text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines)) { // Get the current letter in the string character_t letter = *pos; // Handle the newline character if ((letter & 0xffff) == L'\n' ) { // push back everything up till now CGUIString string(lineStart, pos, true); lines.push_back(string); lineStart = pos + 1; } pos++; } }
bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo) { // draw at our scroll position // we handle the scrolling as follows: // We scroll on a per-pixel basis (eschewing the use of character indices // which were also in use previously). The complete string, including suffix, // is plotted to achieve the desired effect - normally just the one time, but // if there is a wrap point within the viewport then it will be plotted twice. // If the string is smaller than the viewport, then it may be plotted even // more times than that. // if (g_application.ScreenSaverDisablesAutoScrolling()) return false; if (scrollInfo.waitTime) { scrollInfo.waitTime--; return false; } if (text.empty()) return false; CScrollInfo old(scrollInfo); // move along by the appropriate scroll amount float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX()); if (!scrollInfo.m_widthValid) { /* Calculate the pixel width of the complete string */ scrollInfo.m_textWidth = GetTextWidth(text); scrollInfo.m_totalWidth = scrollInfo.m_textWidth + GetTextWidth(scrollInfo.suffix); scrollInfo.m_widthValid = true; } scrollInfo.pixelPos += scrollAmount; assert(scrollInfo.m_totalWidth != 0); while (scrollInfo.pixelPos >= scrollInfo.m_totalWidth) scrollInfo.pixelPos -= scrollInfo.m_totalWidth; if (scrollInfo.pixelPos != old.pixelPos) return true; else return false; }
void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling) { Begin(); // save the origin, which is scaled separately m_originX = x; m_originY = y; // Check if we will really need to truncate or justify the text if ( alignment & XBFONT_TRUNCATED ) { if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) alignment &= ~XBFONT_TRUNCATED; } else if ( alignment & XBFONT_JUSTIFIED ) { if ( maxPixelWidth <= 0.0f ) alignment &= ~XBFONT_JUSTIFIED; } // calculate sizing information float startX = 0; float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) { // Get the extent of this line float w = GetTextWidthInternal( text.begin(), text.end() ); if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues w = maxPixelWidth; if ( alignment & XBFONT_CENTER_X) w *= 0.5f; // Offset this line's starting position startX -= w; } float spacePerLetter = 0; // for justification effects if ( alignment & XBFONT_JUSTIFIED ) { // first compute the size of the text to render in both characters and pixels unsigned int lineChars = 0; float linePixels = 0; for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) { Character *ch = GetCharacter(*pos); if (ch) { // spaces have multiple times the justification spacing of normal letters lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; linePixels += ch->advance; } } if (lineChars > 1) spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); } float cursorX = 0; // current position along the line for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) { // If starting text on a new line, determine justification effects // Get the current letter in the CStdString color_t color = (*pos & 0xff0000) >> 16; if (color >= colors.size()) color = 0; color = colors[color]; // grab the next character Character *ch = GetCharacter(*pos); if (!ch) continue; if ( alignment & XBFONT_TRUNCATED ) { // Check if we will be exceeded the max allowed width if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) { // Yup. Let's draw the ellipses, then bail // Perhaps we should really bail to the next line in this case?? Character *period = GetCharacter(L'.'); if (!period) break; for (int i = 0; i < 3; i++) { RenderCharacter(startX + cursorX, startY, period, color, !scrolling); cursorX += period->advance; } break; } } else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) break; // exceeded max allowed width - stop rendering RenderCharacter(startX + cursorX, startY, ch, color, !scrolling); if ( alignment & XBFONT_JUSTIFIED ) { if ((*pos & 0xffff) == L' ') cursorX += ch->advance + spacePerLetter * justification_word_weight; else cursorX += ch->advance + spacePerLetter; } else cursorX += ch->advance; } End(); }
void CGUIFontTTFBase::DrawTextInternal(float x, float y, const vecColors &colors, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling) { Begin(); uint32_t rawAlignment = alignment; bool dirtyCache; bool hardwareClipping = g_Windowing.ScissorsCanEffectClipping(); CGUIFontCacheStaticPosition staticPos(x, y); CGUIFontCacheDynamicPosition dynamicPos; if (hardwareClipping) { dynamicPos = CGUIFontCacheDynamicPosition(g_graphicsContext.ScaleFinalXCoord(x, y), g_graphicsContext.ScaleFinalYCoord(x, y), g_graphicsContext.ScaleFinalZCoord(x, y)); } CVertexBuffer unusedVertexBuffer; CVertexBuffer &vertexBuffer = hardwareClipping ? m_dynamicCache.Lookup(dynamicPos, colors, text, alignment, maxPixelWidth, scrolling, XbmcThreads::SystemClockMillis(), dirtyCache) : unusedVertexBuffer; std::shared_ptr<std::vector<SVertex> > tempVertices = std::make_shared<std::vector<SVertex> >(); std::shared_ptr<std::vector<SVertex> > &vertices = hardwareClipping ? tempVertices : static_cast<std::shared_ptr<std::vector<SVertex> >&>(m_staticCache.Lookup(staticPos, colors, text, alignment, maxPixelWidth, scrolling, XbmcThreads::SystemClockMillis(), dirtyCache)); if (dirtyCache) { // save the origin, which is scaled separately m_originX = x; m_originY = y; // Check if we will really need to truncate or justify the text if ( alignment & XBFONT_TRUNCATED ) { if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) alignment &= ~XBFONT_TRUNCATED; } else if ( alignment & XBFONT_JUSTIFIED ) { if ( maxPixelWidth <= 0.0f ) alignment &= ~XBFONT_JUSTIFIED; } // calculate sizing information float startX = 0; float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*m_cellHeight : 0; // vertical centering if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) { // Get the extent of this line float w = GetTextWidthInternal( text.begin(), text.end() ); if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth + 0.5f ) // + 0.5f due to rounding issues w = maxPixelWidth; if ( alignment & XBFONT_CENTER_X) w *= 0.5f; // Offset this line's starting position startX -= w; } float spacePerSpaceCharacter = 0; // for justification effects if ( alignment & XBFONT_JUSTIFIED ) { // first compute the size of the text to render in both characters and pixels unsigned int numSpaces = 0; float linePixels = 0; for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) { Character *ch = GetCharacter(*pos); if (ch) { if ((*pos & 0xffff) == L' ') numSpaces += 1; linePixels += ch->advance; } } if (numSpaces > 0) spacePerSpaceCharacter = (maxPixelWidth - linePixels) / numSpaces; } float cursorX = 0; // current position along the line for (vecText::const_iterator pos = text.begin(); pos != text.end(); ++pos) { // If starting text on a new line, determine justification effects // Get the current letter in the CStdString color_t color = (*pos & 0xff0000) >> 16; if (color >= colors.size()) color = 0; color = colors[color]; // grab the next character Character *ch = GetCharacter(*pos); if (!ch) continue; if ( alignment & XBFONT_TRUNCATED ) { // Check if we will be exceeded the max allowed width if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) { // Yup. Let's draw the ellipses, then bail // Perhaps we should really bail to the next line in this case?? Character *period = GetCharacter(L'.'); if (!period) break; for (int i = 0; i < 3; i++) { RenderCharacter(startX + cursorX, startY, period, color, !scrolling, *tempVertices); cursorX += period->advance; } break; } } else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) break; // exceeded max allowed width - stop rendering RenderCharacter(startX + cursorX, startY, ch, color, !scrolling, *tempVertices); if ( alignment & XBFONT_JUSTIFIED ) { if ((*pos & 0xffff) == L' ') cursorX += ch->advance + spacePerSpaceCharacter; else cursorX += ch->advance; } else cursorX += ch->advance; } if (hardwareClipping) { CVertexBuffer &vertexBuffer = m_dynamicCache.Lookup(dynamicPos, colors, text, rawAlignment, maxPixelWidth, scrolling, XbmcThreads::SystemClockMillis(), dirtyCache); CVertexBuffer newVertexBuffer = CreateVertexBuffer(*tempVertices); vertexBuffer = newVertexBuffer; m_vertexTrans.push_back(CTranslatedVertices(0, 0, 0, &vertexBuffer, g_graphicsContext.GetClipRegion())); } else { m_staticCache.Lookup(staticPos, colors, text, rawAlignment, maxPixelWidth, scrolling, XbmcThreads::SystemClockMillis(), dirtyCache) = *static_cast<CGUIFontCacheStaticValue *>(&tempVertices); /* Append the new vertices to the set collected since the first Begin() call */ m_vertex.insert(m_vertex.end(), tempVertices->begin(), tempVertices->end()); } } else { if (hardwareClipping)
void CGUITextLayout::GetFirstText(vecText &text) const { text.clear(); if (m_lines.size()) text = m_lines[0].m_text; }
void CGUITextLayout::ParseText(const CStdStringW &text, uint32_t defaultStyle, color_t defaultColor, vecColors &colors, vecText &parsedText) { // run through the string, searching for: // [B] or [/B] -> toggle bold on and off // [I] or [/I] -> toggle italics on and off // [COLOR ffab007f] or [/COLOR] -> toggle color on and off // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off uint32_t currentStyle = defaultStyle; // start with the default font's style color_t currentColor = 0; colors.push_back(defaultColor); stack<color_t> colorStack; colorStack.push(0); // these aren't independent, but that's probably not too much of an issue // eg [UPPERCASE]Glah[LOWERCASE]FReD[/LOWERCASE]Georeg[/UPPERCASE] will work (lower case >> upper case) // but [LOWERCASE]Glah[UPPERCASE]FReD[/UPPERCASE]Georeg[/LOWERCASE] won't int startPos = 0; size_t pos = text.Find(L'['); while (pos != CStdString::npos && pos + 1 < text.size()) { uint32_t newStyle = 0; color_t newColor = currentColor; bool colorTagChange = false; bool newLine = false; // have a [ - check if it's an ON or OFF switch bool on(true); int endPos = pos++; // finish of string if (text[pos] == L'/') { on = false; pos++; } // check for each type if (text.Mid(pos,2) == L"B]") { // bold - finish the current text block and assign the bold state pos += 2; if ((on && text.Find(L"[/B]",pos) >= 0) || // check for a matching end point (!on && (currentStyle & FONT_STYLE_BOLD))) // or matching start point newStyle = FONT_STYLE_BOLD; } else if (text.Mid(pos,2) == L"I]") { // italics pos += 2; if ((on && text.Find(L"[/I]",pos) >= 0) || // check for a matching end point (!on && (currentStyle & FONT_STYLE_ITALICS))) // or matching start point newStyle = FONT_STYLE_ITALICS; } else if (text.Mid(pos,10) == L"UPPERCASE]") { pos += 10; if ((on && text.Find(L"[/UPPERCASE]",pos) >= 0) || // check for a matching end point (!on && (currentStyle & FONT_STYLE_UPPERCASE))) // or matching start point newStyle = FONT_STYLE_UPPERCASE; } else if (text.Mid(pos,10) == L"LOWERCASE]") { pos += 10; if ((on && text.Find(L"[/LOWERCASE]",pos) >= 0) || // check for a matching end point (!on && (currentStyle & FONT_STYLE_LOWERCASE))) // or matching start point newStyle = FONT_STYLE_LOWERCASE; } else if (text.Mid(pos,3) == L"CR]" && on) { newLine = true; pos += 3; } else if (text.Mid(pos,5) == L"COLOR") { // color size_t finish = text.Find(L']', pos + 5); if (on && finish != CStdString::npos && (size_t)text.Find(L"[/COLOR]",finish) != CStdString::npos) { color_t color = g_colorManager.GetColor(text.Mid(pos + 5, finish - pos - 5)); vecColors::const_iterator it = std::find(colors.begin(), colors.end(), color); if (it == colors.end()) { // create new color if (colors.size() <= 0xFF) { newColor = colors.size(); colors.push_back(color); } else // we have only 8 bits for color index, fallback to first color if reach max. newColor = 0; } else // reuse existing color newColor = it - colors.begin(); colorStack.push(newColor); colorTagChange = true; } else if (!on && finish == pos + 5 && colorStack.size() > 1) { // revert to previous color colorStack.pop(); newColor = colorStack.top(); colorTagChange = true; } if (finish != CStdString::npos) pos = finish + 1; } if (newStyle || colorTagChange || newLine) { // we have a new style or a new color, so format up the previous segment CStdStringW subText = text.Mid(startPos, endPos - startPos); if (currentStyle & FONT_STYLE_UPPERCASE) subText.ToUpper(); if (currentStyle & FONT_STYLE_LOWERCASE) subText.ToLower(); AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText); if (newLine) parsedText.push_back(L'\n'); // and switch to the new style startPos = pos; currentColor = newColor; if (on) currentStyle |= newStyle; else currentStyle &= ~newStyle; } pos = text.Find(L'[',pos); } // now grab the remainder of the string CStdStringW subText = text.Mid(startPos, text.GetLength() - startPos); if (currentStyle & FONT_STYLE_UPPERCASE) subText.ToUpper(); if (currentStyle & FONT_STYLE_LOWERCASE) subText.ToLower(); AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText); }
float CGUIFont::GetTextWidth( const vecText &text ) { if (!m_font) return 0; CSingleLock lock(g_graphicsContext); return m_font->GetTextWidthInternal(text.begin(), text.end()) * g_graphicsContext.GetGUIScaleX(); }
void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, color_t shadowColor, const vecText &text, uint32_t alignment, float maxWidth, CScrollInfo &scrollInfo) { if (!m_font) return; if (!shadowColor) shadowColor = m_shadowColor; float spaceWidth = GetCharWidth(L' '); // max chars on screen + extra margin chars vecText::size_type maxChars = std::min<vecText::size_type>( (text.size() + (vecText::size_type)scrollInfo.suffix.size()), (vecText::size_type)((maxWidth * 1.05f) / spaceWidth)); if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment)) return; // nothing to render maxWidth = ROUND(maxWidth / g_graphicsContext.GetGUIScaleX()); // draw at our scroll position // we handle the scrolling as follows: // We scroll on a per-pixel basis up until we have scrolled the first character outside // of our viewport, whereby we cycle the string around, and reset the scroll position. // // pixelPos is the amount in pixels to move the string by. // characterPos is the amount in characters to rotate the string by. // float offset = scrollInfo.pixelPos; if (!scrollInfo.waitTime) { // move along by the appropriate scroll amount float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX()); if (scrollInfo.pixelSpeed > 0) { // we want to move scrollAmount, grab the next character float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); if (scrollInfo.pixelPos + scrollAmount < charWidth) scrollInfo.pixelPos += scrollAmount; // within the current character else { // past the current character, decrement scrollAmount by the charWidth and move to the next character while (scrollInfo.pixelPos + scrollAmount >= charWidth) { scrollAmount -= (charWidth - scrollInfo.pixelPos); scrollInfo.pixelPos = 0; scrollInfo.characterPos++; if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size()) { scrollInfo.Reset(); break; } charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); } } offset = scrollInfo.pixelPos; } else if (scrollInfo.pixelSpeed < 0) { // scrolling backwards // we want to move scrollAmount, grab the next character float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); if (scrollInfo.pixelPos + scrollAmount < charWidth) scrollInfo.pixelPos += scrollAmount; // within the current character else { // past the current character, decrement scrollAmount by the charWidth and move to the next character while (scrollInfo.pixelPos + scrollAmount >= charWidth) { scrollAmount -= (charWidth - scrollInfo.pixelPos); scrollInfo.pixelPos = 0; if (scrollInfo.characterPos == 0) { scrollInfo.Reset(); scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1; break; } scrollInfo.characterPos--; charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text)); } } offset = charWidth - scrollInfo.pixelPos; } } else scrollInfo.waitTime--; // Now rotate our string as needed, only take a slightly larger then visible part of the text. unsigned int pos = scrollInfo.characterPos; vecText renderText; renderText.reserve(maxChars); for (vecText::size_type i = 0; i < maxChars; i++) { if (pos >= text.size() + scrollInfo.suffix.size()) pos = 0; if (pos < text.size()) renderText.push_back(text[pos]); else renderText.push_back(scrollInfo.suffix[pos - text.size()]); pos++; } vecColors renderColors; for (unsigned int i = 0; i < colors.size(); i++) renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor)); bool scroll = !scrollInfo.waitTime && scrollInfo.pixelSpeed; if (shadowColor) { shadowColor = g_graphicsContext.MergeAlpha(shadowColor); vecColors shadowColors; for (unsigned int i = 0; i < renderColors.size(); i++) shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0); m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); } m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll); g_graphicsContext.RestoreClipRegion(); }
void CGUITextLayout::ParseText(const CStdStringW &text, uint32_t defaultStyle, vecColors &colors, vecText &parsedText) { // run through the string, searching for: // [B] or [/B] -> toggle bold on and off // [I] or [/I] -> toggle italics on and off // [COLOR ffab007f] or [/COLOR] -> toggle color on and off // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off // uint32_t currentStyle = defaultStyle; // start with the default font's style // color_t currentColor = 0; stack<color_t> colorStack; colorStack.push(0); // these aren't independent, but that's probably not too much of an issue // eg [UPPERCASE]Glah[LOWERCASE]FReD[/LOWERCASE]Georeg[/UPPERCASE] will work (lower case >> upper case) // but [LOWERCASE]Glah[UPPERCASE]FReD[/UPPERCASE]Georeg[/LOWERCASE] won't #define FONT_STYLE_UPPERCASE 4 #define FONT_STYLE_LOWERCASE 8 int boldCounter = 0; int italicsCoutner = 0; int upperCounter = 0; int lowerCounter = 0; color_t color = 0; int startPos = 0; size_t pos = text.Find(L'['); while (pos != CStdString::npos && pos + 1 < text.size()) { int style = 0; if (pos - startPos > 0) { if (boldCounter) style |= FONT_STYLE_BOLD; if (italicsCoutner) style |= FONT_STYLE_ITALICS; CStdStringW subText = text.Mid(startPos, pos - startPos); if (upperCounter) { #if defined(_LINUX) && !defined(__APPLE__) std::transform(subText.begin(), subText.end(), subText.begin(), (gunichar(*)(gunichar)) g_unichar_toupper); #else subText.ToUpper(); #endif } if (lowerCounter) { #if defined(_LINUX) && !defined(__APPLE__) std::transform(subText.begin(), subText.end(), subText.begin(), (gunichar(*)(gunichar)) g_unichar_tolower); #else subText.ToLower(); #endif } AppendToUTF32(subText, ((style & 3) << 24) | (color << 16), parsedText); startPos = pos; } // have a [ - check if it's an ON or OFF switch bool ignoreTag = false; ++pos; bool on = true; if (text[pos] == L'/') { on = false; pos++; } // check for each type if (text.Mid(pos,2) == L"B]") { // bold - finish the current text block and assign the bold state pos += 2; on ? ++boldCounter : --boldCounter; } else if (text.Mid(pos,2) == L"I]") { // italics pos += 2; on ? ++italicsCoutner : --italicsCoutner; } else if (text.Mid(pos,10) == L"UPPERCASE]") { pos += 10; on ? ++upperCounter : --upperCounter; } else if (text.Mid(pos,10) == L"LOWERCASE]") { pos += 10; on ? ++lowerCounter : --lowerCounter; } else if (text.Mid(pos,3) == L"CR]" && on) { pos += 3; parsedText.push_back(L'\n'); } else if (text.Mid(pos,5) == L"COLOR") { // color size_t finish = text.Find(L']', pos + 5); if (on && finish != CStdString::npos && (size_t) text.Find(L"[/COLOR]",finish) != CStdString::npos) { // create new color color = colors.size(); colors.push_back(g_colorManager.GetColor(text.Mid(pos + 5, finish - pos - 5))); colorStack.push(color); } else if (!on && finish == pos + 5 && colorStack.size() > 1) { // revert to previous color colorStack.pop(); color = colorStack.top(); } pos = finish + 1; } else { ignoreTag = true; } if (!ignoreTag) { startPos = pos; } pos = text.Find(L'[',pos); } // now grab the remainder of the string CStdStringW subText = text.Mid(startPos, text.GetLength() - startPos); int style = 0; if (upperCounter) { #if defined(_LINUX) && !defined(__APPLE__) std::transform(subText.begin(), subText.end(), subText.begin(), (gunichar(*)(gunichar)) g_unichar_toupper); #else subText.ToUpper(); #endif } if (lowerCounter) { #if defined(_LINUX) && !defined(__APPLE__) std::transform(subText.begin(), subText.end(), subText.begin(), (gunichar(*)(gunichar)) g_unichar_tolower); #else subText.ToLower(); #endif } if (boldCounter) style |= FONT_STYLE_BOLD; if (italicsCoutner) style |= FONT_STYLE_ITALICS; AppendToUTF32(subText, ((style & 3) << 24) | (color << 16), parsedText); }
void CGUIFontTTFBase::BuildTextCoordinates(float x, float y, const vecColors &colors, color_t shadowColor, const vecText &text, uint32_t alignment, float maxPixelWidth, bool scrolling, FontCoordsIndiced& pData) { // Check if we will really need to truncate or justify the text m_originX = x; m_originY = y; if ( alignment & XBFONT_TRUNCATED ) { if ( maxPixelWidth <= 0.0f || GetTextWidthInternal(text.begin(), text.end()) <= maxPixelWidth) alignment &= ~XBFONT_TRUNCATED; } else if ( alignment & XBFONT_JUSTIFIED ) { if ( maxPixelWidth <= 0.0f ) alignment &= ~XBFONT_JUSTIFIED; } // calculate sizing information float startX = 0; float startY = (alignment & XBFONT_CENTER_Y) ? -0.5f*(m_cellHeight-2) : 0; // vertical centering if ( alignment & (XBFONT_RIGHT | XBFONT_CENTER_X) ) { // Get the extent of this line float w = GetTextWidthInternal( text.begin(), text.end() ); if ( alignment & XBFONT_TRUNCATED && w > maxPixelWidth ) w = maxPixelWidth; if ( alignment & XBFONT_CENTER_X) w *= 0.5f; // Offset this line's starting position startX -= w; } float spacePerLetter = 0; // for justification effects #if 0 if ( alignment & XBFONT_JUSTIFIED ) { // first compute the size of the text to render in both characters and pixels unsigned int lineChars = 0; float linePixels = 0; for (vecText::const_iterator pos = text.begin(); pos != text.end(); pos++) { Character *ch = GetCharacter(*pos); if (ch) { // spaces have multiple times the justification spacing of normal letters lineChars += ((*pos & 0xffff) == L' ') ? justification_word_weight : 1; linePixels += ch->advance; } } if (lineChars > 1) spacePerLetter = (maxPixelWidth - linePixels) / (lineChars - 1); } #endif ReloadFace(); #ifdef HAS_HARFBUZZ_NG int i = 0; wchar_t strW[text.size()]; for (vecText::const_iterator pos = text.begin(); pos != text.end(); pos++) { wchar_t letter = (wchar_t)((*pos) & 0xffff); strW[i] = letter; i++; } hb_buffer_t *hb_buffer = hb_buffer_create(text.size()); hb_buffer_set_unicode_funcs(hb_buffer, hb_glib_get_unicode_funcs()); hb_buffer_add_utf32(hb_buffer, (const uint32_t*) strW, text.size(), 0, text.size()); hb_buffer_set_direction(hb_buffer, HB_DIRECTION_LTR); hb_shape (hb_font, hb_buffer, NULL, 0); unsigned int glyph_info_len; hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, &glyph_info_len); hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (hb_buffer, &glyph_info_len); #else /* HAS_HARFBUZZ_NG */ FT_Vector delta; Character* previousCh = NULL; #endif float cursorX = 0; // current position along the line for (vecText::const_iterator pos = text.begin(); pos != text.end(); pos++) { // If starting text on a new line, determine justification effects // Get the current letter in the CStdString color_t color = (*pos & 0xff0000) >> 16; if (color >= colors.size()) color = 0; color = colors[color]; // grab the next character Character *ch = GetCharacter(*pos); if (!ch) continue; if ( alignment & XBFONT_TRUNCATED ) { // Check if we will be exceeded the max allowed width if ( cursorX + ch->advance + 3 * m_ellipsesWidth > maxPixelWidth ) { // Yup. Let's draw the ellipses, then bail // Perhaps we should really bail to the next line in this case?? Character *period = GetCharacter(L'.'); if (!period) break; for (int i = 0; i < 3; i++) { BuildCharacterCoordinates(startX + cursorX, startY, period, color, shadowColor, !scrolling, pData); cursorX += period->advance; } break; } } else if (maxPixelWidth > 0 && cursorX > maxPixelWidth) break; // exceeded max allowed width - stop rendering #ifdef HAS_HARFBUZZ_NG BuildCharacterCoordinates(startX + cursorX + (hb_position->x_offset * (1./64)), startY - (hb_position->y_offset * (1./64)), ch, color, shadowColor, !scrolling, pData); if ( alignment & XBFONT_JUSTIFIED ) { if ((*pos & 0xffff) == L' ') cursorX += (hb_position->x_advance * (1./64)) + spacePerLetter * justification_word_weight; else cursorX += (hb_position->x_advance * (1./64)) + spacePerLetter; } else cursorX += hb_position->x_advance * (1./64); hb_glyph++; hb_position++; #else if (previousCh) { FT_Get_Kerning(m_face, previousCh->glyphIndex, ch->glyphIndex, FT_KERNING_DEFAULT, &delta); cursorX += (float) (delta.x / 64); } BuildCharacterCoordinates(startX + cursorX, startY, ch, color, shadowColor, !scrolling, pData); if ( alignment & XBFONT_JUSTIFIED ) { if ((*pos & 0xffff) == L' ') cursorX += ch->advance + spacePerLetter * justification_word_weight; else cursorX += ch->advance + spacePerLetter; } else cursorX += ch->advance; previousCh = ch; #endif } #ifdef HAS_HARFBUZZ_NG hb_buffer_destroy(hb_buffer); #endif }