2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "GUIFontTTF.h"
23 #include "GraphicContext.h"
25 #include "threads/SingleLock.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/MathUtils.h"
29 #include "utils/CharsetConverter.h"
31 #define ROUND(x) (float)(MathUtils::round_int(x))
33 CScrollInfo::CScrollInfo(unsigned int wait /* = 50 */, float pos /* = 0 */,
34 int speed /* = defaultSpeed */, const CStdString &scrollSuffix /* = " | " */)
38 SetSpeed(speed ? speed : defaultSpeed);
39 g_charsetConverter.utf8ToW(scrollSuffix, suffix);
43 float CScrollInfo::GetPixelsPerFrame()
45 static const float alphaEMA = 0.05f;
48 return 0; // not scrolling
49 unsigned int currentTime = CTimeUtils::GetFrameTime();
50 float delta = m_lastFrameTime ? (float)(currentTime - m_lastFrameTime) : m_averageFrameTime;
52 delta = 100; // assume a minimum of 10 fps
53 m_lastFrameTime = currentTime;
54 // do an exponential moving average of the frame time
55 m_averageFrameTime = m_averageFrameTime + (delta - m_averageFrameTime) * alphaEMA;
56 // and multiply by pixel speed (per ms) to get number of pixels to move this frame
57 return pixelSpeed * m_averageFrameTime;
60 CGUIFont::CGUIFont(const CStdString& strFontName, uint32_t style, color_t textColor,
61 color_t shadowColor, float lineSpacing, float origHeight, CGUIFontTTFBase *font)
63 m_strFontName = strFontName;
64 m_style = style & FONT_STYLE_MASK;
65 m_textColor = textColor;
66 m_shadowColor = shadowColor;
67 m_lineSpacing = lineSpacing;
68 m_origHeight = origHeight;
72 m_font->AddReference();
78 m_font->RemoveReference();
81 CStdString& CGUIFont::GetFontName()
86 void CGUIFont::DrawText( float x, float y, const vecColors &colors, color_t shadowColor,
87 const vecText &text, uint32_t alignment, float maxPixelWidth)
91 bool clip = maxPixelWidth > 0;
92 if (clip && ClippedRegionIsEmpty(x, y, maxPixelWidth, alignment))
95 maxPixelWidth = ROUND(maxPixelWidth / g_graphicsContext.GetGUIScaleX());
96 vecColors renderColors;
97 for (unsigned int i = 0; i < colors.size(); i++)
98 renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor));
99 if (!shadowColor) shadowColor = m_shadowColor;
102 shadowColor = g_graphicsContext.MergeAlpha(shadowColor);
103 vecColors shadowColors;
104 for (unsigned int i = 0; i < renderColors.size(); i++)
105 shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0);
106 m_font->DrawTextInternal(x + 1, y + 1, shadowColors, text, alignment, maxPixelWidth, false);
108 m_font->DrawTextInternal( x, y, renderColors, text, alignment, maxPixelWidth, false);
111 g_graphicsContext.RestoreClipRegion();
114 bool CGUIFont::UpdateScrollInfo(const vecText &text, CScrollInfo &scrollInfo)
116 // draw at our scroll position
117 // we handle the scrolling as follows:
118 // We scroll on a per-pixel basis up until we have scrolled the first character outside
119 // of our viewport, whereby we cycle the string around, and reset the scroll position.
121 // pixelPos is the amount in pixels to move the string by.
122 // characterPos is the amount in characters to rotate the string by.
124 if (scrollInfo.waitTime)
126 scrollInfo.waitTime--;
133 CScrollInfo old(scrollInfo);
135 // move along by the appropriate scroll amount
136 float scrollAmount = fabs(scrollInfo.GetPixelsPerFrame() * g_graphicsContext.GetGUIScaleX());
138 if (scrollInfo.pixelSpeed > 0)
140 // we want to move scrollAmount, grab the next character
141 float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
142 if (scrollInfo.pixelPos + scrollAmount < charWidth)
143 scrollInfo.pixelPos += scrollAmount; // within the current character
145 { // past the current character, decrement scrollAmount by the charWidth and move to the next character
146 while (scrollInfo.pixelPos + scrollAmount >= charWidth)
148 scrollAmount -= (charWidth - scrollInfo.pixelPos);
149 scrollInfo.pixelPos = 0;
150 scrollInfo.characterPos++;
151 if (scrollInfo.characterPos >= text.size() + scrollInfo.suffix.size())
156 charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
160 else if (scrollInfo.pixelSpeed < 0)
161 { // scrolling backwards
162 // we want to move scrollAmount, grab the next character
163 float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
164 if (scrollInfo.pixelPos + scrollAmount < charWidth)
165 scrollInfo.pixelPos += scrollAmount; // within the current character
167 { // past the current character, decrement scrollAmount by the charWidth and move to the next character
168 while (scrollInfo.pixelPos + scrollAmount >= charWidth)
170 scrollAmount -= (charWidth - scrollInfo.pixelPos);
171 scrollInfo.pixelPos = 0;
172 if (scrollInfo.characterPos == 0)
175 scrollInfo.characterPos = text.size() + scrollInfo.suffix.size() - 1;
178 scrollInfo.characterPos--;
179 charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
184 if(scrollInfo.characterPos != old.characterPos
185 || scrollInfo.pixelPos != old.pixelPos)
191 void CGUIFont::DrawScrollingText(float x, float y, const vecColors &colors, color_t shadowColor,
192 const vecText &text, uint32_t alignment, float maxWidth, const CScrollInfo &scrollInfo)
195 if (!shadowColor) shadowColor = m_shadowColor;
197 float spaceWidth = GetCharWidth(L' ');
198 // max chars on screen + extra margin chars
199 vecText::size_type maxChars =
200 std::min<vecText::size_type>(
201 (text.size() + (vecText::size_type)scrollInfo.suffix.size()),
202 (vecText::size_type)((maxWidth * 1.05f) / spaceWidth));
204 if (!text.size() || ClippedRegionIsEmpty(x, y, maxWidth, alignment))
205 return; // nothing to render
207 maxWidth = ROUND(maxWidth / g_graphicsContext.GetGUIScaleX());
209 float charWidth = GetCharWidth(scrollInfo.GetCurrentChar(text));
211 if(scrollInfo.pixelSpeed >= 0)
212 offset = scrollInfo.pixelPos;
214 offset = charWidth - scrollInfo.pixelPos;
216 // Now rotate our string as needed, only take a slightly larger then visible part of the text.
217 unsigned int pos = scrollInfo.characterPos;
219 renderText.reserve(maxChars);
220 for (vecText::size_type i = 0; i < maxChars; i++)
222 if (pos >= text.size() + scrollInfo.suffix.size())
224 if (pos < text.size())
225 renderText.push_back(text[pos]);
227 renderText.push_back(scrollInfo.suffix[pos - text.size()]);
231 vecColors renderColors;
232 for (unsigned int i = 0; i < colors.size(); i++)
233 renderColors.push_back(g_graphicsContext.MergeAlpha(colors[i] ? colors[i] : m_textColor));
235 bool scroll = !scrollInfo.waitTime && scrollInfo.pixelSpeed;
238 shadowColor = g_graphicsContext.MergeAlpha(shadowColor);
239 vecColors shadowColors;
240 for (unsigned int i = 0; i < renderColors.size(); i++)
241 shadowColors.push_back((renderColors[i] & 0xff000000) != 0 ? shadowColor : 0);
242 m_font->DrawTextInternal(x - offset + 1, y + 1, shadowColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll);
244 m_font->DrawTextInternal(x - offset, y, renderColors, renderText, alignment, maxWidth + scrollInfo.pixelPos + m_font->GetLineHeight(2.0f), scroll);
246 g_graphicsContext.RestoreClipRegion();
249 // remaps unsupported font glpyhs to other suitable ones
250 wchar_t CGUIFont::RemapGlyph(wchar_t letter)
252 if (letter == 0x2019 || letter == 0x2018) return 0x0027; // single quotes
253 else if (letter == 0x201c || letter == 0x201d) return 0x0022;
254 return 0; // no decent character map
257 bool CGUIFont::ClippedRegionIsEmpty(float x, float y, float width, uint32_t alignment) const
259 if (alignment & XBFONT_CENTER_X)
261 else if (alignment & XBFONT_RIGHT)
263 if (alignment & XBFONT_CENTER_Y)
264 y -= m_font->GetLineHeight(m_lineSpacing);
266 return !g_graphicsContext.SetClipRegion(x, y, width, m_font->GetTextHeight(1, 2) * g_graphicsContext.GetGUIScaleY());
269 float CGUIFont::GetTextWidth( const vecText &text )
271 if (!m_font) return 0;
272 CSingleLock lock(g_graphicsContext);
273 return m_font->GetTextWidthInternal(text.begin(), text.end()) * g_graphicsContext.GetGUIScaleX();
276 float CGUIFont::GetCharWidth( character_t ch )
278 if (!m_font) return 0;
279 CSingleLock lock(g_graphicsContext);
280 return m_font->GetCharWidthInternal(ch) * g_graphicsContext.GetGUIScaleX();
283 float CGUIFont::GetTextHeight(int numLines) const
285 if (!m_font) return 0;
286 return m_font->GetTextHeight(m_lineSpacing, numLines) * g_graphicsContext.GetGUIScaleY();
289 float CGUIFont::GetTextBaseLine() const
291 if (!m_font) return 0;
292 return m_font->GetTextBaseLine() * g_graphicsContext.GetGUIScaleY();
295 float CGUIFont::GetLineHeight() const
297 if (!m_font) return 0;
298 return m_font->GetLineHeight(m_lineSpacing) * g_graphicsContext.GetGUIScaleY();
301 float CGUIFont::GetScaleFactor() const
303 if (!m_font) return 1.0f;
304 return m_font->GetFontHeight() / m_origHeight;
307 void CGUIFont::Begin()
319 void CGUIFont::SetFont(CGUIFontTTFBase *font)
322 return; // no need to update the font if we already have it
324 m_font->RemoveReference();
327 m_font->AddReference();