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