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 "GUIFontTTFGL.h"
12 #include "GUIFontManager.h"
13 #include "ServiceBroker.h"
15 #include "TextureManager.h"
17 #include "rendering/MatrixGL.h"
18 #include "rendering/gl/RenderSystemGL.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 CGUIFontTTFGL(fontIdent
);
42 CGUIFontTTFGL::CGUIFontTTFGL(const std::string
& fontIdent
) : CGUIFontTTF(fontIdent
)
46 CGUIFontTTFGL::~CGUIFontTTFGL(void)
48 // It's important that all the CGUIFontCacheEntry objects are
49 // destructed before the CGUIFontTTFGL goes out of scope, because
50 // our virtual methods won't be accessible after this point
51 m_dynamicCache
.Flush();
52 DeleteHardwareTexture();
55 bool CGUIFontTTFGL::FirstBegin()
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
);
63 internalFormat
= GL_R8
;
65 internalFormat
= GL_LUMINANCE
;
66 renderSystem
->EnableShader(ShaderMethodGL::SM_FONTS
);
68 if (m_textureStatus
== TEXTURE_REALLOCATED
)
70 if (glIsTexture(m_nTexture
))
71 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
72 m_textureStatus
= TEXTURE_VOID
;
75 if (m_textureStatus
== TEXTURE_VOID
)
77 // Have OpenGL generate a texture object handle for us
78 glGenTextures(1, static_cast<GLuint
*>(&m_nTexture
));
80 // Bind the texture object
81 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
83 // Set the texture's stretching properties
84 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
85 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
87 // Set the texture image -- THIS WORKS, so the pixels must be wrong.
88 glTexImage2D(GL_TEXTURE_2D
, 0, internalFormat
, m_texture
->GetWidth(), m_texture
->GetHeight(), 0,
89 pixformat
, GL_UNSIGNED_BYTE
, 0);
92 m_textureStatus
= TEXTURE_UPDATED
;
95 if (m_textureStatus
== TEXTURE_UPDATED
)
97 // Copies one more line in case we have to sample from there
98 m_updateY2
= std::min(m_updateY2
+ 1, m_texture
->GetHeight());
100 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
101 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, m_updateY1
, m_texture
->GetWidth(), m_updateY2
- m_updateY1
,
102 pixformat
, GL_UNSIGNED_BYTE
,
103 m_texture
->GetPixels() + m_updateY1
* m_texture
->GetPitch());
105 m_updateY1
= m_updateY2
= 0;
106 m_textureStatus
= TEXTURE_READY
;
110 glBlendFuncSeparate(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
, GL_ONE_MINUS_DST_ALPHA
, GL_ONE
);
112 glActiveTexture(GL_TEXTURE0
);
113 glBindTexture(GL_TEXTURE_2D
, m_nTexture
);
118 void CGUIFontTTFGL::LastEnd()
120 CWinSystemBase
* const winSystem
= CServiceBroker::GetWinSystem();
124 CRenderSystemGL
* renderSystem
= dynamic_cast<CRenderSystemGL
*>(CServiceBroker::GetRenderSystem());
126 GLint posLoc
= renderSystem
->ShaderGetPos();
127 GLint colLoc
= renderSystem
->ShaderGetCol();
128 GLint tex0Loc
= renderSystem
->ShaderGetCoord0();
129 GLint modelLoc
= renderSystem
->ShaderGetModel();
131 CreateStaticVertexBuffers();
133 // Enable the attributes used by this shader
134 glEnableVertexAttribArray(posLoc
);
135 glEnableVertexAttribArray(colLoc
);
136 glEnableVertexAttribArray(tex0Loc
);
138 if (!m_vertex
.empty())
141 // Deal with vertices that had to use software clipping
142 std::vector
<SVertex
> vecVertices(6 * (m_vertex
.size() / 4));
143 SVertex
* vertices
= &vecVertices
[0];
144 for (size_t i
= 0; i
< m_vertex
.size(); i
+= 4)
146 *vertices
++ = m_vertex
[i
];
147 *vertices
++ = m_vertex
[i
+ 1];
148 *vertices
++ = m_vertex
[i
+ 2];
150 *vertices
++ = m_vertex
[i
+ 1];
151 *vertices
++ = m_vertex
[i
+ 3];
152 *vertices
++ = m_vertex
[i
+ 2];
154 vertices
= &vecVertices
[0];
158 glGenBuffers(1, &VertexVBO
);
159 glBindBuffer(GL_ARRAY_BUFFER
, VertexVBO
);
160 glBufferData(GL_ARRAY_BUFFER
, sizeof(SVertex
) * vecVertices
.size(), &vecVertices
[0],
163 glVertexAttribPointer(posLoc
, 3, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
164 reinterpret_cast<const GLvoid
*>(offsetof(SVertex
, x
)));
165 glVertexAttribPointer(colLoc
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof(SVertex
),
166 reinterpret_cast<const GLvoid
*>(offsetof(SVertex
, r
)));
167 glVertexAttribPointer(tex0Loc
, 2, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
168 reinterpret_cast<const GLvoid
*>(offsetof(SVertex
, u
)));
170 glDrawArrays(GL_TRIANGLES
, 0, vecVertices
.size());
172 glBindBuffer(GL_ARRAY_BUFFER
, 0);
173 glDeleteBuffers(1, &VertexVBO
);
176 if (!m_vertexTrans
.empty())
178 // Deal with the vertices that can be hardware clipped and therefore translated
180 // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
181 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, m_elementArrayHandle
);
182 // Store current scissor
183 CGraphicContext
& context
= winSystem
->GetGfxContext();
184 CRect scissor
= context
.StereoCorrection(context
.GetScissors());
186 for (size_t i
= 0; i
< m_vertexTrans
.size(); i
++)
188 if (m_vertexTrans
[i
].m_vertexBuffer
->bufferHandle
== 0)
193 // Apply the clip rectangle
194 CRect clip
= renderSystem
->ClipRectToScissorRect(m_vertexTrans
[i
].m_clip
);
197 // intersect with current scissor
198 clip
.Intersect(scissor
);
202 renderSystem
->SetScissors(clip
);
205 // Apply the translation to the currently active (top-of-stack) model view matrix
206 glMatrixModview
.Push();
207 glMatrixModview
.Get().Translatef(m_vertexTrans
[i
].m_translateX
, m_vertexTrans
[i
].m_translateY
,
208 m_vertexTrans
[i
].m_translateZ
);
209 glUniformMatrix4fv(modelLoc
, 1, GL_FALSE
, glMatrixModview
.Get());
211 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
212 glBindBuffer(GL_ARRAY_BUFFER
, m_vertexTrans
[i
].m_vertexBuffer
->bufferHandle
);
214 // Do the actual drawing operation, split into groups of characters no
215 // larger than the pre-determined size of the element array
216 for (size_t character
= 0; m_vertexTrans
[i
].m_vertexBuffer
->size
> character
;
217 character
+= ELEMENT_ARRAY_MAX_CHAR_INDEX
)
219 size_t count
= m_vertexTrans
[i
].m_vertexBuffer
->size
- character
;
220 count
= std::min
<size_t>(count
, ELEMENT_ARRAY_MAX_CHAR_INDEX
);
222 // Set up the offsets of the various vertex attributes within the buffer
223 // object bound to GL_ARRAY_BUFFER
224 glVertexAttribPointer(
225 posLoc
, 3, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
226 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, x
)));
227 glVertexAttribPointer(
228 colLoc
, 4, GL_UNSIGNED_BYTE
, GL_TRUE
, sizeof(SVertex
),
229 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, r
)));
230 glVertexAttribPointer(
231 tex0Loc
, 2, GL_FLOAT
, GL_FALSE
, sizeof(SVertex
),
232 reinterpret_cast<GLvoid
*>(character
* sizeof(SVertex
) * 4 + offsetof(SVertex
, u
)));
234 glDrawElements(GL_TRIANGLES
, 6 * count
, GL_UNSIGNED_SHORT
, 0);
237 glMatrixModview
.Pop();
239 // Restore the original scissor rectangle
240 renderSystem
->SetScissors(scissor
);
241 // Restore the original model view matrix
242 glUniformMatrix4fv(modelLoc
, 1, GL_FALSE
, glMatrixModview
.Get());
243 // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
244 glBindBuffer(GL_ARRAY_BUFFER
, 0);
245 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
248 // Disable the attributes used by this shader
249 glDisableVertexAttribArray(posLoc
);
250 glDisableVertexAttribArray(colLoc
);
251 glDisableVertexAttribArray(tex0Loc
);
253 renderSystem
->DisableShader();
256 CVertexBuffer
CGUIFontTTFGL::CreateVertexBuffer(const std::vector
<SVertex
>& vertices
) const
258 assert(vertices
.size() % 4 == 0);
259 GLuint bufferHandle
= 0;
261 // Do not create empty buffers, leave buffer as 0, it will be ignored in drawing stage
262 if (!vertices
.empty())
264 // Generate a unique buffer object name and put it in bufferHandle
265 glGenBuffers(1, &bufferHandle
);
266 // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
267 glBindBuffer(GL_ARRAY_BUFFER
, bufferHandle
);
268 // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
269 // binding point (i.e. our buffer object) and initialise it from the
270 // specified client-side pointer
271 glBufferData(GL_ARRAY_BUFFER
, vertices
.size() * sizeof(SVertex
), vertices
.data(),
273 // Unbind GL_ARRAY_BUFFER
274 glBindBuffer(GL_ARRAY_BUFFER
, 0);
277 return CVertexBuffer(bufferHandle
, vertices
.size() / 4, this);
280 void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer
& buffer
) const
282 if (buffer
.bufferHandle
!= 0)
284 // Release the buffer name for reuse
285 glDeleteBuffers(1, static_cast<GLuint
*>(&buffer
.bufferHandle
));
286 buffer
.bufferHandle
= 0;
290 std::unique_ptr
<CTexture
> CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight
)
292 newHeight
= CTexture::PadPow2(newHeight
);
294 std::unique_ptr
<CTexture
> newTexture
=
295 CTexture::CreateTexture(m_textureWidth
, newHeight
, XB_FMT_A8
);
297 if (!newTexture
|| !newTexture
->GetPixels())
299 CLog::Log(LOGERROR
, "GUIFontTTFGL::{}: Error creating new cache texture for size {:f}",
304 m_textureHeight
= newTexture
->GetHeight();
305 m_textureScaleY
= 1.0f
/ m_textureHeight
;
306 m_textureWidth
= newTexture
->GetWidth();
307 m_textureScaleX
= 1.0f
/ m_textureWidth
;
308 if (m_textureHeight
< newHeight
)
309 CLog::Log(LOGWARNING
, "GUIFontTTFGL::{}: allocated new texture with height of {}, requested {}",
310 __func__
, m_textureHeight
, newHeight
);
311 m_staticCache
.Flush();
312 m_dynamicCache
.Flush();
314 memset(newTexture
->GetPixels(), 0, m_textureHeight
* newTexture
->GetPitch());
318 m_updateY2
= m_texture
->GetHeight();
320 unsigned char* src
= m_texture
->GetPixels();
321 unsigned char* dst
= newTexture
->GetPixels();
322 for (unsigned int y
= 0; y
< m_texture
->GetHeight(); y
++)
324 memcpy(dst
, src
, m_texture
->GetPitch());
325 src
+= m_texture
->GetPitch();
326 dst
+= newTexture
->GetPitch();
330 m_textureStatus
= TEXTURE_REALLOCATED
;
335 bool CGUIFontTTFGL::CopyCharToTexture(
336 FT_BitmapGlyph bitGlyph
, unsigned int x1
, unsigned int y1
, unsigned int x2
, unsigned int y2
)
338 FT_Bitmap bitmap
= bitGlyph
->bitmap
;
340 unsigned char* source
= bitmap
.buffer
;
341 unsigned char* target
= m_texture
->GetPixels() + y1
* m_texture
->GetPitch() + x1
;
343 for (unsigned int y
= y1
; y
< y2
; y
++)
345 memcpy(target
, source
, x2
- x1
);
346 source
+= bitmap
.width
;
347 target
+= m_texture
->GetPitch();
350 switch (m_textureStatus
)
352 case TEXTURE_UPDATED
:
354 m_updateY1
= std::min(m_updateY1
, y1
);
355 m_updateY2
= std::max(m_updateY2
, y2
);
363 m_textureStatus
= TEXTURE_UPDATED
;
367 case TEXTURE_REALLOCATED
:
369 m_updateY2
= std::max(m_updateY2
, y2
);
381 void CGUIFontTTFGL::DeleteHardwareTexture()
383 if (m_textureStatus
!= TEXTURE_VOID
)
385 if (glIsTexture(m_nTexture
))
386 CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture
);
388 m_textureStatus
= TEXTURE_VOID
;
389 m_updateY1
= m_updateY2
= 0;
393 void CGUIFontTTFGL::CreateStaticVertexBuffers(void)
395 if (m_staticVertexBufferCreated
)
398 // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
399 glGenBuffers(1, &m_elementArrayHandle
);
400 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, m_elementArrayHandle
);
402 // Create an array holding the mesh indices to convert quads to triangles
403 GLushort index
[ELEMENT_ARRAY_MAX_CHAR_INDEX
][6];
404 for (size_t i
= 0; i
< ELEMENT_ARRAY_MAX_CHAR_INDEX
; i
++)
407 index
[i
][1] = 4 * i
+ 1;
408 index
[i
][2] = 4 * i
+ 2;
409 index
[i
][3] = 4 * i
+ 1;
410 index
[i
][4] = 4 * i
+ 3;
411 index
[i
][5] = 4 * i
+ 2;
414 glBufferData(GL_ELEMENT_ARRAY_BUFFER
, sizeof index
, index
, GL_STATIC_DRAW
);
415 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER
, 0);
416 m_staticVertexBufferCreated
= true;
419 void CGUIFontTTFGL::DestroyStaticVertexBuffers(void)
421 if (!m_staticVertexBufferCreated
)
424 glDeleteBuffers(1, &m_elementArrayHandle
);
425 m_staticVertexBufferCreated
= false;
428 GLuint
CGUIFontTTFGL::m_elementArrayHandle
{0};
429 bool CGUIFontTTFGL::m_staticVertexBufferCreated
{false};