PageRenderTime 62ms CodeModel.GetById 24ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/guilib/GUIFontTTFGL.cpp

http://github.com/xbmc/xbmc
C++ | 455 lines | 331 code | 77 blank | 47 comment | 37 complexity | 2cbbaeced9629aa2044f8fcc9d2c7c45 MD5 | raw file
  1/*
  2 *  Copyright (C) 2005-2018 Team Kodi
  3 *  This file is part of Kodi - https://kodi.tv
  4 *
  5 *  SPDX-License-Identifier: GPL-2.0-or-later
  6 *  See LICENSES/README.md for more information.
  7 */
  8
  9#include "GUIFont.h"
 10#include "GUIFontTTFGL.h"
 11#include "GUIFontManager.h"
 12#include "Texture.h"
 13#include "TextureManager.h"
 14#include "windowing/GraphicContext.h"
 15#include "ServiceBroker.h"
 16#include "gui3d.h"
 17#include "utils/log.h"
 18#include "utils/GLUtils.h"
 19#ifdef HAS_GL
 20#include "rendering/gl/RenderSystemGL.h"
 21#elif HAS_GLES
 22#include "rendering/gles/RenderSystemGLES.h"
 23#endif
 24#include "rendering/MatrixGL.h"
 25
 26#include <cassert>
 27
 28// stuff for freetype
 29#include <ft2build.h>
 30#include FT_FREETYPE_H
 31#include FT_GLYPH_H
 32#include FT_OUTLINE_H
 33
 34#define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000)
 35#define BUFFER_OFFSET(i) ((char *)NULL + (i))
 36
 37CGUIFontTTFGL::CGUIFontTTFGL(const std::string& strFileName)
 38: CGUIFontTTFBase(strFileName)
 39{
 40  m_updateY1 = 0;
 41  m_updateY2 = 0;
 42  m_textureStatus = TEXTURE_VOID;
 43}
 44
 45CGUIFontTTFGL::~CGUIFontTTFGL(void)
 46{
 47  // It's important that all the CGUIFontCacheEntry objects are
 48  // destructed before the CGUIFontTTFGL goes out of scope, because
 49  // our virtual methods won't be accessible after this point
 50  m_dynamicCache.Flush();
 51  DeleteHardwareTexture();
 52}
 53
 54bool CGUIFontTTFGL::FirstBegin()
 55{
 56#if defined(HAS_GL)
 57  GLenum pixformat = GL_RED;
 58  GLenum internalFormat;
 59  unsigned int major, minor;
 60  CRenderSystemGL* renderSystem = dynamic_cast<CRenderSystemGL*>(CServiceBroker::GetRenderSystem());
 61  renderSystem->GetRenderVersion(major, minor);
 62  if (major >= 3)
 63    internalFormat = GL_R8;
 64  else
 65    internalFormat = GL_LUMINANCE;
 66#else
 67  GLenum pixformat = GL_ALPHA; // deprecated
 68  GLenum internalFormat = GL_ALPHA;
 69#endif
 70
 71  if (m_textureStatus == TEXTURE_REALLOCATED)
 72  {
 73    if (glIsTexture(m_nTexture))
 74      CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture);
 75    m_textureStatus = TEXTURE_VOID;
 76  }
 77
 78  if (m_textureStatus == TEXTURE_VOID)
 79  {
 80    // Have OpenGL generate a texture object handle for us
 81    glGenTextures(1, (GLuint*) &m_nTexture);
 82
 83    // Bind the texture object
 84    glBindTexture(GL_TEXTURE_2D, m_nTexture);
 85
 86    // Set the texture's stretching properties
 87    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 88    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 89
 90    // Set the texture image -- THIS WORKS, so the pixels must be wrong.
 91    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_texture->GetWidth(), m_texture->GetHeight(), 0,
 92        pixformat, GL_UNSIGNED_BYTE, 0);
 93
 94    VerifyGLState();
 95    m_textureStatus = TEXTURE_UPDATED;
 96  }
 97
 98  if (m_textureStatus == TEXTURE_UPDATED)
 99  {
100    glBindTexture(GL_TEXTURE_2D, m_nTexture);
101    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_updateY1, m_texture->GetWidth(), m_updateY2 - m_updateY1, pixformat, GL_UNSIGNED_BYTE,
102        m_texture->GetPixels() + m_updateY1 * m_texture->GetPitch());
103
104    m_updateY1 = m_updateY2 = 0;
105    m_textureStatus = TEXTURE_READY;
106  }
107
108  // Turn Blending On
109  glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
110  glEnable(GL_BLEND);
111  glActiveTexture(GL_TEXTURE0);
112  glBindTexture(GL_TEXTURE_2D, m_nTexture);
113  return true;
114}
115
116void CGUIFontTTFGL::LastEnd()
117{
118#ifdef HAS_GL
119  CRenderSystemGL* renderSystem = dynamic_cast<CRenderSystemGL*>(CServiceBroker::GetRenderSystem());
120  renderSystem->EnableShader(SM_FONTS);
121
122  GLint posLoc = renderSystem->ShaderGetPos();
123  GLint colLoc = renderSystem->ShaderGetCol();
124  GLint tex0Loc = renderSystem->ShaderGetCoord0();
125  GLint modelLoc = renderSystem->ShaderGetModel();
126
127  CreateStaticVertexBuffers();
128
129  // Enable the attributes used by this shader
130  glEnableVertexAttribArray(posLoc);
131  glEnableVertexAttribArray(colLoc);
132  glEnableVertexAttribArray(tex0Loc);
133
134  if (!m_vertex.empty())
135  {
136
137    // Deal with vertices that had to use software clipping
138    std::vector<SVertex> vecVertices(6 * (m_vertex.size() / 4));
139    SVertex *vertices = &vecVertices[0];
140    for (size_t i=0; i<m_vertex.size(); i+=4)
141    {
142      *vertices++ = m_vertex[i];
143      *vertices++ = m_vertex[i+1];
144      *vertices++ = m_vertex[i+2];
145
146      *vertices++ = m_vertex[i+1];
147      *vertices++ = m_vertex[i+3];
148      *vertices++ = m_vertex[i+2];
149    }
150    vertices = &vecVertices[0];
151
152    GLuint VertexVBO;
153
154    glGenBuffers(1, &VertexVBO);
155    glBindBuffer(GL_ARRAY_BUFFER, VertexVBO);
156    glBufferData(GL_ARRAY_BUFFER, sizeof(SVertex)*vecVertices.size(), &vecVertices[0], GL_STATIC_DRAW);
157
158    glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, x)));
159    glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, r)));
160    glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, u)));
161
162    glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
163
164    glBindBuffer(GL_ARRAY_BUFFER, 0);
165    glDeleteBuffers(1, &VertexVBO);
166  }
167
168#else
169  // GLES 2.0 version.
170  CRenderSystemGLES* renderSystem = dynamic_cast<CRenderSystemGLES*>(CServiceBroker::GetRenderSystem());
171  renderSystem->EnableGUIShader(SM_FONTS);
172
173  GLint posLoc  = renderSystem->GUIShaderGetPos();
174  GLint colLoc  = renderSystem->GUIShaderGetCol();
175  GLint tex0Loc = renderSystem->GUIShaderGetCoord0();
176  GLint modelLoc = renderSystem->GUIShaderGetModel();
177
178
179  CreateStaticVertexBuffers();
180
181  // Enable the attributes used by this shader
182  glEnableVertexAttribArray(posLoc);
183  glEnableVertexAttribArray(colLoc);
184  glEnableVertexAttribArray(tex0Loc);
185
186  if (!m_vertex.empty())
187  {
188    // Deal with vertices that had to use software clipping
189    std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
190    SVertex *vertices = &vecVertices[0];
191
192    for (size_t i=0; i<m_vertex.size(); i+=4)
193    {
194      *vertices++ = m_vertex[i];
195      *vertices++ = m_vertex[i+1];
196      *vertices++ = m_vertex[i+2];
197
198      *vertices++ = m_vertex[i+1];
199      *vertices++ = m_vertex[i+3];
200      *vertices++ = m_vertex[i+2];
201    }
202
203    vertices = &vecVertices[0];
204
205    glVertexAttribPointer(posLoc,  3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
206    glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
207    glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,  GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
208
209    glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
210  }
211#endif
212
213  if (!m_vertexTrans.empty())
214  {
215    // Deal with the vertices that can be hardware clipped and therefore translated
216
217    // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
218    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
219    // Store current scissor
220    CRect scissor = CServiceBroker::GetWinSystem()->GetGfxContext().StereoCorrection(CServiceBroker::GetWinSystem()->GetGfxContext().GetScissors());
221
222    for (size_t i = 0; i < m_vertexTrans.size(); i++)
223    {
224      if (m_vertexTrans[i].vertexBuffer->bufferHandle == 0)
225      {
226        continue;
227      }
228
229      // Apply the clip rectangle
230      CRect clip = renderSystem->ClipRectToScissorRect(m_vertexTrans[i].clip);
231      if (!clip.IsEmpty())
232      {
233        // intersect with current scissor
234        clip.Intersect(scissor);
235        // skip empty clip
236        if (clip.IsEmpty())
237          continue;
238        renderSystem->SetScissors(clip);
239      }
240
241      // Apply the translation to the currently active (top-of-stack) model view matrix
242      glMatrixModview.Push();
243      glMatrixModview.Get().Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
244      glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glMatrixModview.Get());
245
246      // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
247      glBindBuffer(GL_ARRAY_BUFFER, m_vertexTrans[i].vertexBuffer->bufferHandle);
248
249      // Do the actual drawing operation, split into groups of characters no
250      // larger than the pre-determined size of the element array
251      for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
252      {
253        size_t count = m_vertexTrans[i].vertexBuffer->size - character;
254        count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
255
256        // Set up the offsets of the various vertex attributes within the buffer
257        // object bound to GL_ARRAY_BUFFER
258        glVertexAttribPointer(posLoc,  3, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x)));
259        glVertexAttribPointer(colLoc,  4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r)));
260        glVertexAttribPointer(tex0Loc, 2, GL_FLOAT,         GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u)));
261
262        glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0);
263      }
264
265      glMatrixModview.Pop();
266    }
267    // Restore the original scissor rectangle
268    renderSystem->SetScissors(scissor);
269    // Restore the original model view matrix
270    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glMatrixModview.Get());
271    // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
272    glBindBuffer(GL_ARRAY_BUFFER, 0);
273    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
274  }
275
276  // Disable the attributes used by this shader
277  glDisableVertexAttribArray(posLoc);
278  glDisableVertexAttribArray(colLoc);
279  glDisableVertexAttribArray(tex0Loc);
280
281#ifdef HAS_GL
282  renderSystem->DisableShader();
283#else
284  renderSystem->DisableGUIShader();
285#endif
286}
287
288CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
289{
290  assert(vertices.size() % 4 == 0);
291  GLuint bufferHandle = 0;
292
293  // Do not create empty buffers, leave buffer as 0, it will be ignored in drawing stage
294  if (!vertices.empty())
295  {
296    // Generate a unique buffer object name and put it in bufferHandle
297    glGenBuffers(1, &bufferHandle);
298    // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
299    glBindBuffer(GL_ARRAY_BUFFER, bufferHandle);
300    // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
301    // binding point (i.e. our buffer object) and initialise it from the
302    // specified client-side pointer
303    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(SVertex), vertices.data(), GL_STATIC_DRAW);
304    // Unbind GL_ARRAY_BUFFER
305    glBindBuffer(GL_ARRAY_BUFFER, 0);
306  }
307
308  return CVertexBuffer(bufferHandle, vertices.size() / 4, this);
309}
310
311void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const
312{
313  if (buffer.bufferHandle != 0)
314  {
315    // Release the buffer name for reuse
316    glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle);
317    buffer.bufferHandle = 0;
318  }
319}
320
321CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
322{
323  newHeight = CBaseTexture::PadPow2(newHeight);
324
325  CBaseTexture* newTexture = new CTexture(m_textureWidth, newHeight, XB_FMT_A8);
326
327  if (!newTexture || newTexture->GetPixels() == NULL)
328  {
329    CLog::Log(LOGERROR, "GUIFontTTFGL::CacheCharacter: Error creating new cache texture for size %f", m_height);
330    delete newTexture;
331    return NULL;
332  }
333  m_textureHeight = newTexture->GetHeight();
334  m_textureScaleY = 1.0f / m_textureHeight;
335  m_textureWidth = newTexture->GetWidth();
336  m_textureScaleX = 1.0f / m_textureWidth;
337  if (m_textureHeight < newHeight)
338    CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
339  m_staticCache.Flush();
340  m_dynamicCache.Flush();
341
342  memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
343  if (m_texture)
344  {
345    m_updateY1 = 0;
346    m_updateY2 = m_texture->GetHeight();
347
348    unsigned char* src = m_texture->GetPixels();
349    unsigned char* dst = newTexture->GetPixels();
350    for (unsigned int y = 0; y < m_texture->GetHeight(); y++)
351    {
352      memcpy(dst, src, m_texture->GetPitch());
353      src += m_texture->GetPitch();
354      dst += newTexture->GetPitch();
355    }
356    delete m_texture;
357  }
358
359  m_textureStatus = TEXTURE_REALLOCATED;
360
361  return newTexture;
362}
363
364bool CGUIFontTTFGL::CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
365{
366  FT_Bitmap bitmap = bitGlyph->bitmap;
367
368  unsigned char* source = bitmap.buffer;
369  unsigned char* target = m_texture->GetPixels() + y1 * m_texture->GetPitch() + x1;
370
371  for (unsigned int y = y1; y < y2; y++)
372  {
373    memcpy(target, source, x2-x1);
374    source += bitmap.width;
375    target += m_texture->GetPitch();
376  }
377
378  switch (m_textureStatus)
379  {
380  case TEXTURE_UPDATED:
381    {
382      m_updateY1 = std::min(m_updateY1, y1);
383      m_updateY2 = std::max(m_updateY2, y2);
384    }
385    break;
386
387  case TEXTURE_READY:
388    {
389      m_updateY1 = y1;
390      m_updateY2 = y2;
391      m_textureStatus = TEXTURE_UPDATED;
392    }
393    break;
394
395  case TEXTURE_REALLOCATED:
396    {
397      m_updateY2 = std::max(m_updateY2, y2);
398    }
399    break;
400
401  case TEXTURE_VOID:
402  default:
403    break;
404  }
405
406  return true;
407}
408
409void CGUIFontTTFGL::DeleteHardwareTexture()
410{
411  if (m_textureStatus != TEXTURE_VOID)
412  {
413    if (glIsTexture(m_nTexture))
414      CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture);
415
416    m_textureStatus = TEXTURE_VOID;
417    m_updateY1 = m_updateY2 = 0;
418  }
419}
420
421void CGUIFontTTFGL::CreateStaticVertexBuffers(void)
422{
423  if (m_staticVertexBufferCreated)
424    return;
425
426  // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
427  glGenBuffers(1, &m_elementArrayHandle);
428  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
429  // Create an array holding the mesh indices to convert quads to triangles
430  GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
431  for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
432  {
433    index[i][0] = 4*i;
434    index[i][1] = 4*i+1;
435    index[i][2] = 4*i+2;
436    index[i][3] = 4*i+1;
437    index[i][4] = 4*i+3;
438    index[i][5] = 4*i+2;
439  }
440  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW);
441  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
442  m_staticVertexBufferCreated = true;
443}
444
445void CGUIFontTTFGL::DestroyStaticVertexBuffers(void)
446{
447  if (!m_staticVertexBufferCreated)
448    return;
449  glDeleteBuffers(1, &m_elementArrayHandle);
450  m_staticVertexBufferCreated = false;
451}
452
453GLuint CGUIFontTTFGL::m_elementArrayHandle;
454bool CGUIFontTTFGL::m_staticVertexBufferCreated;
455