00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "textrenderer.h"
00028
00029 #include "glwidget.h"
00030
00031 #include <QPainter>
00032 #include <QHash>
00033 #include <QMap>
00034 #include <QDebug>
00035
00036 namespace {
00037
00038 #define OUTLINE_WIDTH 3
00039 const int OUTLINE_BRUSH[2*OUTLINE_WIDTH+1][2*OUTLINE_WIDTH+1]
00040 = { { 10, 30, 45, 50, 45, 30, 10 },
00041 { 30, 65, 85, 100, 85, 65, 30 },
00042 { 45, 85, 200, 256, 200, 85, 45 },
00043 { 50, 100, 256, 256, 256, 100, 50},
00044 { 45, 85, 200, 256, 200, 85, 45 },
00045 { 30, 65, 85, 100, 85, 65, 30 },
00046 { 10, 30, 45, 50, 45, 30, 10 } };
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00069 class CharRenderer
00070 {
00071 protected:
00072 GLuint m_glyphTexture;
00073 GLuint m_outlineTexture;
00074
00075 GLuint m_quadDisplayList;
00076
00077 GLenum m_textureTarget;
00078
00082 int m_realwidth, m_realheight;
00083
00084 public:
00085 CharRenderer();
00086 ~CharRenderer();
00087
00089 bool initialize( QChar c, const QFont &font, GLenum textureTarget );
00090
00092 void draw(const float *color) const;
00093
00095 inline int height() const { return m_realheight; }
00096
00098 inline int width() const { return m_realwidth; }
00099
00100 inline void drawOutline() const
00101 {
00102 glBindTexture(m_textureTarget, m_outlineTexture);
00103 glCallList( m_quadDisplayList );
00104 }
00105
00106 inline void drawGlyph() const
00107 {
00108 glBindTexture(m_textureTarget, m_glyphTexture);
00109 glCallList( m_quadDisplayList );
00110 }
00111 };
00112
00113 CharRenderer::CharRenderer()
00114 {
00115 m_glyphTexture = 0;
00116 m_outlineTexture = 0;
00117 m_quadDisplayList = 0;
00118 }
00119
00120 CharRenderer::~CharRenderer()
00121 {
00122 if( m_glyphTexture ) glDeleteTextures( 1, &m_glyphTexture );
00123 if( m_outlineTexture ) glDeleteTextures( 1, &m_outlineTexture );
00124 if( m_quadDisplayList ) glDeleteLists( m_quadDisplayList, 1 );
00125 }
00126
00127 static void normalizeTexSize( GLenum textureTarget,
00128 int& texwidth, int& texheight )
00129 {
00130
00131
00132
00133 if( textureTarget == GL_TEXTURE_2D )
00134 {
00135 int x = qMax( texwidth, texheight );
00136
00137 int n;
00138 for(n = 1; n < x; n = n << 1) {}
00139
00140 texwidth = texheight = n;
00141 }
00142 }
00143
00144 bool CharRenderer::initialize( QChar c, const QFont &font, GLenum textureTarget )
00145 {
00146 if( m_quadDisplayList ) return true;
00147 m_textureTarget = textureTarget;
00148
00149
00150
00151 const QFontMetrics fontMetrics ( font );
00152 m_realwidth = fontMetrics.width(c);
00153 m_realheight = fontMetrics.height();
00154 if(m_realwidth == 0 || m_realheight == 0) return false;
00155 int texwidth = m_realwidth + 2 * OUTLINE_WIDTH;
00156 int texheight = m_realheight + 2 * OUTLINE_WIDTH;
00157 normalizeTexSize(textureTarget, texwidth, texheight);
00158
00159
00160 QImage image( texwidth, texheight, QImage::Format_RGB32 );
00161 QPainter painter;
00162
00163 painter.begin( &image );
00164 painter.setFont( font );
00165 painter.setRenderHint( QPainter::TextAntialiasing );
00166 painter.setBackground( Qt::black );
00167 painter.eraseRect( image.rect() );
00168
00169 painter.setPen( Qt::blue );
00170
00171
00172
00173 painter.drawText ( 1, m_realheight
00174 + 2 * OUTLINE_WIDTH
00175 - painter.fontMetrics().descent(),
00176 c );
00177
00178 painter.end();
00179
00180
00181
00182
00183
00184
00185
00186
00187 int *rawbitmap = new int[ texwidth * texheight ];
00188 if( ! rawbitmap ) return false;
00189 int n = 0;
00190
00191 for( int j = texheight - 1; j >= 0; j-- )
00192 for( int i = 0; i < texwidth; i++, n++ )
00193 {
00194 double x = qBlue( image.pixel( i, j ) ) / 255.0;
00195 double y = pow(x, 0.75);
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 rawbitmap[n] = static_cast<int>(255.0 * y);
00207 }
00208
00209
00210
00211
00212
00213
00214
00215 int *neighborhood = new int[ texwidth * texheight ];
00216 if( ! neighborhood ) return false;
00217 for( int i = 0; i < texheight * texwidth; i++)
00218 neighborhood[i] = 0;
00219
00220 for( int i = 0; i < texheight; i++ ) {
00221 for( int j = 0; j < texwidth; j++ ) {
00222 n = j + i * texwidth;
00223 for( int di = -OUTLINE_WIDTH; di <= OUTLINE_WIDTH; di++ ) {
00224 for( int dj = -OUTLINE_WIDTH; dj <= OUTLINE_WIDTH; dj++ ) {
00225 int fi = i + di;
00226 int fj = j + dj;
00227 if( fi >= 0 && fi < texheight && fj >= 0 && fj < texwidth ) {
00228 int fn = fj + fi * texwidth;
00229 neighborhood[fn]
00230 = qMax(
00231 neighborhood[fn],
00232 rawbitmap[n]
00233 * OUTLINE_BRUSH[OUTLINE_WIDTH + di]
00234 [OUTLINE_WIDTH + dj]);
00235 }
00236 }
00237 }
00238 }
00239 }
00240
00241
00242
00243
00244 GLubyte *glyphbitmap = new GLubyte[ texwidth * texheight ];
00245 if( ! glyphbitmap ) return false;
00246 GLubyte *outlinebitmap = new GLubyte[ texwidth * texheight ];
00247 if( ! outlinebitmap ) return false;
00248
00249 for( int n = 0; n < texwidth * texheight; n++ )
00250 {
00251 glyphbitmap[n] = static_cast<GLubyte>(rawbitmap[n]);
00252 int alpha = (neighborhood[n] >> 8) + rawbitmap[n];
00253 if( alpha > 255 ) {
00254 alpha = 255;
00255 }
00256 outlinebitmap[n] = static_cast<GLubyte>(alpha);
00257 }
00258
00259 delete [] rawbitmap;
00260 delete [] neighborhood;
00261
00262
00263
00264 glGenTextures( 1, &m_glyphTexture );
00265 if( ! m_glyphTexture ) return false;
00266 glGenTextures( 1, &m_outlineTexture );
00267 if( ! m_outlineTexture ) return false;
00268
00269 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
00270
00271 glBindTexture( textureTarget, m_glyphTexture );
00272 glTexImage2D(
00273 textureTarget,
00274 0,
00275 GL_ALPHA,
00276 texwidth,
00277 texheight,
00278 0,
00279 GL_ALPHA,
00280 GL_UNSIGNED_BYTE,
00281 glyphbitmap );
00282
00283 glTexParameteri( textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
00284 glTexParameteri( textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00285
00286 glBindTexture( textureTarget, m_outlineTexture );
00287 glTexImage2D(
00288 textureTarget,
00289 0,
00290 GL_ALPHA,
00291 texwidth,
00292 texheight,
00293 0,
00294 GL_ALPHA,
00295 GL_UNSIGNED_BYTE,
00296 outlinebitmap );
00297
00298 glTexParameteri( textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
00299 glTexParameteri( textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00300
00301
00302 delete [] glyphbitmap;
00303 delete [] outlinebitmap;
00304
00305
00306
00307 m_quadDisplayList = glGenLists(1);
00308 if( ! m_quadDisplayList ) return false;
00309
00310 int texcoord_width = (textureTarget == GL_TEXTURE_2D) ? 1 : texwidth;
00311 int texcoord_height = (textureTarget == GL_TEXTURE_2D) ? 1 : texheight;
00312
00313 glNewList( m_quadDisplayList, GL_COMPILE );
00314 glBegin( GL_QUADS );
00315 glTexCoord2i( 0, 0);
00316 glVertex2f( 0 , -texheight );
00317 glTexCoord2i( texcoord_width, 0);
00318 glVertex2f( texwidth , -texheight );
00319 glTexCoord2i( texcoord_width, texcoord_height);
00320 glVertex2f( texwidth, 0 );
00321 glTexCoord2i( 0, texcoord_height);
00322 glVertex2f( 0 , 0 );
00323 glEnd();
00324 glEndList();
00325
00326 return true;
00327 }
00328
00329 }
00330
00331 namespace KGLLib {
00332
00333 class TextRenderer::Private
00334 {
00335 public:
00336
00337 Private() : initialized(false) {}
00338 ~Private() {}
00339
00348 QMap<QFont, QHash<QChar, CharRenderer*> > charTable;
00349
00354 GLWidget *glwidget;
00355
00356 GLboolean textmode;
00357
00358 bool initialized;
00359
00360 GLenum textureTarget;
00361
00362 void do_draw(const QString &string, const QFont& font);
00363 };
00364
00365 void TextRenderer::Private::do_draw( const QString &string, const QFont& font )
00366 {
00367 int i;
00368 GLfloat color[4];
00369 glGetFloatv(GL_CURRENT_COLOR, color);
00370
00371 QHash<QChar, CharRenderer*>& chars = charTable[font];
00372 const QFontMetrics fontMetrics ( font );
00373
00374
00375 for( i = 0; i < string.size(); i++ )
00376 {
00377 if( ! chars.contains( string[i]) )
00378 {
00379 CharRenderer *c = new CharRenderer;
00380 if(!c->initialize( string[i], font, textureTarget ) )
00381 {
00382 delete c;
00383 c = new CharRenderer;
00384 qDebug() << "Character " << string[i]
00385 << "(unicode" << string[i].unicode()
00386 << ") failed to render using the following font:";
00387 qDebug() << font.toString();
00388 if(!c->initialize( '*', font, textureTarget ))
00389 {
00390 qDebug() << "Can't render even a simple character (*).";
00391 qDebug() << "Are you using a bad font, or what?";
00392 qDebug() << "The font being used is:";
00393 qDebug() << font.toString();
00394 assert(false);
00395 }
00396 }
00397 chars.insert( string[i], c);
00398 }
00399 }
00400
00401
00402 glColor4f(0,0,0,color[3]);
00403 glPushMatrix();
00404 for( i = 0; i < string.size(); i++ )
00405 {
00406 chars.value( string[i] )->drawOutline();
00407 glTranslatef(fontMetrics.charWidth(string, i), 0, 0);
00408 }
00409 glPopMatrix();
00410
00411
00412 glPushMatrix();
00413 glColor4fv(color);
00414 for( i = 0; i < string.size(); i++ )
00415 {
00416 chars.value( string[i] )->drawGlyph();
00417 glTranslatef(fontMetrics.charWidth(string, i), 0, 0);
00418 }
00419 glPopMatrix();
00420 }
00421
00422
00423 TextRenderer::TextRenderer() : d(new Private)
00424 {
00425 d->glwidget = 0;
00426 d->textmode = false;
00427 }
00428
00429 TextRenderer::~TextRenderer()
00430 {
00431 QMap<QFont, QHash<QChar, CharRenderer*> >::iterator j = d->charTable.begin();
00432 for(; j != d->charTable.end(); j++ )
00433 {
00434 QHash<QChar, CharRenderer *>::iterator i = j.value().begin();
00435 for(; i != j.value().end(); i++ )
00436 {
00437 delete i.value();
00438 }
00439 }
00440 delete d;
00441 }
00442
00443
00444
00445
00446
00447
00448
00449
00450 void TextRenderer::begin(GLWidget *widget)
00451 {
00452 if(!d->initialized) {
00453 if(GLEW_ARB_texture_rectangle) {
00454 d->textureTarget = GL_TEXTURE_RECTANGLE_ARB;
00455 qDebug() << "OpenGL extension GL_ARB_texture_rectangle is present.";
00456 } else {
00457 d->textureTarget = GL_TEXTURE_2D;
00458 qDebug() << "OpenGL extension GL_ARB_texture_rectangle is absent.";
00459 }
00460 d->initialized = true;
00461 }
00462
00463
00464 if(d->glwidget == widget)
00465 {
00466 return;
00467 }
00468
00469
00470
00471 assert(!d->glwidget);
00472
00473 d->glwidget = widget;
00474 d->textmode = true;
00475
00476 glPushAttrib(GL_ALL_ATTRIB_BITS);
00477 glDisable(GL_LIGHTING);
00478 glDisable(GL_FOG);
00479 glDisable(GL_CULL_FACE);
00480 glEnable(d->textureTarget);
00481 glEnable(GL_BLEND);
00482 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00483 glDepthMask(GL_FALSE);
00484 glMatrixMode(GL_PROJECTION);
00485 glPushMatrix();
00486 glLoadIdentity();
00487 glOrtho( 0, d->glwidget->width(), 0, d->glwidget->height(), 0, 1 );
00488 glMatrixMode( GL_MODELVIEW );
00489 }
00490
00491 void TextRenderer::end()
00492 {
00493 if(d->glwidget) {
00494 assert(d->textmode);
00495 glMatrixMode( GL_PROJECTION );
00496 glPopMatrix();
00497 glMatrixMode( GL_MODELVIEW );
00498 glPopAttrib();
00499 d->textmode = false;
00500 d->glwidget = 0;
00501 }
00502 }
00503
00504 int TextRenderer::draw( int x, int y, const QString &string, const QFont& font )
00505 {
00506 assert(d->textmode);
00507 if( string.isEmpty() ) return 0;
00508 glPushMatrix();
00509 glLoadIdentity();
00510 const QFontMetrics fontMetrics ( font );
00511 glTranslatef( x, d->glwidget->height() - y, 0 );
00512
00513
00514 QStringList lines = string.split('\n');
00515 foreach (QString line, lines) {
00516 d->do_draw(line, font);
00517
00518 glTranslatef(0, -fontMetrics.lineSpacing(), 0);
00519 }
00520
00521 glPopMatrix();
00522 return lines.count() * fontMetrics.lineSpacing();
00523 }
00524
00525 int TextRenderer::draw( const QRect& rect, const QString &string, int flags, const QFont& font)
00526 {
00527 glDisable(GL_DEPTH_TEST);
00528 const QFontMetrics fontMetrics(font);
00529
00530 QStringList lines;
00531 if (flags & Qt::TextSingleLine) {
00532
00533 lines.append(string);
00534 } else {
00535
00536 lines = string.split('\n');
00537 }
00538 if (flags & Qt::TextWordWrap) {
00539
00540 QStringList newlines;
00541 foreach (QString line, lines) {
00542 QString newline;
00543 int newlinew = 0;
00544 QStringList words = line.simplified().split(' ');
00545 foreach (QString word, words) {
00546 int wordw = fontMetrics.width(" " + word);
00547 if (newlinew + wordw > rect.width()) {
00548
00549 newlines.append(newline);
00550 newline = QString();
00551 newlinew = 0;
00552 }
00553 if (!newline.isEmpty()) {
00554 newline += ' ';
00555 }
00556 newline += word;
00557 newlinew += wordw;
00558 }
00559 if (!newline.isEmpty()) {
00560 newlines.append(newline);
00561 }
00562 }
00563 lines = newlines;
00564 }
00565
00566
00567 int texth = lines.count() * fontMetrics.lineSpacing();
00568
00569
00570 int y = rect.top();
00571 if (flags & Qt::AlignBottom) {
00572 y = rect.bottom() - texth;
00573 } else if (flags & Qt::AlignVCenter) {
00574 y = rect.center().y() - texth/2;
00575 }
00576
00577 for (int i = 0; i < lines.count(); i++) {
00578 const QString& line = lines[i];
00579
00580 int x = rect.left();
00581 if (flags & Qt::AlignRight) {
00582 x = rect.right() - fontMetrics.width(line);
00583 } else if (flags & Qt::AlignHCenter) {
00584 x = rect.center().x() - fontMetrics.width(line)/2;
00585 }
00586
00587 y += draw(x, y, line, font);
00588 }
00589 return texth;
00590 }
00591
00592 int TextRenderer::draw( const Eigen::Vector3d &pos, const QString &string, const QFont& font )
00593 {
00594 assert(d->textmode);
00595 if( string.isEmpty() ) return 0;
00596
00597 const QFontMetrics fontMetrics ( font );
00598 int w = fontMetrics.width(string);
00599 int h = fontMetrics.height();
00600
00601
00602 Eigen::Vector3d wincoords;
00603
00604
00605 wincoords.y() = d->glwidget->height() - wincoords.y();
00606
00607 wincoords.x() -= w/2;
00608 wincoords.y() += h/2;
00609
00610 glPushMatrix();
00611 glLoadIdentity();
00612 glTranslatef( static_cast<int>(wincoords.x()),
00613 static_cast<int>(wincoords.y()),
00614 -wincoords.z() );
00615 d->do_draw(string, font);
00616 glPopMatrix();
00617 return fontMetrics.lineSpacing();
00618 }
00619
00620 bool TextRenderer::isActive()
00621 {
00622 return d->glwidget;
00623 }
00624
00625 }