Skip to content

Commit

Permalink
Client: Added GLTextComposer, utility for drawing GL text
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed May 18, 2013
1 parent 901f470 commit ba7f4cb
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 0 deletions.
89 changes: 89 additions & 0 deletions doomsday/client/include/ui/widgets/gltextcomposer.h
@@ -0,0 +1,89 @@
/** @file gltextcomposer.h
*
* @authors Copyright (c) 2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>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 of the License, 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 this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef DENG_CLIENT_GLTEXTCOMPOSER_H
#define DENG_CLIENT_GLTEXTCOMPOSER_H

#include <QVector>
#include <de/Font>
#include <de/Atlas>
#include <de/GLBuffer>

#include "fontlinewrapping.h"

/**
* Manages lines of text on an atlas and produces geometry for drawing the
* text.
*/
class GLTextComposer
{
public:
typedef de::Vertex2TexRgba Vertex;
typedef de::GLBufferT<Vertex> VertexBuf;
typedef VertexBuf::Vertices Vertices;

/**
* Flags for specifying alignment.
*/
enum AlignmentFlag
{
AlignTop = 0x1,
AlignBottom = 0x2,
AlignLeft = 0x4,
AlignRight = 0x8,

AlignCenter = 0,

DefaultAlignment = AlignCenter
};
Q_DECLARE_FLAGS(Alignment, AlignmentFlag)

public:
GLTextComposer();

void setAtlas(de::Atlas &atlas);
void setText(de::String const &text, FontLineWrapping const &wrappedLines);

/**
* Makes sure all the lines are allocated on the atlas.
*/
void update();

/**
* Releases all the allocations from the atlas.
*/
void release();

/**
* Generates vertices for all the text lines and concatenates them
* onto the existing triangle strip in @a triStrip.
*
* @param triStrip Vertices for a triangle strip.
*/
void makeVertices(Vertices &triStrip,
de::Rectanglei const &rect,
Alignment const &alignInRect,
Alignment const &lineAlign);

private:
DENG2_PRIVATE(d)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(GLTextComposer::Alignment)

#endif // DENG_CLIENT_GLTEXTCOMPOSER_H
169 changes: 169 additions & 0 deletions doomsday/client/src/ui/widgets/gltextcomposer.cpp
@@ -0,0 +1,169 @@
/** @file gltextcomposer.cpp
*
* @authors Copyright (c) 2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>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 of the License, 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 this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#include "ui/widgets/gltextcomposer.h"

#include <QList>

using namespace de;

DENG2_PIMPL(GLTextComposer)
{
Font const *font;
Atlas *atlas;
String text;
FontLineWrapping const *wraps;
bool needRaster;

typedef QList<Id> Lines;
Lines lines;

Instance(Public *i) : Base(i), font(0), atlas(0), wraps(0), needRaster(false)
{}

~Instance()
{
releaseLines();
}

void releaseLines()
{
DENG2_ASSERT(atlas != 0);

foreach(Id const &id, lines)
{
atlas->release(id);
}
lines.clear();
}

void allocLines()
{
/// @todo Could check which lines have actually changed.

// Release the old lines.
releaseLines();

qDebug() << "allocLines for" << text << wraps->height();

for(int i = 0; i < wraps->height(); ++i)
{
shell::WrappedLine const span = wraps->line(i);
String part = text.substr(span.range.start, span.range.size());
lines.append(atlas->alloc(font->rasterize(part)));

qDebug() << lines.back().asText() << part;
}
}
};

GLTextComposer::GLTextComposer() : d(new Instance(this))
{}

void GLTextComposer::release()
{
d->releaseLines();
}

void GLTextComposer::setAtlas(Atlas &atlas)
{
d->atlas = &atlas;
}

void GLTextComposer::setText(String const &text, FontLineWrapping const &wrappedLines)
{
d->text = text;
d->wraps = &wrappedLines;
d->font = &d->wraps->font();
d->needRaster = true;
}

void GLTextComposer::update()
{
if(d->needRaster)
{
d->needRaster = false;
d->allocLines();
}

DENG2_ASSERT(d->wraps->height() == d->lines.size());
}

void GLTextComposer::makeVertices(Vertices &triStrip,
Rectanglei const &rect,
Alignment const &alignInRect,
Alignment const &lineAlign)
{
// Top left corner.
Vector2f p = rect.topLeft;

Vector2i const contentSize(d->wraps->width(), d->wraps->totalHeightInPixels());

// Apply alignment within the provided rectangle.
if(alignInRect.testFlag(AlignRight))
{
p.x += rect.width() - contentSize.x;
}
else if(!alignInRect.testFlag(AlignLeft))
{
p.x += (rect.width() - contentSize.x) / 2;
}
if(alignInRect.testFlag(AlignBottom))
{
p.y += rect.height() - contentSize.y;
}
else
{
p.y += (rect.height() - contentSize.y) / 2;
}

DENG2_ASSERT(d->wraps->height() == d->lines.size());

// Generate vertices for each line.
for(int i = 0; i < d->wraps->height(); ++i)
{
Vector2ui const size = d->atlas->imageRect(d->lines[i]).size();
Rectanglef const uv = d->atlas->imageRectf(d->lines[i]);

Vertex v;
Vertices quad;

v.rgba = Vector4f(1, 1, 1, 1); // should be a param

Vector2f linePos = p;

// Align the line.
if(lineAlign.testFlag(AlignRight))
{
linePos.x += rect.width() - size.x;
}
else if(!lineAlign.testFlag(AlignLeft))
{
linePos.x += (rect.width() - size.x) / 2;
}

v.pos = linePos; v.texCoord = uv.topLeft; quad << v;
v.pos = linePos + Vector2f(size.x, 0); v.texCoord = uv.topRight(); quad << v;
v.pos = linePos + Vector2f(0, size.y); v.texCoord = uv.bottomLeft(); quad << v;
v.pos = linePos + Vector2f(size.x, size.y); v.texCoord = uv.bottomRight; quad << v;

VertexBuf::concatenate(quad, triStrip);

p.y += d->font->lineSpacing().value();
}
}

0 comments on commit ba7f4cb

Please sign in to comment.