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