[ExecString] combine SplitParameters with identical function of CUtil
[xbmc.git] / xbmc / guilib / GUIFontTTFGL.cpp
blob12889c9b1dad154bf2e3d3d4861c1a1676bfeaa8
1 /*
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.
7 */
9 #include "GUIFontTTFGL.h"
11 #include "GUIFont.h"
12 #include "GUIFontManager.h"
13 #include "ServiceBroker.h"
14 #include "Texture.h"
15 #include "TextureManager.h"
16 #include "gui3d.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"
23 #include <cassert>
24 #include <memory>
26 // stuff for freetype
27 #include <ft2build.h>
28 #include FT_FREETYPE_H
29 #include FT_GLYPH_H
30 #include FT_OUTLINE_H
32 namespace
34 constexpr size_t ELEMENT_ARRAY_MAX_CHAR_INDEX = 1000;
35 } /* namespace */
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);
62 if (major >= 3)
63 internalFormat = GL_R8;
64 else
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);
91 VerifyGLState();
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;
109 // Turn Blending On
110 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
111 glEnable(GL_BLEND);
112 glActiveTexture(GL_TEXTURE0);
113 glBindTexture(GL_TEXTURE_2D, m_nTexture);
115 return true;
118 void CGUIFontTTFGL::LastEnd()
120 CWinSystemBase* const winSystem = CServiceBroker::GetWinSystem();
121 if (!winSystem)
122 return;
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];
156 GLuint VertexVBO;
158 glGenBuffers(1, &VertexVBO);
159 glBindBuffer(GL_ARRAY_BUFFER, VertexVBO);
160 glBufferData(GL_ARRAY_BUFFER, sizeof(SVertex) * vecVertices.size(), &vecVertices[0],
161 GL_STATIC_DRAW);
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)
190 continue;
193 // Apply the clip rectangle
194 CRect clip = renderSystem->ClipRectToScissorRect(m_vertexTrans[i].m_clip);
195 if (!clip.IsEmpty())
197 // intersect with current scissor
198 clip.Intersect(scissor);
199 // skip empty clip
200 if (clip.IsEmpty())
201 continue;
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(),
272 GL_STATIC_DRAW);
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}",
300 __func__, m_height);
301 return nullptr;
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());
315 if (m_texture)
317 m_updateY1 = 0;
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;
332 return newTexture;
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);
357 break;
359 case TEXTURE_READY:
361 m_updateY1 = y1;
362 m_updateY2 = y2;
363 m_textureStatus = TEXTURE_UPDATED;
365 break;
367 case TEXTURE_REALLOCATED:
369 m_updateY2 = std::max(m_updateY2, y2);
371 break;
373 case TEXTURE_VOID:
374 default:
375 break;
378 return true;
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)
396 return;
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++)
406 index[i][0] = 4 * 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)
422 return;
424 glDeleteBuffers(1, &m_elementArrayHandle);
425 m_staticVertexBufferCreated = false;
428 GLuint CGUIFontTTFGL::m_elementArrayHandle{0};
429 bool CGUIFontTTFGL::m_staticVertexBufferCreated{false};