2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIFontTTFGLES.h"
12 #include "GUIFontManager.h"
13 #include "ServiceBroker.h"
15 #include "TextureManager.h"
17 #include "rendering/MatrixGL.h"
18 #include "rendering/gles/RenderSystemGLES.h"
19 #include "utils/GLUtils.h"
20 #include "utils/log.h"
21 #include "windowing/GraphicContext.h"
28 #include FT_FREETYPE_H
34 constexpr size_t ELEMENT_ARRAY_MAX_CHAR_INDEX
= 1000;
37 CGUIFontTTF
* CGUIFontTTF::CreateGUIFontTTF(const std::string
& fontIdent
)
39 return new CGUIFontTTFGLES(fontIdent
);
42 CGUIFontTTFGLES::CGUIFontTTFGLES(const std::string
& fontIdent
) : CGUIFontTTF(fontIdent
)
46 CGUIFontTTFGLES::~CGUIFontTTFGLES(void)
48 // It's important that all the CGUIFontCacheEntry objects are
49 // destructed before the CGUIFontTTFGLES goes out of scope, because
50 // our virtual methods won't be accessible after this point
51 m_dynamicCache
.Flush();
52 DeleteHardwareTexture();
55 bool CGUIFontTTFGLES::FirstBegin()
57 CRenderSystemGLES
* renderSystem
=
58 dynamic_cast<CRenderSystemGLES
*>(CServiceBroker::GetRenderSystem());
59 renderSystem
->EnableGUIShader(ShaderMethodGLES::SM_FONTS
);
60 GLenum pixformat
= GL_ALPHA
; // deprecated
61 GLenum internalFormat
= GL_ALPHA
;
63 if (m_textureStatus
== TEXTURE_REALLOCATED
)
65 if (glIsTexture(m_nTexture
))
66 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
67 m_textureStatus
= TEXTURE_VOID
;
70 if (m_textureStatus
== TEXTURE_VOID
)
72 // Have OpenGL generate a texture object handle for us
73 glGenTextures(1, static_cast<GLuint
*>(&m_nTexture
));
75 // Bind the texture object
76 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
78 // Set the texture's stretching properties
79 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
80 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
82 // Set the texture image -- THIS WORKS, so the pixels must be wrong.
83 glTexImage2D(GL_TEXTURE_2D
, 0, internalFormat
, m_texture
->GetWidth(), m_texture
->GetHeight(), 0,
84 pixformat
, GL_UNSIGNED_BYTE
, 0);
87 m_textureStatus
= TEXTURE_UPDATED
;
90 if (m_textureStatus
== TEXTURE_UPDATED
)
92 // Copies one more line in case we have to sample from there
93 m_updateY2
= std::min(m_updateY2
+ 1, m_texture
->GetHeight());
95 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
96 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, m_updateY1
, m_texture
->GetWidth(), m_updateY2
- m_updateY1
,
97 pixformat
, GL_UNSIGNED_BYTE
,
98 m_texture
->GetPixels() + m_updateY1
* m_texture
->GetPitch());
100 m_updateY1
= m_updateY2
= 0;
101 m_textureStatus
= TEXTURE_READY
;
105 glBlendFuncSeparate(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
, GL_ONE_MINUS_DST_ALPHA
, GL_ONE
);
107 glActiveTexture(GL_TEXTURE0
);
108 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
113 void CGUIFontTTFGLES::LastEnd()
115 CWinSystemBase
* const winSystem
= CServiceBroker::GetWinSystem();
119 CRenderSystemGLES
* renderSystem
=
120 dynamic_cast<CRenderSystemGLES
*>(CServiceBroker::GetRenderSystem());
122 GLint posLoc
= renderSystem
->GUIShaderGetPos();
123 GLint colLoc
= renderSystem
->GUIShaderGetCol();
124 GLint tex0Loc
= renderSystem
->GUIShaderGetCoord0();
125 GLint modelLoc
= renderSystem
->GUIShaderGetModel();
127 CreateStaticVertexBuffers();
129 // Enable the attributes used by this shader
130 glEnableVertexAttribArray(posLoc
);
131 glEnableVertexAttribArray(colLoc
);
132 glEnableVertexAttribArray(tex0Loc
);
134 if (!m_vertex
.empty())
136 // Deal with vertices that had to use software clipping
137 std::vector
<SVertex
> vecVertices(6 * (m_vertex
.size() / 4));
138 SVertex
* vertices
= &vecVertices
[0];
140 for (size_t i
= 0; i
< m_vertex
.size(); i
+= 4)
142 *vertices
++ = m_vertex
[i
];
143 *vertices
++ = m_vertex
[i
+ 1];
144 *vertices
++ = m_vertex
[i
+ 2];
146 *vertices
++ = m_vertex
[i
+ 1];
147 *vertices
++ = m_vertex
[i
+ 3];
148 *vertices
++ = m_vertex
[i
+ 2];
151 vertices
= &vecVertices
[0];
153 glVertexAttribPointer(posLoc
, 3, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
154 reinterpret_cast<char*>(vertices
) + offsetof(SVertex
, x
));
155 glVertexAttribPointer(colLoc
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof(SVertex
),
156 reinterpret_cast<char*>(vertices
) + offsetof(SVertex
, r
));
157 glVertexAttribPointer(tex0Loc
, 2, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
158 reinterpret_cast<char*>(vertices
) + offsetof(SVertex
, u
));
160 glDrawArrays(GL_TRIANGLES
, 0, vecVertices
.size());
163 if (!m_vertexTrans
.empty())
165 // Deal with the vertices that can be hardware clipped and therefore translated
167 // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
168 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, m_elementArrayHandle
);
169 // Store current scissor
170 CGraphicContext
& context
= winSystem
->GetGfxContext();
171 CRect scissor
= context
.StereoCorrection(context
.GetScissors());
173 for (size_t i
= 0; i
< m_vertexTrans
.size(); i
++)
175 if (m_vertexTrans
[i
].m_vertexBuffer
->bufferHandle
== 0)
180 // Apply the clip rectangle
181 CRect clip
= renderSystem
->ClipRectToScissorRect(m_vertexTrans
[i
].m_clip
);
184 // intersect with current scissor
185 clip
.Intersect(scissor
);
189 renderSystem
->SetScissors(clip
);
192 // Apply the translation to the currently active (top-of-stack) model view matrix
193 glMatrixModview
.Push();
194 glMatrixModview
.Get().Translatef(m_vertexTrans
[i
].m_translateX
, m_vertexTrans
[i
].m_translateY
,
195 m_vertexTrans
[i
].m_translateZ
);
196 glUniformMatrix4fv(modelLoc
, 1, GL_FALSE
, glMatrixModview
.Get());
198 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
199 glBindBuffer(GL_ARRAY_BUFFER
, m_vertexTrans
[i
].m_vertexBuffer
->bufferHandle
);
201 // Do the actual drawing operation, split into groups of characters no
202 // larger than the pre-determined size of the element array
203 for (size_t character
= 0; m_vertexTrans
[i
].m_vertexBuffer
->size
> character
;
204 character
+= ELEMENT_ARRAY_MAX_CHAR_INDEX
)
206 size_t count
= m_vertexTrans
[i
].m_vertexBuffer
->size
- character
;
207 count
= std::min
<size_t>(count
, ELEMENT_ARRAY_MAX_CHAR_INDEX
);
209 // Set up the offsets of the various vertex attributes within the buffer
210 // object bound to GL_ARRAY_BUFFER
211 glVertexAttribPointer(
212 posLoc
, 3, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
213 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, x
)));
214 glVertexAttribPointer(
215 colLoc
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof(SVertex
),
216 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, r
)));
217 glVertexAttribPointer(
218 tex0Loc
, 2, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
219 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, u
)));
221 glDrawElements(GL_TRIANGLES
, 6 * count
, GL_UNSIGNED_SHORT
, 0);
224 glMatrixModview
.Pop();
226 // Restore the original scissor rectangle
227 renderSystem
->SetScissors(scissor
);
228 // Restore the original model view matrix
229 glUniformMatrix4fv(modelLoc
, 1, GL_FALSE
, glMatrixModview
.Get());
230 // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
231 glBindBuffer(GL_ARRAY_BUFFER
, 0);
232 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
235 // Disable the attributes used by this shader
236 glDisableVertexAttribArray(posLoc
);
237 glDisableVertexAttribArray(colLoc
);
238 glDisableVertexAttribArray(tex0Loc
);
240 renderSystem
->DisableGUIShader();
243 CVertexBuffer
CGUIFontTTFGLES::CreateVertexBuffer(const std::vector
<SVertex
>& vertices
) const
245 assert(vertices
.size() % 4 == 0);
246 GLuint bufferHandle
= 0;
248 // Do not create empty buffers, leave buffer as 0, it will be ignored in drawing stage
249 if (!vertices
.empty())
251 // Generate a unique buffer object name and put it in bufferHandle
252 glGenBuffers(1, &bufferHandle
);
253 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
254 glBindBuffer(GL_ARRAY_BUFFER
, bufferHandle
);
255 // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
256 // binding point (i.e. our buffer object) and initialise it from the
257 // specified client-side pointer
258 glBufferData(GL_ARRAY_BUFFER
, vertices
.size() * sizeof(SVertex
), vertices
.data(),
260 // Unbind GL_ARRAY_BUFFER
261 glBindBuffer(GL_ARRAY_BUFFER
, 0);
264 return CVertexBuffer(bufferHandle
, vertices
.size() / 4, this);
267 void CGUIFontTTFGLES::DestroyVertexBuffer(CVertexBuffer
& buffer
) const
269 if (buffer
.bufferHandle
!= 0)
271 // Release the buffer name for reuse
272 glDeleteBuffers(1, static_cast<GLuint
*>(&buffer
.bufferHandle
));
273 buffer
.bufferHandle
= 0;
277 std::unique_ptr
<CTexture
> CGUIFontTTFGLES::ReallocTexture(unsigned int& newHeight
)
279 newHeight
= CTexture::PadPow2(newHeight
);
281 std::unique_ptr
<CTexture
> newTexture
=
282 CTexture::CreateTexture(m_textureWidth
, newHeight
, XB_FMT_A8
);
284 if (!newTexture
|| !newTexture
->GetPixels())
286 CLog::Log(LOGERROR
, "GUIFontTTFGLES::{}: Error creating new cache texture for size {:f}",
291 m_textureHeight
= newTexture
->GetHeight();
292 m_textureScaleY
= 1.0f
/ m_textureHeight
;
293 m_textureWidth
= newTexture
->GetWidth();
294 m_textureScaleX
= 1.0f
/ m_textureWidth
;
295 if (m_textureHeight
< newHeight
)
296 CLog::Log(LOGWARNING
,
297 "GUIFontTTFGLES::{}: allocated new texture with height of {}, requested {}", __func__
,
298 m_textureHeight
, newHeight
);
299 m_staticCache
.Flush();
300 m_dynamicCache
.Flush();
302 memset(newTexture
->GetPixels(), 0, m_textureHeight
* newTexture
->GetPitch());
306 m_updateY2
= m_texture
->GetHeight();
308 unsigned char* src
= m_texture
->GetPixels();
309 unsigned char* dst
= newTexture
->GetPixels();
310 for (unsigned int y
= 0; y
< m_texture
->GetHeight(); y
++)
312 memcpy(dst
, src
, m_texture
->GetPitch());
313 src
+= m_texture
->GetPitch();
314 dst
+= newTexture
->GetPitch();
318 m_textureStatus
= TEXTURE_REALLOCATED
;
323 bool CGUIFontTTFGLES::CopyCharToTexture(
324 FT_BitmapGlyph bitGlyph
, unsigned int x1
, unsigned int y1
, unsigned int x2
, unsigned int y2
)
326 FT_Bitmap bitmap
= bitGlyph
->bitmap
;
328 unsigned char* source
= bitmap
.buffer
;
329 unsigned char* target
= m_texture
->GetPixels() + y1
* m_texture
->GetPitch() + x1
;
331 for (unsigned int y
= y1
; y
< y2
; y
++)
333 memcpy(target
, source
, x2
- x1
);
334 source
+= bitmap
.width
;
335 target
+= m_texture
->GetPitch();
338 switch (m_textureStatus
)
340 case TEXTURE_UPDATED
:
342 m_updateY1
= std::min(m_updateY1
, y1
);
343 m_updateY2
= std::max(m_updateY2
, y2
);
351 m_textureStatus
= TEXTURE_UPDATED
;
355 case TEXTURE_REALLOCATED
:
357 m_updateY2
= std::max(m_updateY2
, y2
);
369 void CGUIFontTTFGLES::DeleteHardwareTexture()
371 if (m_textureStatus
!= TEXTURE_VOID
)
373 if (glIsTexture(m_nTexture
))
374 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
376 m_textureStatus
= TEXTURE_VOID
;
377 m_updateY1
= m_updateY2
= 0;
381 void CGUIFontTTFGLES::CreateStaticVertexBuffers(void)
383 if (m_staticVertexBufferCreated
)
386 // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
387 glGenBuffers(1, &m_elementArrayHandle
);
388 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, m_elementArrayHandle
);
390 // Create an array holding the mesh indices to convert quads to triangles
391 GLushort index
[ELEMENT_ARRAY_MAX_CHAR_INDEX
][6];
392 for (size_t i
= 0; i
< ELEMENT_ARRAY_MAX_CHAR_INDEX
; i
++)
395 index
[i
][1] = 4 * i
+ 1;
396 index
[i
][2] = 4 * i
+ 2;
397 index
[i
][3] = 4 * i
+ 1;
398 index
[i
][4] = 4 * i
+ 3;
399 index
[i
][5] = 4 * i
+ 2;
402 glBufferData(GL_ELEMENT_ARRAY_BUFFER
, sizeof index
, index
, GL_STATIC_DRAW
);
403 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
404 m_staticVertexBufferCreated
= true;
407 void CGUIFontTTFGLES::DestroyStaticVertexBuffers(void)
409 if (!m_staticVertexBufferCreated
)
412 glDeleteBuffers(1, &m_elementArrayHandle
);
413 m_staticVertexBufferCreated
= false;
416 GLuint
CGUIFontTTFGLES::m_elementArrayHandle
{0};
417 bool CGUIFontTTFGLES::m_staticVertexBufferCreated
{false};