aboutsummaryrefslogtreecommitdiff
path: root/guilib/GUIFontTTF.cpp
diff options
context:
space:
mode:
authorAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
committerAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
commit45285e8a9300cd754a760560640b75b09f98035e (patch)
treead9f093885ad5c98e9dd4156674e7691c22ed0a2 /guilib/GUIFontTTF.cpp
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'guilib/GUIFontTTF.cpp')
-rw-r--r--guilib/GUIFontTTF.cpp772
1 files changed, 772 insertions, 0 deletions
diff --git a/guilib/GUIFontTTF.cpp b/guilib/GUIFontTTF.cpp
new file mode 100644
index 0000000000..91dd551bcf
--- /dev/null
+++ b/guilib/GUIFontTTF.cpp
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIFont.h"
+#include "GUIFontTTF.h"
+#include "GUIFontManager.h"
+#include "Texture.h"
+#include "GraphicContext.h"
+#include "FileSystem/SpecialProtocol.h"
+#include "MathUtils.h"
+#include "utils/log.h"
+
+#include <math.h>
+
+// stuff for freetype
+#ifndef _LINUX
+#include "ft2build.h"
+#else
+#include <ft2build.h>
+#endif
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+
+#define USE_RELEASE_LIBS
+
+using namespace std;
+
+
+
+// our free type library (debug)
+#if defined(_DEBUG) && !defined(USE_RELEASE_LIBS)
+ #pragma comment (lib,"../../guilib/freetype2/freetype239ST_D.lib")
+#elif !defined(__GNUC__)
+ #pragma comment (lib,"../../guilib/freetype2/freetype239ST.lib")
+#endif
+
+
+#define CHARS_PER_TEXTURE_LINE 20 // number of characters to cache per texture line
+#define CHAR_CHUNK 64 // 64 chars allocated at a time (1024 bytes)
+
+int CGUIFontTTFBase::justification_word_weight = 6; // weight of word spacing over letter spacing when justifying.
+ // A larger number means more of the "dead space" is placed between
+ // words rather than between letters.
+
+unsigned int CGUIFontTTFBase::max_texture_size = 2048; // max texture size - 2048 for GMA965
+
+class CFreeTypeLibrary
+{
+public:
+ CFreeTypeLibrary()
+ {
+ m_library = NULL;
+ }
+
+ virtual ~CFreeTypeLibrary()
+ {
+ if (m_library)
+ FT_Done_FreeType(m_library);
+ }
+
+ FT_Face GetFont(const CStdString &filename, float size, float aspect)
+ {
+ // don't have it yet - create it
+ if (!m_library)
+ FT_Init_FreeType(&m_library);
+ if (!m_library)
+ {
+ CLog::Log(LOGERROR, "Unable to initialize freetype library");
+ return NULL;
+ }
+
+ FT_Face face;
+
+ // ok, now load the font face
+ if (FT_New_Face( m_library, _P(filename).c_str(), 0, &face ))
+ return NULL;
+
+ unsigned int ydpi = GetDPI();
+ unsigned int xdpi = (unsigned int)MathUtils::round_int(ydpi * aspect);
+
+ // we set our screen res currently to 96dpi in both directions (windows default)
+ // we cache our characters (for rendering speed) so it's probably
+ // not a good idea to allow free scaling of fonts - rather, just
+ // scaling to pixel ratio on screen perhaps?
+ if (FT_Set_Char_Size( face, 0, (int)(size*64 + 0.5f), xdpi, ydpi ))
+ {
+ FT_Done_Face(face);
+ return NULL;
+ }
+
+ return face;
+ };
+
+ void ReleaseFont(FT_Face face)
+ {
+ assert(face);
+ FT_Done_Face(face);
+ };
+
+ unsigned int GetDPI() const
+ {
+ return 72; // default dpi, matches what XPR fonts used to use for sizing
+ };
+
+private:
+ FT_Library m_library;
+};
+
+CFreeTypeLibrary g_freeTypeLibrary; // our freetype library
+
+CGUIFontTTFBase::CGUIFontTTFBase(const CStdString& strFileName)
+{
+ m_texture = NULL;
+ m_char = NULL;
+ m_maxChars = 0;
+ m_nestedBeginCount = 0;
+
+ m_bTextureLoaded = false;
+ m_vertex_size = 4*1024;
+ m_vertex = (SVertex*)malloc(m_vertex_size * sizeof(SVertex));
+
+ m_face = NULL;
+ memset(m_charquick, 0, sizeof(m_charquick));
+ m_strFileName = strFileName;
+ m_referenceCount = 0;
+ m_originX = m_originY = 0.0f;
+ m_cellBaseLine = m_cellHeight = 0;
+ m_numChars = 0;
+ m_posX = m_posY = 0;
+ m_textureHeight = m_textureWidth = 0;
+ m_textureScaleX = m_textureScaleY = 0.0;
+ m_ellipsesWidth = m_height = 0.0f;
+ m_color = 0;
+ m_vertex_count = 0;
+ m_nTexture = 0;
+}
+
+CGUIFontTTFBase::~CGUIFontTTFBase(void)
+{
+ Clear();
+}
+
+void CGUIFontTTFBase::AddReference()
+{
+ m_referenceCount++;
+}
+
+void CGUIFontTTFBase::RemoveReference()
+{
+ // delete this object when it's reference count hits zero
+ m_referenceCount--;
+ if (!m_referenceCount)
+ g_fontManager.FreeFontFile(this);
+}
+
+
+void CGUIFontTTFBase::ClearCharacterCache()
+{
+ if (m_texture)
+ {
+ delete(m_texture);
+ }
+
+ DeleteHardwareTexture();
+
+ m_texture = NULL;
+ if (m_char)
+ delete[] m_char;
+ m_char = new Character[CHAR_CHUNK];
+ memset(m_charquick, 0, sizeof(m_charquick));
+ m_numChars = 0;
+ m_maxChars = CHAR_CHUNK;
+ // set the posX and posY so that our texture will be created on first character write.
+ m_posX = m_textureWidth;
+ m_posY = -(int)m_cellHeight;
+}
+
+void CGUIFontTTFBase::Clear()
+{
+ if (m_texture)
+ delete(m_texture);
+
+ m_texture = NULL;
+ if (m_char)
+ delete[] m_char;
+ memset(m_charquick, 0, sizeof(m_charquick));
+ m_char = NULL;
+ m_maxChars = 0;
+ m_numChars = 0;
+ m_posX = 0;
+ m_posY = 0;
+ m_nestedBeginCount = 0;
+
+ if (m_face)
+ g_freeTypeLibrary.ReleaseFont(m_face);
+ m_face = NULL;
+
+ free(m_vertex);
+ m_vertex = NULL;
+ m_vertex_count = 0;
+}
+
+bool CGUIFontTTFBase::Load(const CStdString& strFilename, float height, float aspect, float lineSpacing)
+{
+ // we now know that this object is unique - only the GUIFont objects are non-unique, so no need
+ // for reference tracking these fonts
+ m_face = g_freeTypeLibrary.GetFont(strFilename, height, aspect);
+
+ if (!m_face)
+ return false;
+
+ // grab the maximum cell height and width
+ unsigned int m_cellWidth = m_face->bbox.xMax - m_face->bbox.xMin;
+ m_cellHeight = m_face->bbox.yMax - m_face->bbox.yMin;
+ m_cellBaseLine = m_face->bbox.yMax;
+
+ unsigned int ydpi = g_freeTypeLibrary.GetDPI();
+ unsigned int xdpi = (unsigned int)MathUtils::round_int(ydpi * aspect);
+
+ m_cellWidth *= (unsigned int)(height * xdpi);
+ m_cellWidth /= (72 * m_face->units_per_EM);
+
+ m_cellHeight *= (unsigned int)(height * ydpi);
+ m_cellHeight /= (72 * m_face->units_per_EM);
+
+ m_cellBaseLine *= (unsigned int)(height * ydpi);
+ m_cellBaseLine /= (72 * m_face->units_per_EM);
+
+ // increment for good measure to give space in our texture
+ m_cellWidth++;
+ m_cellHeight+=2;
+ m_cellBaseLine++;
+
+// CLog::Log(LOGDEBUG, "%s Scaled size of font %s (%f): width = %i, height = %i, lineheight = %li",
+// __FUNCTION__, strFilename.c_str(), height, m_cellWidth, m_cellHeight, m_face->size->metrics.height / 64);
+
+ m_height = height;
+
+ if (m_texture)
+ delete(m_texture);
+
+ m_texture = NULL;
+ if (m_char)
+ delete[] m_char;
+ m_char = NULL;
+
+ m_maxChars = 0;
+ m_numChars = 0;
+
+ m_strFilename = strFilename;
+
+ m_textureHeight = 0;
+ m_textureWidth = ((m_cellHeight * CHARS_PER_TEXTURE_LINE) & ~63) + 64;
+
+ m_textureWidth = CBaseTexture::PadPow2(m_textureWidth);
+
+ if (m_textureWidth > max_texture_size) m_textureWidth = max_texture_size;
+
+ // set the posX and posY so that our texture will be created on first character write.
+ m_posX = m_textureWidth;
+ m_posY = -(int)m_cellHeight;
+
+ // cache the ellipses width
+ Character *ellipse = GetCharacter(L'.');
+ if (ellipse) m_ellipsesWidth = ellipse->advance;
+
+ return true;
+}
+
+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-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 ( 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();
+}
+
+// this routine assumes a single line (i.e. it was called from GUITextLayout)
+float CGUIFontTTFBase::GetTextWidthInternal(vecText::const_iterator start, vecText::const_iterator end)
+{
+ float width = 0;
+ while (start != end)
+ {
+ Character *c = GetCharacter(*start++);
+ if (c) width += c->advance;
+ }
+ return width;
+}
+
+float CGUIFontTTFBase::GetCharWidthInternal(character_t ch)
+{
+ Character *c = GetCharacter(ch);
+ if (c) return c->advance;
+ return 0;
+}
+
+float CGUIFontTTFBase::GetTextHeight(float lineSpacing, int numLines) const
+{
+ return (float)(numLines - 1) * GetLineHeight(lineSpacing) + (m_cellHeight - 2); // -2 as we increment this for space in our texture
+}
+
+float CGUIFontTTFBase::GetLineHeight(float lineSpacing) const
+{
+ if (m_face)
+ return lineSpacing * m_face->size->metrics.height / 64.0f;
+ return 0.0f;
+}
+
+CGUIFontTTFBase::Character* CGUIFontTTFBase::GetCharacter(character_t chr)
+{
+ wchar_t letter = (wchar_t)(chr & 0xffff);
+ character_t style = (chr & 0x3000000) >> 24;
+
+ // ignore linebreaks
+ if (letter == L'\r')
+ return NULL;
+
+ // quick access to ascii chars
+ if (letter < 255)
+ {
+ character_t ch = (style << 8) | letter;
+ if (m_charquick[ch])
+ return m_charquick[ch];
+ }
+
+ // letters are stored based on style and letter
+ character_t ch = (style << 16) | letter;
+
+ int low = 0;
+ int high = m_numChars - 1;
+ int mid;
+ while (low <= high)
+ {
+ mid = (low + high) >> 1;
+ if (ch > m_char[mid].letterAndStyle)
+ low = mid + 1;
+ else if (ch < m_char[mid].letterAndStyle)
+ high = mid - 1;
+ else
+ return &m_char[mid];
+ }
+ // if we get to here, then low is where we should insert the new character
+
+ // increase the size of the buffer if we need it
+ if (m_numChars >= m_maxChars)
+ { // need to increase the size of the buffer
+ Character *newTable = new Character[m_maxChars + CHAR_CHUNK];
+ if (m_char)
+ {
+ memcpy(newTable, m_char, low * sizeof(Character));
+ memcpy(newTable + low + 1, m_char + low, (m_numChars - low) * sizeof(Character));
+ delete[] m_char;
+ }
+ m_char = newTable;
+ m_maxChars += CHAR_CHUNK;
+
+ }
+ else
+ { // just move the data along as necessary
+ memmove(m_char + low + 1, m_char + low, (m_numChars - low) * sizeof(Character));
+ }
+ // render the character to our texture
+ // must End() as we can't render text to our texture during a Begin(), End() block
+ unsigned int nestedBeginCount = m_nestedBeginCount;
+ m_nestedBeginCount = 1;
+ if (nestedBeginCount) End();
+ if (!CacheCharacter(letter, style, m_char + low))
+ { // unable to cache character - try clearing them all out and starting over
+ CLog::Log(LOGDEBUG, "GUIFontTTF::GetCharacter: Unable to cache character. Clearing character cache of %i characters", m_numChars);
+ ClearCharacterCache();
+ low = 0;
+ if (!CacheCharacter(letter, style, m_char + low))
+ {
+ CLog::Log(LOGERROR, "GUIFontTTF::GetCharacter: Unable to cache character (out of memory?)");
+ if (nestedBeginCount) Begin();
+ m_nestedBeginCount = nestedBeginCount;
+ return NULL;
+ }
+ }
+ if (nestedBeginCount) Begin();
+ m_nestedBeginCount = nestedBeginCount;
+
+ // fixup quick access
+ memset(m_charquick, 0, sizeof(m_charquick));
+ for(int i=0;i<m_numChars;i++)
+ {
+ if ((m_char[i].letterAndStyle & 0xffff) < 255)
+ {
+ character_t ch = ((m_char[i].letterAndStyle & 0xffff0000) >> 8) | (m_char[i].letterAndStyle & 0xff);
+ m_charquick[ch] = m_char+i;
+ }
+ }
+
+ return m_char + low;
+}
+
+bool CGUIFontTTFBase::CacheCharacter(wchar_t letter, uint32_t style, Character *ch)
+{
+ int glyph_index = FT_Get_Char_Index( m_face, letter );
+
+ FT_Glyph glyph = NULL;
+ if (FT_Load_Glyph( m_face, glyph_index, FT_LOAD_TARGET_LIGHT ))
+ {
+ CLog::Log(LOGDEBUG, "%s Failed to load glyph %x", __FUNCTION__, letter);
+ return false;
+ }
+ // make bold if applicable
+ if (style & FONT_STYLE_BOLD)
+ EmboldenGlyph(m_face->glyph);
+ // and italics if applicable
+ if (style & FONT_STYLE_ITALICS)
+ ObliqueGlyph(m_face->glyph);
+ // grab the glyph
+ if (FT_Get_Glyph(m_face->glyph, &glyph))
+ {
+ CLog::Log(LOGDEBUG, "%s Failed to get glyph %x", __FUNCTION__, letter);
+ return false;
+ }
+ // render the glyph
+ if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1))
+ {
+ CLog::Log(LOGDEBUG, "%s Failed to render glyph %x to a bitmap", __FUNCTION__, letter);
+ return false;
+ }
+ FT_BitmapGlyph bitGlyph = (FT_BitmapGlyph)glyph;
+ FT_Bitmap bitmap = bitGlyph->bitmap;
+ if (bitGlyph->left < 0)
+ m_posX += -bitGlyph->left;
+
+ // check we have enough room for the character
+ if (m_posX + bitGlyph->left + bitmap.width > (int)m_textureWidth)
+ { // no space - gotta drop to the next line (which means creating a new texture and copying it across)
+ m_posX = 0;
+ m_posY += m_cellHeight;
+ if (bitGlyph->left < 0)
+ m_posX += -bitGlyph->left;
+
+ if(m_posY + m_cellHeight >= m_textureHeight)
+ {
+ // create the new larger texture
+ unsigned int newHeight = m_posY + m_cellHeight;
+ // check for max height (can't be more than max_texture_size texels
+ if (newHeight > max_texture_size)
+ {
+ CLog::Log(LOGDEBUG, "GUIFontTTF::CacheCharacter: New cache texture is too large (%u > %u pixels long)", newHeight, max_texture_size);
+ FT_Done_Glyph(glyph);
+ return false;
+ }
+
+ CBaseTexture* newTexture = NULL;
+ newTexture = ReallocTexture(newHeight);
+ if(newTexture == NULL)
+ {
+ FT_Done_Glyph(glyph);
+ return false;
+ }
+ m_texture = newTexture;
+ }
+ }
+
+ // set the character in our table
+ ch->letterAndStyle = (style << 16) | letter;
+ ch->offsetX = (short)bitGlyph->left;
+ ch->offsetY = (short)max((short)m_cellBaseLine - bitGlyph->top, 0);
+ ch->left = (float)m_posX + ch->offsetX;
+ ch->top = (float)m_posY + ch->offsetY;
+ ch->right = ch->left + bitmap.width;
+ ch->bottom = ch->top + bitmap.rows;
+ ch->advance = (float)MathUtils::round_int( (float)m_face->glyph->advance.x / 64 );
+
+ // we need only render if we actually have some pixels
+ if (bitmap.width * bitmap.rows)
+ {
+ CopyCharToTexture(bitGlyph, ch);
+ }
+ m_posX += (unsigned short)max(ch->right - ch->left + ch->offsetX, ch->advance + 1);
+ m_numChars++;
+
+ m_textureScaleX = 1.0f / m_textureWidth;
+ m_textureScaleY = 1.0f / m_textureHeight;
+
+ // free the glyph
+ FT_Done_Glyph(glyph);
+
+ return true;
+}
+
+void CGUIFontTTFBase::RenderCharacter(float posX, float posY, const Character *ch, color_t color, bool roundX)
+{
+ // actual image width isn't same as the character width as that is
+ // just baseline width and height should include the descent
+ const float width = ch->right - ch->left;
+ const float height = ch->bottom - ch->top;
+
+ // posX and posY are relative to our origin, and the textcell is offset
+ // from our (posX, posY). Plus, these are unscaled quantities compared to the underlying GUI resolution
+ CRect vertex((posX + ch->offsetX) * g_graphicsContext.GetGUIScaleX(),
+ (posY + ch->offsetY) * g_graphicsContext.GetGUIScaleY(),
+ (posX + ch->offsetX + width) * g_graphicsContext.GetGUIScaleX(),
+ (posY + ch->offsetY + height) * g_graphicsContext.GetGUIScaleY());
+ vertex += CPoint(m_originX, m_originY);
+ CRect texture(ch->left, ch->top, ch->right, ch->bottom);
+ g_graphicsContext.ClipRect(vertex, texture);
+
+ // transform our positions - note, no scaling due to GUI calibration/resolution occurs
+ float x[4];
+
+ x[0] = g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y1);
+ x[1] = g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y1);
+ x[2] = g_graphicsContext.ScaleFinalXCoord(vertex.x2, vertex.y2);
+ x[3] = g_graphicsContext.ScaleFinalXCoord(vertex.x1, vertex.y2);
+
+ if (roundX)
+ {
+ // We only round the "left" side of the character, and then use the direction of rounding to
+ // move the "right" side of the character. This ensures that a constant width is kept when rendering
+ // the same letter at the same size at different places of the screen, avoiding the problem
+ // of the "left" side rounding one way while the "right" side rounds the other way, thus getting
+ // altering the width of thin characters substantially. This only really works for positive
+ // coordinates (due to the direction of truncation for negatives) but this is the only case that
+ // really interests us anyway.
+ float rx0 = (float)MathUtils::round_int(x[0]);
+ float rx3 = (float)MathUtils::round_int(x[3]);
+ x[1] = (float)MathUtils::truncate_int(x[1]);
+ x[2] = (float)MathUtils::truncate_int(x[2]);
+ if (rx0 > x[0])
+ x[1] += 1;
+ if (rx3 > x[3])
+ x[2] += 1;
+ x[0] = rx0;
+ x[3] = rx3;
+ }
+
+ float y1 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y1));
+ float y2 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y1));
+ float y3 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x2, vertex.y2));
+ float y4 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalYCoord(vertex.x1, vertex.y2));
+
+ float z1 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y1));
+ float z2 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y1));
+ float z3 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x2, vertex.y2));
+ float z4 = (float)MathUtils::round_int(g_graphicsContext.ScaleFinalZCoord(vertex.x1, vertex.y2));
+
+ // tex coords converted to 0..1 range
+ float tl = texture.x1 * m_textureScaleX;
+ float tr = texture.x2 * m_textureScaleX;
+ float tt = texture.y1 * m_textureScaleY;
+ float tb = texture.y2 * m_textureScaleY;
+
+ // grow the vertex buffer if required
+ if(m_vertex_count >= m_vertex_size)
+ {
+ m_vertex_size *= 2;
+ m_vertex = (SVertex*)realloc(m_vertex, m_vertex_size * sizeof(SVertex));
+ }
+
+ m_color = color;
+ SVertex* v = m_vertex + m_vertex_count;
+
+ for(int i = 0; i < 4; i++)
+ {
+ v[i].r = GET_R(color);
+ v[i].g = GET_G(color);
+ v[i].b = GET_B(color);
+ v[i].a = GET_A(color);
+ }
+
+ v[0].u = tl;
+ v[0].v = tt;
+ v[0].x = x[0];
+ v[0].y = y1;
+ v[0].z = z1;
+
+ v[1].u = tr;
+ v[1].v = tt;
+ v[1].x = x[1];
+ v[1].y = y2;
+ v[1].z = z2;
+
+ v[2].u = tr;
+ v[2].v = tb;
+ v[2].x = x[2];
+ v[2].y = y3;
+ v[2].z = z3;
+
+ v[3].u = tl;
+ v[3].v = tb;
+ v[3].x = x[3];
+ v[3].y = y4;
+ v[3].z = z4;
+
+ RenderInternal(v);
+
+ m_vertex_count+=4;
+}
+
+// Oblique code - original taken from freetype2 (ftsynth.c)
+void CGUIFontTTFBase::ObliqueGlyph(FT_GlyphSlot slot)
+{
+ /* only oblique outline glyphs */
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ return;
+
+ /* we don't touch the advance width */
+
+ /* For italic, simply apply a shear transform, with an angle */
+ /* of about 12 degrees. */
+
+ FT_Matrix transform;
+ transform.xx = 0x10000L;
+ transform.yx = 0x00000L;
+
+ transform.xy = 0x06000L;
+ transform.yy = 0x10000L;
+
+ FT_Outline_Transform( &slot->outline, &transform );
+}
+
+
+// Embolden code - original taken from freetype2 (ftsynth.c)
+void CGUIFontTTFBase::EmboldenGlyph(FT_GlyphSlot slot)
+{
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ return;
+
+ /* some reasonable strength */
+ FT_Pos strength = FT_MulFix( m_face->units_per_EM,
+ m_face->size->metrics.y_scale ) / 24;
+
+ FT_BBox bbox_before, bbox_after;
+ FT_Outline_Get_CBox( &slot->outline, &bbox_before );
+ FT_Outline_Embolden( &slot->outline, strength ); // ignore error
+ FT_Outline_Get_CBox( &slot->outline, &bbox_after );
+
+ FT_Pos dx = bbox_after.xMax - bbox_before.xMax;
+ FT_Pos dy = bbox_after.yMax - bbox_before.yMax;
+
+ if ( slot->advance.x )
+ slot->advance.x += dx;
+
+ if ( slot->advance.y )
+ slot->advance.y += dy;
+
+ slot->metrics.width += dx;
+ slot->metrics.height += dy;
+ slot->metrics.horiBearingY += dy;
+ slot->metrics.horiAdvance += dx;
+ slot->metrics.vertBearingX -= dx / 2;
+ slot->metrics.vertBearingY += dy;
+ slot->metrics.vertAdvance += dy;
+}
+
+