[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / guilib / GUIFontTTFGLES.cpp
blobad18836be32efd8142170d84cce8d3369391737b
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 "GUIFontTTFGLES.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/gles/RenderSystemGLES.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 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);
86 VerifyGLState();
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;
104 // Turn Blending On
105 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
106 glEnable(GL_BLEND);
107 glActiveTexture(GL_TEXTURE0);
108 glBindTexture(GL_TEXTURE_2D, m_nTexture);
110 return true;
113 void CGUIFontTTFGLES::LastEnd()
115 CWinSystemBase* const winSystem = CServiceBroker::GetWinSystem();
116 if (!winSystem)
117 return;
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)
177 continue;
180 // Apply the clip rectangle
181 CRect clip = renderSystem->ClipRectToScissorRect(m_vertexTrans[i].m_clip);
182 if (!clip.IsEmpty())
184 // intersect with current scissor
185 clip.Intersect(scissor);
186 // skip empty clip
187 if (clip.IsEmpty())
188 continue;
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(),
259 GL_STATIC_DRAW);
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}",
287 __func__, m_height);
288 return nullptr;
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());
303 if (m_texture)
305 m_updateY1 = 0;
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;
320 return newTexture;
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);
345 break;
347 case TEXTURE_READY:
349 m_updateY1 = y1;
350 m_updateY2 = y2;
351 m_textureStatus = TEXTURE_UPDATED;
353 break;
355 case TEXTURE_REALLOCATED:
357 m_updateY2 = std::max(m_updateY2, y2);
359 break;
361 case TEXTURE_VOID:
362 default:
363 break;
366 return true;
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)
384 return;
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++)
394 index[i][0] = 4 * 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)
410 return;
412 glDeleteBuffers(1, &m_elementArrayHandle);
413 m_staticVertexBufferCreated = false;
416 GLuint CGUIFontTTFGLES::m_elementArrayHandle{0};
417 bool CGUIFontTTFGLES::m_staticVertexBufferCreated{false};