Created
April 6, 2018 20:57
-
-
Save tylerreisinger/3f9748e88b882330a335952be31c7cc0 to your computer and use it in GitHub Desktop.
SpriteFont class cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* SpriteFont.cpp | |
* | |
* Created on: Dec 1, 2012 | |
* Author: tyler | |
*/ | |
#include "SpriteFont.h" | |
#include "FontFace.h" | |
#include "Rectangle.h" | |
SpriteFont::SpriteFont(FontFace* face) | |
{ | |
int characterWidth = face->MaxAdvanceWidth(); | |
int characterHeight = face->Height(); | |
int charCount = 127 - 32; | |
int textureWidth = 512; | |
int charactersPerLine = textureWidth / characterWidth; | |
int lines = std::ceil(static_cast<float>(charCount) / charactersPerLine + 1); | |
int textureHeight = std::pow(2, std::ceil(std::log2(lines * characterWidth))); | |
unsigned char characters[charCount + 1]; | |
for(int ch = 33; ch < 128; ++ch) | |
{ | |
characters[ch - 33] = ch; | |
} | |
m_charPositions.reserve(charCount); | |
unsigned int* bitmap = new unsigned int[textureWidth * textureHeight]; | |
FT_Face faceObj = face->GetFreeTypeFaceObject(); | |
int xCursor = 0; | |
int yCursor = 0; | |
for(int i = 0; i < charCount; ++i) | |
{ | |
FT_Load_Char(faceObj, characters[i], FT_LOAD_RENDER); | |
int belowBaseline = faceObj->glyph->bitmap_top - faceObj->glyph->bitmap.rows; | |
if(belowBaseline > 0) | |
{ | |
belowBaseline = 0; | |
} | |
for(int y = 0; y < faceObj->glyph->bitmap.rows; ++y) | |
{ | |
for(int x = 0; x < faceObj->glyph->bitmap.width; ++x) | |
{ | |
int position = textureWidth * ((yCursor + characterHeight - faceObj->glyph->bitmap_top + belowBaseline) + y) + (xCursor + x + faceObj->glyph->bitmap_left); | |
unsigned char color = faceObj->glyph->bitmap.buffer[faceObj->glyph->bitmap.pitch * y + x]; | |
if(color == 0) | |
{ | |
bitmap[position] = 0; | |
} | |
else | |
{ | |
bitmap[position] = 0x00FFFFFF | (color << 24); | |
} | |
} | |
} | |
m_charPositions[characters[i]] = std::make_tuple(xCursor / static_cast<double>(textureWidth), yCursor / static_cast<double>(textureHeight), | |
(xCursor + (faceObj->glyph->advance.x >> 6)) / static_cast<double>(textureWidth), (yCursor + characterHeight) / static_cast<double>(textureHeight), | |
belowBaseline); | |
if(xCursor < (charactersPerLine - 1) * characterWidth) | |
{ | |
xCursor += faceObj->glyph->advance.x >> 6; | |
} | |
else | |
{ | |
xCursor = 0; | |
yCursor += characterHeight; | |
} | |
} | |
m_firstCharacter = 33; | |
m_characterMaxWidth = characterWidth; | |
m_characterHeight = characterHeight; | |
m_characterCount = charCount; | |
m_glyphTexture = Texture(textureWidth, textureHeight, bitmap); | |
delete [] bitmap; | |
} | |
SpriteFont::~SpriteFont() | |
{ | |
} | |
Rectangle SpriteFont::MeasureString(const char* string, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[std::strlen(string)]; | |
for(unsigned int i = 0; i < std::strlen(string); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
Rectangle rect = MeasureString(characters, std::strlen(string), maxWidth, maxHeight); | |
delete [] characters; | |
return rect; | |
} | |
void SpriteFont::RenderString(const char* string, int x, int y, Color color, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[std::strlen(string)]; | |
for(unsigned int i = 0; i < std::strlen(string); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
RenderString(characters, std::strlen(string), x, y, color, maxWidth, maxHeight); | |
delete [] characters; | |
} | |
Rectangle SpriteFont::MeasureString(const wchar_t* string, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[std::wcslen(string)]; | |
for(unsigned int i = 0; i < std::wcslen(string); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
Rectangle rect = MeasureString(characters, std::wcslen(string), maxWidth, maxHeight); | |
delete [] characters; | |
return rect; | |
} | |
void SpriteFont::RenderString(const wchar_t* string, int x, int y, Color color, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[std::wcslen(string)]; | |
for(unsigned int i = 0; i < std::wcslen(string); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
RenderString(characters, std::wcslen(string), x, y, color, maxWidth, maxHeight); | |
delete [] characters; | |
} | |
Rectangle SpriteFont::MeasureString(const std::string& string, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[string.size()]; | |
for(unsigned int i = 0; i < string.size(); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
Rectangle rect = MeasureString(characters, string.size(), maxWidth, maxHeight); | |
delete [] characters; | |
return rect; | |
} | |
void SpriteFont::RenderString(const std::string& string, int x, int y, Color color, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[string.size()]; | |
for(unsigned int i = 0; i < string.size(); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
RenderString(characters, string.size(), x, y, color, maxWidth, maxHeight); | |
delete [] characters; | |
} | |
Rectangle SpriteFont::MeasureString(const std::wstring& string, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[string.size()]; | |
for(unsigned int i = 0; i < string.size(); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
Rectangle rect = MeasureString(characters, string.size(), maxWidth, maxHeight); | |
delete [] characters; | |
return rect; | |
} | |
void SpriteFont::RenderString(const std::wstring& string, int x, int y, Color color, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
unsigned int* characters = new unsigned int[string.size()]; | |
for(unsigned int i = 0; i < string.size(); i++) | |
{ | |
characters[i] = string[i]; | |
} | |
RenderString(characters, string.size(), x, y, color, maxWidth, maxHeight); | |
delete [] characters; | |
} | |
Rectangle SpriteFont::MeasureString(unsigned int* characters, int characterCount, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
int charactersPerLine = (maxWidth / m_characterMaxWidth); | |
int width = charactersPerLine * m_characterMaxWidth; | |
int height = (characterCount / charactersPerLine) * m_characterHeight + m_characterHeight; | |
if(characterCount < charactersPerLine) | |
{ | |
width = characterCount * m_characterMaxWidth; | |
} | |
int maxLines = maxHeight / m_characterHeight; | |
if(height > maxLines * static_cast<int>(m_characterHeight)) | |
{ | |
height = maxLines * m_characterHeight; | |
} | |
return Rectangle(0, 0, width, height); | |
} | |
void SpriteFont::RenderString(unsigned int* characters, int characterCount, int x, int y, Color color, unsigned int maxWidth, unsigned int maxHeight) | |
{ | |
int xCursor = 0; | |
int yCursor = 0; | |
int splitIndex = 0; | |
int quadCount = 0; | |
GLfloat* texCoordArray = new GLfloat[characterCount * 8]; | |
GLfloat* vertexArray = new GLfloat[characterCount * 8]; | |
for(int i = 0; i < characterCount; ++i) | |
{ | |
if(characters[i] == '\n') | |
{ | |
if(yCursor + m_characterHeight > maxHeight) | |
{ | |
break; | |
} | |
yCursor += m_characterHeight; | |
xCursor = 0.; | |
continue; | |
} | |
if(i != 0 && (characters[i - 1] == ' ' || characters[i - 1] == '-')) | |
{ | |
int advance = ShouldWrap(characters, characterCount, i, xCursor, maxWidth); | |
if(advance != -1) | |
{ | |
if(yCursor + m_characterHeight > maxHeight) | |
{ | |
break; | |
} | |
yCursor += m_characterHeight; | |
xCursor = 0.; | |
} | |
} | |
if(xCursor == 0.) | |
{ | |
int advance = ShouldWrap(characters, characterCount, i, xCursor, maxWidth); | |
if(advance != -1) | |
{ | |
splitIndex = advance + i; | |
} | |
} | |
if(i != 0 && splitIndex == static_cast<int>(i)) | |
{ | |
if(yCursor + m_characterHeight > maxHeight) | |
{ | |
break; | |
} | |
yCursor += m_characterHeight; | |
xCursor = 0.; | |
} | |
if(characters[i] == ' ') | |
{ | |
} | |
else if(m_charPositions.find(characters[i]) == m_charPositions.end()) | |
{ | |
const auto& m_charData = m_charPositions['?']; | |
texCoordArray[quadCount * 8] = std::get<0>(m_charData); | |
texCoordArray[quadCount * 8 + 1] = std::get<1>(m_charData); | |
texCoordArray[quadCount * 8 + 2] = std::get<2>(m_charData); | |
texCoordArray[quadCount * 8 + 3] = std::get<1>(m_charData); | |
texCoordArray[quadCount * 8 + 4] = std::get<2>(m_charData); | |
texCoordArray[quadCount * 8 + 5] = std::get<3>(m_charData); | |
texCoordArray[quadCount * 8 + 6] = std::get<0>(m_charData); | |
texCoordArray[quadCount * 8 + 7] = std::get<3>(m_charData); | |
vertexArray[quadCount * 8] = xCursor; | |
vertexArray[quadCount * 8 + 1] = yCursor; | |
vertexArray[quadCount * 8 + 2] = xCursor + m_characterMaxWidth; | |
vertexArray[quadCount * 8 + 3] = yCursor; | |
vertexArray[quadCount * 8 + 4] = xCursor + m_characterMaxWidth; | |
vertexArray[quadCount * 8 + 5] = yCursor + m_characterHeight; | |
vertexArray[quadCount * 8 + 6] = xCursor; | |
vertexArray[quadCount * 8 + 7] = yCursor + m_characterHeight; | |
quadCount++; | |
} | |
else | |
{ | |
const auto& m_charData = m_charPositions[characters[i]]; | |
texCoordArray[quadCount * 8] = std::get<0>(m_charData); | |
texCoordArray[quadCount * 8 + 1] = std::get<1>(m_charData); | |
texCoordArray[quadCount * 8 + 2] = std::get<2>(m_charData); | |
texCoordArray[quadCount * 8 + 3] = std::get<1>(m_charData); | |
texCoordArray[quadCount * 8 + 4] = std::get<2>(m_charData); | |
texCoordArray[quadCount * 8 + 5] = std::get<3>(m_charData); | |
texCoordArray[quadCount * 8 + 6] = std::get<0>(m_charData); | |
texCoordArray[quadCount * 8 + 7] = std::get<3>(m_charData); | |
vertexArray[quadCount * 8] = xCursor; | |
vertexArray[quadCount * 8 + 1] = yCursor - std::get<4>(m_charData); | |
vertexArray[quadCount * 8 + 2] = xCursor + m_characterMaxWidth; | |
vertexArray[quadCount * 8 + 3] = yCursor - std::get<4>(m_charData); | |
vertexArray[quadCount * 8 + 4] = xCursor + m_characterMaxWidth; | |
vertexArray[quadCount * 8 + 5] = yCursor + m_characterHeight - std::get<4>(m_charData); | |
vertexArray[quadCount * 8 + 6] = xCursor; | |
vertexArray[quadCount * 8 + 7] = yCursor + m_characterHeight - std::get<4>(m_charData); | |
quadCount++; | |
} | |
xCursor += m_characterMaxWidth; | |
} | |
glLoadIdentity(); | |
m_glyphTexture.Bind(); | |
glColor4f(color.red, color.green, color.blue, color.alpha); | |
glTranslatef(x, y, 0); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
glVertexPointer(2, GL_FLOAT, 0, vertexArray); | |
glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray); | |
glDrawArrays(GL_QUADS, 0, quadCount * 4); | |
glDisableClientState(GL_VERTEX_ARRAY); | |
glDisableClientState(GL_TEXTURE_COORD_ARRAY); | |
delete [] vertexArray; | |
delete [] texCoordArray; | |
} | |
int SpriteFont::ShouldWrap(unsigned int* characters, int characterCount, unsigned int index, unsigned int startWidth, unsigned int maxWidth) | |
{ | |
unsigned int width = startWidth; | |
for(int i = index; i < characterCount; ++i) | |
{ | |
if(i != static_cast<int>(index) && (characters[i - 1] == L' ' || characters[i - 1] == L'-')) | |
{ | |
return -1; | |
} | |
else | |
{ | |
width += m_characterMaxWidth; | |
if(width > maxWidth) | |
{ | |
if(characters[i] == ' ') | |
{ | |
return -1; | |
} | |
return i - index; | |
} | |
} | |
} | |
return -1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment