NGL  6.5
The NCCA Graphics Library
Text.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 Jon Macey
3 
4  This program is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 //---------------------------------------------------------------------------
18 
19 #include <iostream>
20 #include <QtGui/QImage>
21 #include <QFontMetrics>
22 #include <unordered_map>
23 
24 #include <QPainter>
25 #include <array>
26 #include <memory>
27 #include "Text.h"
28 #include "ShaderLib.h"
29 
30 namespace ngl
31 {
32 
33 
34 //---------------------------------------------------------------------------
37 // OpenGL needs textures to be in powers of two, this function will get the
38 // nearest power of two to the current value passed in
39 //---------------------------------------------------------------------------
40 unsigned int nearestPowerOfTwo ( unsigned int _num )
41 {
42  unsigned int j, k;
43  (j = _num & 0xFFFF0000) || (j = _num);
44  (k = j & 0xFF00FF00) || (k = j);
45  (j = k & 0xF0F0F0F0) || (j = k);
46  (k = j & 0xCCCCCCCC) || (k = j);
47  (j = k & 0xAAAAAAAA) || (j = k);
48  return j << 1;
49 }
50 // end citation
51 
52 //---------------------------------------------------------------------------
53 Text::Text( const QFont &_f) noexcept
54 {
55 
56  // so first we grab the font metric of the font being used
57  QFontMetrics metric(_f);
58  // this allows us to get the height which should be the same for all
59  // fonts of the same class as this is the total glyph height
60  int fontHeight=metric.height();
61 
62  // loop for all basic keyboard chars we will use space to ~
63  // should really change this to unicode at some stage
64  const static char startChar=' ';
65  const static char endChar='~';
66  // Most OpenGL cards need textures to be in powers of 2 (128x512 1024X1024 etc etc) so
67  // to be safe we will conform to this and calculate the nearest power of 2 for the glyph height
68  // we will do the same for each width of the font below
69  unsigned int heightPow2=nearestPowerOfTwo(fontHeight);
70 
71  // we are now going to create a texture / billboard for each font
72  // they will be the same height but will possibly have different widths
73  // as some of the fonts will be the same width, to save VAO space we will only create
74  // a vao if we don't have one of the set width. To do this we use the has below
75  std::unordered_map <int,AbstractVAO *> widthVAO;
76 
77  for(char c=startChar; c<=endChar; ++c)
78  {
79  QChar ch(c);
80  FontChar fc;
81  // get the width of the font and calculate the ^2 size
82  int width=metric.width(c);
83  unsigned int widthPow2=nearestPowerOfTwo(width);
84  // now we set the texture co-ords for our quad it is a simple
85  // triangle billboard with tex-cords as shown
86  // s0/t0 ---- s1,t0
87  // |\ |
88  // | \|
89  // s0,t1 ---- s1,t1
90  // each quad will have the same s0 and the range s0-s1 == 0.0 -> 1.0
91  Real s0=0.0;
92  // we now need to scale the tex cord to it ranges from 0-1 based on the coverage
93  // of the glyph and not the power of 2 texture size. This will ensure that kerns
94  // / ligatures match
95  Real s1=width*1.0f/widthPow2;
96  // t0 will always be the same
97  Real t0=0.0f;
98  // this will scale the height so we only get coverage of the glyph as above
99  Real t1=metric.height()*-1.0f/heightPow2;
100  // we need to store the font width for later drawing
101  fc.width=width;
102  // now we will create a QImage to store the texture, basically we are going to draw
103  // into the qimage then save this in OpenGL format and load as a texture.
104  // This is relativly quick but should be done as early as possible for max performance when drawing
105  QImage finalImage(nearestPowerOfTwo(width),nearestPowerOfTwo(fontHeight),QImage::Format_ARGB32);
106  // set the background for transparent so we can avoid any areas which don't have text in them
107  finalImage.fill(Qt::transparent);
108  // we now use the QPainter class to draw into the image and create our billboards
109  QPainter painter;
110  painter.begin(&finalImage);
111  // try and use high quality text rendering (works well on the mac not as good on linux)
112  painter.setRenderHints(QPainter::HighQualityAntialiasing
113  | QPainter::TextAntialiasing);
114  // set the font to draw with
115  painter.setFont(_f);
116  // we set the glyph to be drawn in black the shader will override the actual colour later
117  // see TextShader.h in src/shaders/
118  painter.setPen(Qt::black);
119  // finally we draw the text to the Image
120  painter.drawText(0, metric.ascent(), QString(c));
121  painter.end();
122  // for debug purposes we can save the files as .png and view them
123  // not needed just useful when developing the class/
124  /*
125  QString filename=".png";
126  filename.prepend(c);
127  finalImage.save(filename);
128  */
129 
130  // now we create the OpenGL texture ID and bind to make it active
131  glGenTextures(1, &fc.textureID);
133  // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
134  // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
137 
138 
139  // QImage has a method to convert itself to a format suitable for OpenGL
140  finalImage=finalImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
141  // set rgba image data
142  int widthTexture=finalImage.width();
143  int heightTexture=finalImage.height();
144  std::unique_ptr<unsigned char []> data(new unsigned char[ widthTexture*heightTexture * 4]);
145  unsigned int index=0;
146  QRgb colour;
147  for(int y=heightTexture-1; y>0; --y)
148  {
149  for(int x=0; x<widthTexture; ++x)
150  {
151  colour=finalImage.pixel(x,y);
152  data[index++]=static_cast<unsigned char>(qRed(colour));
153  data[index++]=static_cast<unsigned char>(qGreen(colour));
154  data[index++]=static_cast<unsigned char>(qBlue(colour));
155  data[index++]=static_cast<unsigned char>(qAlpha(colour));
156  }
157  }
158 
159  // the image in in RGBA format and unsigned byte load it ready for later
160  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthTexture, heightTexture,0, GL_RGBA, GL_UNSIGNED_BYTE, data.get());
162 
163  // see if we have a Billboard of this width already
164  if (widthVAO.find(width) ==widthVAO.end())
165  {
166  // this structure is used by the VAO to store the data to be uploaded
167  // for drawing the quad
168  struct textVertData
169  {
170  Real x;
171  Real y;
172  Real u;
173  Real v;
174  };
175  // we are creating a billboard with two triangles so we only need the
176  // 6 verts, (could use index and save some space but shouldn't be too much of an
177  // issue
178  std::array<textVertData,6> d;
179  // load values for triangle 1
180  d[0].x=0;
181  d[0].y=0;
182  d[0].u=s0;
183  d[0].v=t0;
184 
185  d[1].x=fc.width;
186  d[1].y=0;
187  d[1].u=s1;
188  d[1].v=t0;
189 
190  d[2].x=0;
191  d[2].y=fontHeight;
192  d[2].u=s0;
193  d[2].v=t1;
194  // load values for triangle two
195  d[3].x=0;
196  d[3].y=0+fontHeight;
197  d[3].u=s0;
198  d[3].v=t1;
199 
200 
201  d[4].x=fc.width;
202  d[4].y=0;
203  d[4].u=s1;
204  d[4].v=t0;
205 
206 
207  d[5].x=fc.width;
208  d[5].y=fontHeight;
209  d[5].u=s1;
210  d[5].v=t1;
211 
212 
213  // now we create a VAO to store the data
215  // bind it so we can set values
216  vao->bind();
217  // set the vertex data (2 for x,y 2 for u,v)
218  vao->setData(SimpleVAO::VertexData(6*sizeof(textVertData),d[0].x));
219  // now we set the attribute pointer to be 0 (as this matches vertIn in our shader)
220  vao->setVertexAttributePointer(0,2,GL_FLOAT,sizeof(textVertData),0);
221  // We can now create another set of data (which will be added to the VAO)
222  // in this case the UV co-ords
223  // now we set this as the 2nd attribute pointer (1) to match inUV in the shader
224  vao->setVertexAttributePointer(1,2,GL_FLOAT,sizeof(textVertData),2);
225  // say how many indecis to be rendered
226  vao->setNumIndices(6);
227 
228  // now unbind
229  vao->unbind();
230  // store the vao pointer for later use in the draw method
231  fc.vao =vao;
232  widthVAO[width]=vao;
233  }
234  else
235  {
236  fc.vao=widthVAO[width];
237  }
238  // finally add the element to the map, this must be the last
239  // thing we do
240  m_characters[c]=std::move(fc);
241  }
242  std::cout<<"created "<<widthVAO.size()<<" unique billboards\n";
243  // set a default colour (black) incase user forgets
244  this->setColour(0,0,0);
245  this->setTransform(1.0,1.0);
246 }
247 
248 
249 //---------------------------------------------------------------------------
251 {
252  // our dtor should clear out the textures and remove the VAO's
253  for( auto &m : m_characters)
254  {
255  glDeleteTextures(1,&m.textureID);
256  m.vao->removeVAO();
257  }
258 
259 }
260 
261 
262 
263 
264 //---------------------------------------------------------------------------
265 void Text::renderText( float _x, float _y, const QString &text ) const noexcept
266 {
267  // make sure we are in texture unit 0 as this is what the
268  // shader expects
270  // grab an instance of the shader manager
272  // use the built in text rendering shader
273  (*shader)["nglTextShader"]->use();
274  // the y pos will always be the same so set it once for each
275  // string we are rendering
276  shader->setRegisteredUniform1f("ypos",_y);
277  // now enable blending and disable depth sorting so the font renders
278  // correctly
282  // now loop for each of the char and draw our billboard
283  int textLength=text.length();
284 
285  for ( int i = 0; i < textLength; ++i)
286  {
287  // set the shader x position this will change each time
288  // we render a glyph by the width of the char
289  shader->setRegisteredUniform1f("xpos",_x);
290  // so find the FontChar data for our current char
291 // FontChar f = m_characters[text[i].toAscii()];
292  FontChar f = m_characters[text[i].toLatin1()];
293 
294  // bind the pre-generated texture
296  // bind the vao
297  f.vao->bind();
298  // draw
299  f.vao->draw();
300  // now unbind the vao
301  f.vao->unbind();
302  // finally move to the next glyph x position by incrementing
303  // by the width of the char just drawn
304  _x+=f.width;
305 
306  }
307  // finally disable the blend and re-enable depth sort
310 
311 }
312 
313 //---------------------------------------------------------------------------
314 void Text::setScreenSize(int _w, int _h ) noexcept
315 {
316 
317  float scaleX=2.0f/_w;
318  float scaleY=-2.0f/_h;
319  // in shader we do the following code to transform from
320  // x,y to NDC
321  // gl_Position=vec4( ((xpos+inVert.x)*scaleX)-1,((ypos+inVert.y)*scaleY)+1.0,0.0,1.0); "
322  // so all we need to do is calculate the scale above and pass to shader every time the
323  // screen dimensions change
325  (*shader)["nglTextShader"]->use();
326 
327  shader->setRegisteredUniform1f("scaleX",scaleX);
328  shader->setRegisteredUniform1f("scaleY",scaleY);
329 }
330 
331 //---------------------------------------------------------------------------
332 // our text shader uses the alpha of the texture to modulate visibility
333 // when we render the text we use this colour passed to the shader
334 // it is default to black but this will change it
335 // the shader uses the following code
336 // vec4 text=texture(tex,vertUV.st);
337 // fragColour.rgb=textColour.rgb;
338 // fragColour.a=text.a;
339 
340 void Text::setColour(const Colour &_c ) noexcept
341 {
342  // get shader instance
344  // make current shader active
345  (*shader)["nglTextShader"]->use();
346  // set the values
347  shader->setRegisteredUniform3f("textColour",_c.m_r,_c.m_g,_c.m_b);
348 }
349 
350 
351 //---------------------------------------------------------------------------
352 void Text::setColour(Real _r, Real _g, Real _b) noexcept
353 {
354 
356  (*shader)["nglTextShader"]->use();
357 
358  shader->setRegisteredUniform3f("textColour",_r,_g,_b);
359 }
360 
361 void Text::setTransform(float _x, float _y) noexcept
362 {
363 
365  (*shader)["nglTextShader"]->use();
366 
367  shader->setRegisteredUniform2f("transform",_x,_y);
368 }
369 
370 
371 } //end namespace
372 //---------------------------------------------------------------------------
373 
GLAPI void GLAPIENTRY glDeleteTextures(GLsizei n, const GLuint *textures)
#define GL_TEXTURE0
Definition: glew.h:1292
#define GL_RGBA
Definition: glew.h:681
int width
Definition: Text.h:60
simple class to hold colour information and set the basic opengl colour state. also has overloaded me...
Definition: Colour.h:40
GLAPI void GLAPIENTRY glEnable(GLenum cap)
static AbstractVAO * createVAO(const std::string &_type, GLenum _mode=GL_TRIANGLES)
Definition: VAOFactory.cpp:19
GLuint textureID
the width of the font
Definition: Text.h:61
GLAPI void GLAPIENTRY glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
const GLfloat * c
Definition: glew.h:16629
Singleton Class to init and Use GLSL Shaders the class stores the shaders as a map of shader objects ...
Definition: ShaderLib.h:55
#define GL_TRIANGLES
Definition: glew.h:330
a structure to hold the font char texture id and the vao. The vao for each font will be a different s...
Definition: Text.h:58
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition: glew.h:12898
Text(const QFont &_f) noexcept
ctor must pass in a ready constructed QFont make sure the size and emphasis is set before doing this ...
Definition: Text.cpp:53
void setColour(const Colour &_c) noexcept
set the colour of the font from an Colour
Definition: Text.cpp:340
#define GL_UNSIGNED_BYTE
Definition: glew.h:637
#define GL_LINEAR
Definition: glew.h:718
#define GL_ONE_MINUS_SRC_ALPHA
Definition: glew.h:359
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1255
#define GL_DEPTH_TEST
Definition: glew.h:454
void setRegisteredUniform2f(const std::string &_paramName, float _p1, float _p2) noexcept
set the pre-registered uniform
Definition: ShaderLib.cpp:133
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1382
void setScreenSize(int _w, int _h) noexcept
set the size of the screen to scale our font to fit correctly this basically creates the orthographic...
Definition: Text.cpp:314
main shader loader / manager class for GLSL shaders
implementation files for RibExport class
Definition: AABB.cpp:22
void setVertexAttributePointer(GLuint _id, GLint _size, GLenum _type, GLsizei _stride, unsigned int _dataOffset, bool _normalise=false)
set the generic vertex attribute pointer data usually this method will do however the user may occasi...
Definition: AbstractVAO.cpp:36
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition: glew.h:12898
GLAPI void GLAPIENTRY glBlendFunc(GLenum sfactor, GLenum dfactor)
PRECISION Real
create a variable called Real which is the main data type we use (GLfloat for most cases) ...
Definition: Types.h:127
GLAPI void GLAPIENTRY glBindTexture(GLenum target, GLuint texture)
GLuint shader
Definition: glew.h:1816
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s0
Definition: glew.h:12898
void setRegisteredUniform1f(const std::string &_paramName, float _p1) noexcept
set the pre-registered uniform
Definition: ShaderLib.cpp:160
#define glGenerateMipmap
Definition: glew.h:4579
base class for all VAO from the VAOFactory this defines the base class type with simple draw / bind b...
Definition: AbstractVAO.h:34
fmt::BufferedFile & move(fmt::BufferedFile &f)
Definition: posix.h:361
void setTransform(float _x, float _y) noexcept
Definition: Text.cpp:361
const GLdouble * v
Definition: glew.h:1394
void setNumIndices(size_t _s)
the number of indices to draw in the array. It may be that the draw routine can overide this at anoth...
Definition: AbstractVAO.h:91
GLAPI void GLAPIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param)
AbstractVAO * vao
the texture id of the font billboard
Definition: Text.h:62
#define GL_BLEND
Definition: glew.h:485
void unbind()
unbind the VAO by binding default 0
Definition: AbstractVAO.cpp:29
void bind()
bind the VAO so it can be used.
Definition: AbstractVAO.cpp:23
virtual void setData(const VertexData &_data)=0
this method is used to set the data in the VAO, we have a base data type of VertexData above...
Basic text rendering for OpenGL 3.x.
#define GL_SRC_ALPHA
Definition: glew.h:358
#define GL_TEXTURE_MIN_FILTER
Definition: glew.h:724
GLuint index
Definition: glew.h:1817
void renderText(float _x, float _y, const QString &_text) const noexcept
render the text to the screen at _x,_y where 0,0 is top left of the screen all positioning is relativ...
Definition: Text.cpp:265
#define GL_TEXTURE_MAG_FILTER
Definition: glew.h:723
GLint GLint GLint GLint GLint x
Definition: glew.h:1255
static ShaderLib * instance()
Get the instance.
GLAPI void GLAPIENTRY glGenTextures(GLsizei n, GLuint *textures)
void setRegisteredUniform3f(const std::string &_paramName, float _p1, float _p2, float _p3) noexcept
set the pre-registered uniform
Definition: ShaderLib.cpp:120
virtual void draw() const =0
this is the draw method for the VAO the user must implement this per VAO data, usually this will be a...
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition: glew.h:12898
unsigned int nearestPowerOfTwo(unsigned int _num)
code taken from here http://jeffreystedfast.blogspot.com/2008/06/calculating-nearest-power-of-2.html
Definition: Text.cpp:40
const GLdouble * m
Definition: glew.h:9164
#define GL_FLOAT
Definition: glew.h:642
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1255
GLAPI void GLAPIENTRY glDisable(GLenum cap)
~Text()
dtor will clean / remove textures and VAO&#39;s for the class
Definition: Text.cpp:250
#define GL_TEXTURE_2D
Definition: glew.h:10194
#define GL_NEAREST_MIPMAP_LINEAR
Definition: glew.h:721
#define glActiveTexture
Definition: glew.h:1427
GLclampf f
Definition: glew.h:3511