/** * Copyright (c) 2006-2011 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ #include "Font.h" #include #include "Quad.h" #include #include #include // for max namespace love { namespace graphics { namespace opengl { Font::Font(love::font::Rasterizer * r, const Image::Filter& filter) : rasterizer(r), height(r->getHeight()), lineHeight(1), mSpacing(1), filter(filter) { love::font::GlyphData * gd = r->getGlyphData(32); type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE); delete gd; createTexture(); } Font::~Font() { unloadVolatile(); } void Font::createTexture() { texture_x = texture_y = rowHeight = 0; GLuint t; glGenTextures(1, &t); textures.push_back(t); glBindTexture(GL_TEXTURE_2D, t); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (filter.mag == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (filter.min == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)TEXTURE_WIDTH, (GLsizei)TEXTURE_HEIGHT, 0, format, GL_UNSIGNED_BYTE, NULL); } Font::Glyph * Font::addGlyph(int glyph) { Glyph * g = new Glyph; g->list = glGenLists(1); if (g->list == 0) { // opengl failed to generate the list delete g; return NULL; } love::font::GlyphData *gd = rasterizer->getGlyphData(glyph); g->spacing = gd->getAdvance(); int w = gd->getWidth(); int h = gd->getHeight(); if (texture_x + w > TEXTURE_WIDTH) { // out of space - new row! texture_x = 0; texture_y += rowHeight; rowHeight = 0; } if (texture_y + h > TEXTURE_HEIGHT) { // totally out of space - new texture! createTexture(); } GLuint t = textures.back(); glBindTexture(GL_TEXTURE_2D, t); glTexSubImage2D(GL_TEXTURE_2D, 0, texture_x, texture_y, w, h, (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA), GL_UNSIGNED_BYTE, gd->getData()); Quad::Viewport v; v.x = texture_x; v.y = texture_y; v.w = w; v.h = h; Quad * q = new Quad(v, TEXTURE_WIDTH, TEXTURE_HEIGHT); const vertex * verts = q->getVertices(); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].x); glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].s); glNewList(g->list, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, t); glPushMatrix(); glTranslatef(static_cast(gd->getBearingX()), static_cast(-gd->getBearingY()), 0.0f); glDrawArrays(GL_QUADS, 0, 4); glPopMatrix(); glEndList(); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); delete q; delete gd; texture_x += w; rowHeight = std::max(rowHeight, h); glyphs[glyph] = g; return g; } float Font::getHeight() const { return static_cast(height); } void Font::print(std::string text, float x, float y, float angle, float sx, float sy) { float dx = 0.0f; // spacing counter for newline handling glPushMatrix(); glTranslatef(ceil(x), ceil(y), 0.0f); glRotatef(LOVE_TODEG(angle), 0, 0, 1.0f); glScalef(sx, sy, 1.0f); for (unsigned int i = 0; i < text.size(); i++) { unsigned char g = (unsigned char)text[i]; if (g == '\n') { // wrap newline, but do not print it glTranslatef(-dx, floor(getHeight() * getLineHeight() + 0.5f), 0); dx = 0.0f; continue; } Glyph * glyph = glyphs[g]; if (!glyph) glyph = addGlyph(g); glPushMatrix(); // 1.25 is magic line height for true type fonts if (type == FONT_TRUETYPE) glTranslatef(0, floor(getHeight() / 1.25f + 0.5f), 0); glCallList(glyph->list); glPopMatrix(); glTranslatef(static_cast(glyph->spacing), 0, 0); dx += glyph->spacing; } glPopMatrix(); } void Font::print(char character, float x, float y) { Glyph * glyph = glyphs[character]; if (!glyph) glyph = addGlyph(character); glPushMatrix(); glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f); glCallList(glyph->list); glPopMatrix(); } int Font::getWidth(const std::string & line) { if(line.size() == 0) return 0; int temp = 0; Glyph * g; for(unsigned int i = 0; i < line.size(); i++) { g = glyphs[line[i]]; if (!g) g = addGlyph(line[i]); temp += static_cast(g->spacing * mSpacing); } return temp; } int Font::getWidth(const char * line) { return this->getWidth(std::string(line)); } int Font::getWidth(const char character) { Glyph * g = glyphs[character]; if (!g) g = addGlyph(character); return g->spacing; } int Font::getWrap(const std::string & line, float wrap, int * lines) { if(line.size() == 0) return 0; int maxw = 0; int linen = 1; int temp = 0; std::string text; Glyph * g; for(unsigned int i = 0; i < line.size(); i++) { if(temp > wrap && text.find(" ") != std::string::npos) { unsigned int space = text.find_last_of(' '); std::string tmp = text.substr(0, space); int w = getWidth(tmp); if(w > maxw) maxw = w; text = text.substr(space+1); temp = getWidth(text); linen++; } g = glyphs[line[i]]; if (!g) g = addGlyph(line[i]); temp += static_cast(g->spacing * mSpacing); text += line[i]; } if(temp > maxw) maxw = temp; if(lines) *lines = linen; return maxw; } int Font::getWrap(const char * line, float wrap, int * lines) { return getWrap(std::string(line), wrap, lines); } void Font::setLineHeight(float height) { this->lineHeight = height; } float Font::getLineHeight() const { return lineHeight; } void Font::setSpacing(float amount) { mSpacing = amount; } float Font::getSpacing() const { return mSpacing; } bool Font::loadVolatile() { createTexture(); return true; } void Font::unloadVolatile() { // nuke everything from orbit std::map::iterator it = glyphs.begin(); Glyph * g; while (it != glyphs.end()) { g = it->second; glDeleteLists(g->list, 1); delete g; glyphs.erase(it++); } std::vector::iterator iter = textures.begin(); while (iter != textures.end()) { glDeleteTextures(1, (GLuint*)&*iter); iter++; } textures.clear(); } } // opengl } // graphics } // love