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 "settings/AdvancedSettings.h"
20 #include "settings/SettingsComponent.h"
21 #include "utils/GLUtils.h"
22 #include "utils/log.h"
23 #include "windowing/GraphicContext.h"
30 #include FT_FREETYPE_H
36 constexpr size_t ELEMENT_ARRAY_MAX_CHAR_INDEX
= 1000;
39 CGUIFontTTF
* CGUIFontTTF::CreateGUIFontTTF(const std::string
& fontIdent
)
41 return new CGUIFontTTFGLES(fontIdent
);
44 CGUIFontTTFGLES::CGUIFontTTFGLES(const std::string
& fontIdent
) : CGUIFontTTF(fontIdent
)
48 CGUIFontTTFGLES::~CGUIFontTTFGLES(void)
50 // It's important that all the CGUIFontCacheEntry objects are
51 // destructed before the CGUIFontTTFGLES goes out of scope, because
52 // our virtual methods won't be accessible after this point
53 m_dynamicCache
.Flush();
54 DeleteHardwareTexture();
57 bool CGUIFontTTFGLES::FirstBegin()
59 CRenderSystemGLES
* renderSystem
=
60 dynamic_cast<CRenderSystemGLES
*>(CServiceBroker::GetRenderSystem());
61 renderSystem
->EnableGUIShader(ShaderMethodGLES::SM_FONTS
);
62 GLenum pixformat
= GL_ALPHA
; // deprecated
63 GLenum internalFormat
= GL_ALPHA
;
65 if (renderSystem
->ScissorsCanEffectClipping())
71 m_scissorClip
= false;
72 renderSystem
->ResetScissors();
73 renderSystem
->EnableGUIShader(ShaderMethodGLES::SM_FONTS_SHADER_CLIP
);
76 if (m_textureStatus
== TEXTURE_REALLOCATED
)
78 if (glIsTexture(m_nTexture
))
79 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
80 m_textureStatus
= TEXTURE_VOID
;
83 if (m_textureStatus
== TEXTURE_VOID
)
85 // Have OpenGL generate a texture object handle for us
86 glGenTextures(1, static_cast<GLuint
*>(&m_nTexture
));
88 // Bind the texture object
89 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
91 // Set the texture's stretching properties
92 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
93 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
95 // Set the texture image -- THIS WORKS, so the pixels must be wrong.
96 glTexImage2D(GL_TEXTURE_2D
, 0, internalFormat
, m_texture
->GetWidth(), m_texture
->GetHeight(), 0,
97 pixformat
, GL_UNSIGNED_BYTE
, 0);
99 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
100 if (CServiceBroker::GetRenderSystem()->IsExtSupported("GL_EXT_texture_filter_anisotropic"))
103 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiAnisotropicFiltering
;
105 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAX_ANISOTROPY_EXT
, aniso
);
110 m_textureStatus
= TEXTURE_UPDATED
;
113 if (m_textureStatus
== TEXTURE_UPDATED
)
115 // Copies one more line in case we have to sample from there
116 m_updateY2
= std::min(m_updateY2
+ 1, m_texture
->GetHeight());
118 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
119 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, m_updateY1
, m_texture
->GetWidth(), m_updateY2
- m_updateY1
,
120 pixformat
, GL_UNSIGNED_BYTE
,
121 m_texture
->GetPixels() + m_updateY1
* m_texture
->GetPitch());
123 m_updateY1
= m_updateY2
= 0;
124 m_textureStatus
= TEXTURE_READY
;
128 glBlendFuncSeparate(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
, GL_ONE_MINUS_DST_ALPHA
, GL_ONE
);
130 glActiveTexture(GL_TEXTURE0
);
131 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
136 void CGUIFontTTFGLES::LastEnd()
138 // static vertex arrays are not supported anymore
139 assert(m_vertex
.empty());
141 CWinSystemBase
* const winSystem
= CServiceBroker::GetWinSystem();
145 CRenderSystemGLES
* renderSystem
=
146 dynamic_cast<CRenderSystemGLES
*>(CServiceBroker::GetRenderSystem());
148 GLint posLoc
= renderSystem
->GUIShaderGetPos();
149 GLint colLoc
= renderSystem
->GUIShaderGetCol();
150 GLint tex0Loc
= renderSystem
->GUIShaderGetCoord0();
151 GLint clipUniformLoc
= renderSystem
->GUIShaderGetClip();
152 GLint coordStepUniformLoc
= renderSystem
->GUIShaderGetCoordStep();
153 GLint matrixUniformLoc
= renderSystem
->GUIShaderGetMatrix();
154 GLint depthLoc
= renderSystem
->GUIShaderGetDepth();
156 CreateStaticVertexBuffers();
158 // Enable the attributes used by this shader
159 glEnableVertexAttribArray(posLoc
);
160 glEnableVertexAttribArray(colLoc
);
161 glEnableVertexAttribArray(tex0Loc
);
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
);
192 // clip using scissors
193 renderSystem
->SetScissors(clip
);
197 // clip using vertex shader
198 renderSystem
->ResetScissors();
201 m_vertexTrans
[i
].m_clip
.x1
- m_vertexTrans
[i
].m_translateX
- m_vertexTrans
[i
].m_offsetX
;
203 m_vertexTrans
[i
].m_clip
.y1
- m_vertexTrans
[i
].m_translateY
- m_vertexTrans
[i
].m_offsetY
;
205 m_vertexTrans
[i
].m_clip
.x2
- m_vertexTrans
[i
].m_translateX
- m_vertexTrans
[i
].m_offsetX
;
207 m_vertexTrans
[i
].m_clip
.y2
- m_vertexTrans
[i
].m_translateY
- m_vertexTrans
[i
].m_offsetY
;
209 glUniform4f(clipUniformLoc
, x1
, y1
, x2
, y2
);
211 // setup texture step
212 float stepX
= context
.GetGUIScaleX() / (static_cast<float>(m_textureWidth
));
213 float stepY
= context
.GetGUIScaleY() / (static_cast<float>(m_textureHeight
));
214 glUniform4f(coordStepUniformLoc
, stepX
, stepY
, 1.0f
, 1.0f
);
217 // calculate the fractional offset to the ideal position
219 context
.ScaleFinalXCoord(m_vertexTrans
[i
].m_translateX
, m_vertexTrans
[i
].m_translateY
);
221 context
.ScaleFinalYCoord(m_vertexTrans
[i
].m_translateX
, m_vertexTrans
[i
].m_translateY
);
222 fractX
= -fractX
+ std::round(fractX
);
223 fractY
= -fractY
+ std::round(fractY
);
225 // proj * model * gui * scroll * translation * scaling * correction factor
226 CMatrixGL matrix
= glMatrixProject
.Get();
227 matrix
.MultMatrixf(glMatrixModview
.Get());
228 matrix
.MultMatrixf(CMatrixGL(context
.GetGUIMatrix()));
229 matrix
.Translatef(m_vertexTrans
[i
].m_offsetX
, m_vertexTrans
[i
].m_offsetY
, 0.0f
);
230 matrix
.Translatef(m_vertexTrans
[i
].m_translateX
, m_vertexTrans
[i
].m_translateY
, 0.0f
);
231 // the gui matrix messes with the scale. correct it here for now.
232 matrix
.Scalef(context
.GetGUIScaleX(), context
.GetGUIScaleY(), 1.0f
);
233 // the gui matrix doesn't align to exact pixel coords atm. correct it here for now.
234 matrix
.Translatef(fractX
, fractY
, 0.0f
);
236 // Apply the depth value of the layer
237 float depth
= CServiceBroker::GetWinSystem()->GetGfxContext().GetTransformDepth();
238 glUniform1f(depthLoc
, depth
);
240 glUniformMatrix4fv(matrixUniformLoc
, 1, GL_FALSE
, matrix
);
242 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
243 glBindBuffer(GL_ARRAY_BUFFER
, m_vertexTrans
[i
].m_vertexBuffer
->bufferHandle
);
245 // Do the actual drawing operation, split into groups of characters no
246 // larger than the pre-determined size of the element array
247 for (size_t character
= 0; m_vertexTrans
[i
].m_vertexBuffer
->size
> character
;
248 character
+= ELEMENT_ARRAY_MAX_CHAR_INDEX
)
250 size_t count
= m_vertexTrans
[i
].m_vertexBuffer
->size
- character
;
251 count
= std::min
<size_t>(count
, ELEMENT_ARRAY_MAX_CHAR_INDEX
);
253 // Set up the offsets of the various vertex attributes within the buffer
254 // object bound to GL_ARRAY_BUFFER
255 glVertexAttribPointer(
256 posLoc
, 3, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
257 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, x
)));
258 glVertexAttribPointer(
259 colLoc
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof(SVertex
),
260 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, r
)));
261 glVertexAttribPointer(
262 tex0Loc
, 2, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
263 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, u
)));
265 glDrawElements(GL_TRIANGLES
, 6 * count
, GL_UNSIGNED_SHORT
, 0);
268 glMatrixModview
.Pop();
270 // Restore the original scissor rectangle
272 renderSystem
->SetScissors(scissor
);
273 // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
274 glBindBuffer(GL_ARRAY_BUFFER
, 0);
275 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
278 // Disable the attributes used by this shader
279 glDisableVertexAttribArray(posLoc
);
280 glDisableVertexAttribArray(colLoc
);
281 glDisableVertexAttribArray(tex0Loc
);
283 renderSystem
->DisableGUIShader();
286 CVertexBuffer
CGUIFontTTFGLES::CreateVertexBuffer(const std::vector
<SVertex
>& vertices
) const
288 assert(vertices
.size() % 4 == 0);
289 GLuint bufferHandle
= 0;
291 // Do not create empty buffers, leave buffer as 0, it will be ignored in drawing stage
292 if (!vertices
.empty())
294 // Generate a unique buffer object name and put it in bufferHandle
295 glGenBuffers(1, &bufferHandle
);
296 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
297 glBindBuffer(GL_ARRAY_BUFFER
, bufferHandle
);
298 // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
299 // binding point (i.e. our buffer object) and initialise it from the
300 // specified client-side pointer
301 glBufferData(GL_ARRAY_BUFFER
, vertices
.size() * sizeof(SVertex
), vertices
.data(),
303 // Unbind GL_ARRAY_BUFFER
304 glBindBuffer(GL_ARRAY_BUFFER
, 0);
307 return CVertexBuffer(bufferHandle
, vertices
.size() / 4, this);
310 void CGUIFontTTFGLES::DestroyVertexBuffer(CVertexBuffer
& buffer
) const
312 if (buffer
.bufferHandle
!= 0)
314 // Release the buffer name for reuse
315 glDeleteBuffers(1, static_cast<GLuint
*>(&buffer
.bufferHandle
));
316 buffer
.bufferHandle
= 0;
320 std::unique_ptr
<CTexture
> CGUIFontTTFGLES::ReallocTexture(unsigned int& newHeight
)
322 newHeight
= CTexture::PadPow2(newHeight
);
324 std::unique_ptr
<CTexture
> newTexture
=
325 CTexture::CreateTexture(m_textureWidth
, newHeight
, XB_FMT_A8
);
327 if (!newTexture
|| !newTexture
->GetPixels())
329 CLog::Log(LOGERROR
, "GUIFontTTFGLES::{}: Error creating new cache texture for size {:f}",
334 m_textureHeight
= newTexture
->GetHeight();
335 m_textureScaleY
= 1.0f
/ m_textureHeight
;
336 m_textureWidth
= newTexture
->GetWidth();
337 m_textureScaleX
= 1.0f
/ m_textureWidth
;
338 if (m_textureHeight
< newHeight
)
339 CLog::Log(LOGWARNING
,
340 "GUIFontTTFGLES::{}: allocated new texture with height of {}, requested {}", __func__
,
341 m_textureHeight
, newHeight
);
342 m_staticCache
.Flush();
343 m_dynamicCache
.Flush();
345 memset(newTexture
->GetPixels(), 0, m_textureHeight
* newTexture
->GetPitch());
349 m_updateY2
= m_texture
->GetHeight();
351 unsigned char* src
= m_texture
->GetPixels();
352 unsigned char* dst
= newTexture
->GetPixels();
353 for (unsigned int y
= 0; y
< m_texture
->GetHeight(); y
++)
355 memcpy(dst
, src
, m_texture
->GetPitch());
356 src
+= m_texture
->GetPitch();
357 dst
+= newTexture
->GetPitch();
361 m_textureStatus
= TEXTURE_REALLOCATED
;
366 bool CGUIFontTTFGLES::CopyCharToTexture(
367 FT_BitmapGlyph bitGlyph
, unsigned int x1
, unsigned int y1
, unsigned int x2
, unsigned int y2
)
369 FT_Bitmap bitmap
= bitGlyph
->bitmap
;
371 unsigned char* source
= bitmap
.buffer
;
372 unsigned char* target
= m_texture
->GetPixels() + y1
* m_texture
->GetPitch() + x1
;
374 for (unsigned int y
= y1
; y
< y2
; y
++)
376 memcpy(target
, source
, x2
- x1
);
377 source
+= bitmap
.width
;
378 target
+= m_texture
->GetPitch();
381 switch (m_textureStatus
)
383 case TEXTURE_UPDATED
:
385 m_updateY1
= std::min(m_updateY1
, y1
);
386 m_updateY2
= std::max(m_updateY2
, y2
);
394 m_textureStatus
= TEXTURE_UPDATED
;
398 case TEXTURE_REALLOCATED
:
400 m_updateY2
= std::max(m_updateY2
, y2
);
412 void CGUIFontTTFGLES::DeleteHardwareTexture()
414 if (m_textureStatus
!= TEXTURE_VOID
)
416 if (glIsTexture(m_nTexture
))
417 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
419 m_textureStatus
= TEXTURE_VOID
;
420 m_updateY1
= m_updateY2
= 0;
424 void CGUIFontTTFGLES::CreateStaticVertexBuffers(void)
426 if (m_staticVertexBufferCreated
)
429 // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
430 glGenBuffers(1, &m_elementArrayHandle
);
431 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, m_elementArrayHandle
);
433 // Create an array holding the mesh indices to convert quads to triangles
434 GLushort index
[ELEMENT_ARRAY_MAX_CHAR_INDEX
][6];
435 for (size_t i
= 0; i
< ELEMENT_ARRAY_MAX_CHAR_INDEX
; i
++)
438 index
[i
][1] = 4 * i
+ 1;
439 index
[i
][2] = 4 * i
+ 2;
440 index
[i
][3] = 4 * i
+ 1;
441 index
[i
][4] = 4 * i
+ 3;
442 index
[i
][5] = 4 * i
+ 2;
445 glBufferData(GL_ELEMENT_ARRAY_BUFFER
, sizeof index
, index
, GL_STATIC_DRAW
);
446 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
447 m_staticVertexBufferCreated
= true;
450 void CGUIFontTTFGLES::DestroyStaticVertexBuffers(void)
452 if (!m_staticVertexBufferCreated
)
455 glDeleteBuffers(1, &m_elementArrayHandle
);
456 m_staticVertexBufferCreated
= false;
459 GLuint
CGUIFontTTFGLES::m_elementArrayHandle
{0};
460 bool CGUIFontTTFGLES::m_staticVertexBufferCreated
{false};