mirror of
https://github.com/love2d/love.git
synced 2026-08-15 15:51:12 +02:00
312 lines
7.9 KiB
C++
312 lines
7.9 KiB
C++
/**
|
|
* 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 <common/config.h>
|
|
#include "Font.h"
|
|
#include <font/GlyphData.h>
|
|
#include "Quad.h"
|
|
|
|
#include <libraries/utf8/utf8.h>
|
|
|
|
#include <common/math.h>
|
|
#include <math.h>
|
|
|
|
#include <algorithm> // 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)
|
|
{
|
|
r->retain();
|
|
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()
|
|
{
|
|
rasterizer->release();
|
|
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<float>(gd->getBearingX()), static_cast<float>(-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<float>(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);
|
|
try
|
|
{
|
|
utf8::iterator<std::string::iterator> i (text.begin(), text.begin(), text.end());
|
|
utf8::iterator<std::string::iterator> end (text.end(), text.begin(), text.end());
|
|
while (i != end) {
|
|
int g = *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<GLfloat>(glyph->spacing), 0, 0);
|
|
dx += glyph->spacing;
|
|
}
|
|
}
|
|
catch (utf8::invalid_utf8 e)
|
|
{
|
|
glPopMatrix();
|
|
throw love::Exception(e.what());
|
|
}
|
|
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;
|
|
|
|
utf8::iterator<std::string::const_iterator> i (line.begin(), line.begin(), line.end());
|
|
utf8::iterator<std::string::const_iterator> end (line.end(), line.begin(), line.end());
|
|
while (i != end) {
|
|
int c = *i++;
|
|
g = glyphs[c];
|
|
if (!g) g = addGlyph(c);
|
|
temp += static_cast<int>(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;
|
|
|
|
|
|
utf8::iterator<std::string::const_iterator> i (line.begin(), line.begin(), line.end());
|
|
utf8::iterator<std::string::const_iterator> end (line.end(), line.begin(), line.end());
|
|
while (i != end) {
|
|
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++;
|
|
}
|
|
int c = *i++;
|
|
g = glyphs[c];
|
|
if (!g) g = addGlyph(c);
|
|
temp += static_cast<int>(g->spacing * mSpacing);
|
|
utf8::append(c, text.end());
|
|
}
|
|
|
|
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<int, Glyph *>::iterator it = glyphs.begin();
|
|
Glyph * g;
|
|
while (it != glyphs.end()) {
|
|
g = it->second;
|
|
glDeleteLists(g->list, 1);
|
|
delete g;
|
|
glyphs.erase(it++);
|
|
}
|
|
std::vector<GLuint>::iterator iter = textures.begin();
|
|
while (iter != textures.end()) {
|
|
glDeleteTextures(1, (GLuint*)&*iter);
|
|
iter++;
|
|
}
|
|
textures.clear();
|
|
}
|
|
|
|
} // opengl
|
|
} // graphics
|
|
} // love
|