/xbmc/guilib/GUIFontTTFGL.cpp

http://github.com/xbmc/xbmc · C++ · 455 lines · 331 code · 77 blank · 47 comment · 37 complexity · 2cbbaeced9629aa2044f8fcc9d2c7c45 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "GUIFont.h"
  9. #include "GUIFontTTFGL.h"
  10. #include "GUIFontManager.h"
  11. #include "Texture.h"
  12. #include "TextureManager.h"
  13. #include "windowing/GraphicContext.h"
  14. #include "ServiceBroker.h"
  15. #include "gui3d.h"
  16. #include "utils/log.h"
  17. #include "utils/GLUtils.h"
  18. #ifdef HAS_GL
  19. #include "rendering/gl/RenderSystemGL.h"
  20. #elif HAS_GLES
  21. #include "rendering/gles/RenderSystemGLES.h"
  22. #endif
  23. #include "rendering/MatrixGL.h"
  24. #include <cassert>
  25. // stuff for freetype
  26. #include <ft2build.h>
  27. #include FT_FREETYPE_H
  28. #include FT_GLYPH_H
  29. #include FT_OUTLINE_H
  30. #define ELEMENT_ARRAY_MAX_CHAR_INDEX (1000)
  31. #define BUFFER_OFFSET(i) ((char *)NULL + (i))
  32. CGUIFontTTFGL::CGUIFontTTFGL(const std::string& strFileName)
  33. : CGUIFontTTFBase(strFileName)
  34. {
  35. m_updateY1 = 0;
  36. m_updateY2 = 0;
  37. m_textureStatus = TEXTURE_VOID;
  38. }
  39. CGUIFontTTFGL::~CGUIFontTTFGL(void)
  40. {
  41. // It's important that all the CGUIFontCacheEntry objects are
  42. // destructed before the CGUIFontTTFGL goes out of scope, because
  43. // our virtual methods won't be accessible after this point
  44. m_dynamicCache.Flush();
  45. DeleteHardwareTexture();
  46. }
  47. bool CGUIFontTTFGL::FirstBegin()
  48. {
  49. #if defined(HAS_GL)
  50. GLenum pixformat = GL_RED;
  51. GLenum internalFormat;
  52. unsigned int major, minor;
  53. CRenderSystemGL* renderSystem = dynamic_cast<CRenderSystemGL*>(CServiceBroker::GetRenderSystem());
  54. renderSystem->GetRenderVersion(major, minor);
  55. if (major >= 3)
  56. internalFormat = GL_R8;
  57. else
  58. internalFormat = GL_LUMINANCE;
  59. #else
  60. GLenum pixformat = GL_ALPHA; // deprecated
  61. GLenum internalFormat = GL_ALPHA;
  62. #endif
  63. if (m_textureStatus == TEXTURE_REALLOCATED)
  64. {
  65. if (glIsTexture(m_nTexture))
  66. CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture);
  67. m_textureStatus = TEXTURE_VOID;
  68. }
  69. if (m_textureStatus == TEXTURE_VOID)
  70. {
  71. // Have OpenGL generate a texture object handle for us
  72. glGenTextures(1, (GLuint*) &m_nTexture);
  73. // Bind the texture object
  74. glBindTexture(GL_TEXTURE_2D, m_nTexture);
  75. // Set the texture's stretching properties
  76. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  77. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  78. // Set the texture image -- THIS WORKS, so the pixels must be wrong.
  79. glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_texture->GetWidth(), m_texture->GetHeight(), 0,
  80. pixformat, GL_UNSIGNED_BYTE, 0);
  81. VerifyGLState();
  82. m_textureStatus = TEXTURE_UPDATED;
  83. }
  84. if (m_textureStatus == TEXTURE_UPDATED)
  85. {
  86. glBindTexture(GL_TEXTURE_2D, m_nTexture);
  87. glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_updateY1, m_texture->GetWidth(), m_updateY2 - m_updateY1, pixformat, GL_UNSIGNED_BYTE,
  88. m_texture->GetPixels() + m_updateY1 * m_texture->GetPitch());
  89. m_updateY1 = m_updateY2 = 0;
  90. m_textureStatus = TEXTURE_READY;
  91. }
  92. // Turn Blending On
  93. glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
  94. glEnable(GL_BLEND);
  95. glActiveTexture(GL_TEXTURE0);
  96. glBindTexture(GL_TEXTURE_2D, m_nTexture);
  97. return true;
  98. }
  99. void CGUIFontTTFGL::LastEnd()
  100. {
  101. #ifdef HAS_GL
  102. CRenderSystemGL* renderSystem = dynamic_cast<CRenderSystemGL*>(CServiceBroker::GetRenderSystem());
  103. renderSystem->EnableShader(SM_FONTS);
  104. GLint posLoc = renderSystem->ShaderGetPos();
  105. GLint colLoc = renderSystem->ShaderGetCol();
  106. GLint tex0Loc = renderSystem->ShaderGetCoord0();
  107. GLint modelLoc = renderSystem->ShaderGetModel();
  108. CreateStaticVertexBuffers();
  109. // Enable the attributes used by this shader
  110. glEnableVertexAttribArray(posLoc);
  111. glEnableVertexAttribArray(colLoc);
  112. glEnableVertexAttribArray(tex0Loc);
  113. if (!m_vertex.empty())
  114. {
  115. // Deal with vertices that had to use software clipping
  116. std::vector<SVertex> vecVertices(6 * (m_vertex.size() / 4));
  117. SVertex *vertices = &vecVertices[0];
  118. for (size_t i=0; i<m_vertex.size(); i+=4)
  119. {
  120. *vertices++ = m_vertex[i];
  121. *vertices++ = m_vertex[i+1];
  122. *vertices++ = m_vertex[i+2];
  123. *vertices++ = m_vertex[i+1];
  124. *vertices++ = m_vertex[i+3];
  125. *vertices++ = m_vertex[i+2];
  126. }
  127. vertices = &vecVertices[0];
  128. GLuint VertexVBO;
  129. glGenBuffers(1, &VertexVBO);
  130. glBindBuffer(GL_ARRAY_BUFFER, VertexVBO);
  131. glBufferData(GL_ARRAY_BUFFER, sizeof(SVertex)*vecVertices.size(), &vecVertices[0], GL_STATIC_DRAW);
  132. glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, x)));
  133. glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, r)));
  134. glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), BUFFER_OFFSET(offsetof(SVertex, u)));
  135. glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
  136. glBindBuffer(GL_ARRAY_BUFFER, 0);
  137. glDeleteBuffers(1, &VertexVBO);
  138. }
  139. #else
  140. // GLES 2.0 version.
  141. CRenderSystemGLES* renderSystem = dynamic_cast<CRenderSystemGLES*>(CServiceBroker::GetRenderSystem());
  142. renderSystem->EnableGUIShader(SM_FONTS);
  143. GLint posLoc = renderSystem->GUIShaderGetPos();
  144. GLint colLoc = renderSystem->GUIShaderGetCol();
  145. GLint tex0Loc = renderSystem->GUIShaderGetCoord0();
  146. GLint modelLoc = renderSystem->GUIShaderGetModel();
  147. CreateStaticVertexBuffers();
  148. // Enable the attributes used by this shader
  149. glEnableVertexAttribArray(posLoc);
  150. glEnableVertexAttribArray(colLoc);
  151. glEnableVertexAttribArray(tex0Loc);
  152. if (!m_vertex.empty())
  153. {
  154. // Deal with vertices that had to use software clipping
  155. std::vector<SVertex> vecVertices( 6 * (m_vertex.size() / 4) );
  156. SVertex *vertices = &vecVertices[0];
  157. for (size_t i=0; i<m_vertex.size(); i+=4)
  158. {
  159. *vertices++ = m_vertex[i];
  160. *vertices++ = m_vertex[i+1];
  161. *vertices++ = m_vertex[i+2];
  162. *vertices++ = m_vertex[i+1];
  163. *vertices++ = m_vertex[i+3];
  164. *vertices++ = m_vertex[i+2];
  165. }
  166. vertices = &vecVertices[0];
  167. glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, x));
  168. glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, r));
  169. glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (char*)vertices + offsetof(SVertex, u));
  170. glDrawArrays(GL_TRIANGLES, 0, vecVertices.size());
  171. }
  172. #endif
  173. if (!m_vertexTrans.empty())
  174. {
  175. // Deal with the vertices that can be hardware clipped and therefore translated
  176. // Bind our pre-calculated array to GL_ELEMENT_ARRAY_BUFFER
  177. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
  178. // Store current scissor
  179. CRect scissor = CServiceBroker::GetWinSystem()->GetGfxContext().StereoCorrection(CServiceBroker::GetWinSystem()->GetGfxContext().GetScissors());
  180. for (size_t i = 0; i < m_vertexTrans.size(); i++)
  181. {
  182. if (m_vertexTrans[i].vertexBuffer->bufferHandle == 0)
  183. {
  184. continue;
  185. }
  186. // Apply the clip rectangle
  187. CRect clip = renderSystem->ClipRectToScissorRect(m_vertexTrans[i].clip);
  188. if (!clip.IsEmpty())
  189. {
  190. // intersect with current scissor
  191. clip.Intersect(scissor);
  192. // skip empty clip
  193. if (clip.IsEmpty())
  194. continue;
  195. renderSystem->SetScissors(clip);
  196. }
  197. // Apply the translation to the currently active (top-of-stack) model view matrix
  198. glMatrixModview.Push();
  199. glMatrixModview.Get().Translatef(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
  200. glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glMatrixModview.Get());
  201. // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
  202. glBindBuffer(GL_ARRAY_BUFFER, m_vertexTrans[i].vertexBuffer->bufferHandle);
  203. // Do the actual drawing operation, split into groups of characters no
  204. // larger than the pre-determined size of the element array
  205. for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
  206. {
  207. size_t count = m_vertexTrans[i].vertexBuffer->size - character;
  208. 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(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, x)));
  212. glVertexAttribPointer(colLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, r)));
  213. glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (GLvoid *) (character*sizeof(SVertex)*4 + offsetof(SVertex, u)));
  214. glDrawElements(GL_TRIANGLES, 6 * count, GL_UNSIGNED_SHORT, 0);
  215. }
  216. glMatrixModview.Pop();
  217. }
  218. // Restore the original scissor rectangle
  219. renderSystem->SetScissors(scissor);
  220. // Restore the original model view matrix
  221. glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glMatrixModview.Get());
  222. // Unbind GL_ARRAY_BUFFER and GL_ELEMENT_ARRAY_BUFFER
  223. glBindBuffer(GL_ARRAY_BUFFER, 0);
  224. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  225. }
  226. // Disable the attributes used by this shader
  227. glDisableVertexAttribArray(posLoc);
  228. glDisableVertexAttribArray(colLoc);
  229. glDisableVertexAttribArray(tex0Loc);
  230. #ifdef HAS_GL
  231. renderSystem->DisableShader();
  232. #else
  233. renderSystem->DisableGUIShader();
  234. #endif
  235. }
  236. CVertexBuffer CGUIFontTTFGL::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
  237. {
  238. assert(vertices.size() % 4 == 0);
  239. GLuint bufferHandle = 0;
  240. // Do not create empty buffers, leave buffer as 0, it will be ignored in drawing stage
  241. if (!vertices.empty())
  242. {
  243. // Generate a unique buffer object name and put it in bufferHandle
  244. glGenBuffers(1, &bufferHandle);
  245. // Bind the buffer to the OpenGL context's GL_ARRAY_BUFFER binding point
  246. glBindBuffer(GL_ARRAY_BUFFER, bufferHandle);
  247. // Create a data store for the buffer object bound to the GL_ARRAY_BUFFER
  248. // binding point (i.e. our buffer object) and initialise it from the
  249. // specified client-side pointer
  250. glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(SVertex), vertices.data(), GL_STATIC_DRAW);
  251. // Unbind GL_ARRAY_BUFFER
  252. glBindBuffer(GL_ARRAY_BUFFER, 0);
  253. }
  254. return CVertexBuffer(bufferHandle, vertices.size() / 4, this);
  255. }
  256. void CGUIFontTTFGL::DestroyVertexBuffer(CVertexBuffer &buffer) const
  257. {
  258. if (buffer.bufferHandle != 0)
  259. {
  260. // Release the buffer name for reuse
  261. glDeleteBuffers(1, (GLuint *) &buffer.bufferHandle);
  262. buffer.bufferHandle = 0;
  263. }
  264. }
  265. CBaseTexture* CGUIFontTTFGL::ReallocTexture(unsigned int& newHeight)
  266. {
  267. newHeight = CBaseTexture::PadPow2(newHeight);
  268. CBaseTexture* newTexture = new CTexture(m_textureWidth, newHeight, XB_FMT_A8);
  269. if (!newTexture || newTexture->GetPixels() == NULL)
  270. {
  271. CLog::Log(LOGERROR, "GUIFontTTFGL::CacheCharacter: Error creating new cache texture for size %f", m_height);
  272. delete newTexture;
  273. return NULL;
  274. }
  275. m_textureHeight = newTexture->GetHeight();
  276. m_textureScaleY = 1.0f / m_textureHeight;
  277. m_textureWidth = newTexture->GetWidth();
  278. m_textureScaleX = 1.0f / m_textureWidth;
  279. if (m_textureHeight < newHeight)
  280. CLog::Log(LOGWARNING, "%s: allocated new texture with height of %d, requested %d", __FUNCTION__, m_textureHeight, newHeight);
  281. m_staticCache.Flush();
  282. m_dynamicCache.Flush();
  283. memset(newTexture->GetPixels(), 0, m_textureHeight * newTexture->GetPitch());
  284. if (m_texture)
  285. {
  286. m_updateY1 = 0;
  287. m_updateY2 = m_texture->GetHeight();
  288. unsigned char* src = m_texture->GetPixels();
  289. unsigned char* dst = newTexture->GetPixels();
  290. for (unsigned int y = 0; y < m_texture->GetHeight(); y++)
  291. {
  292. memcpy(dst, src, m_texture->GetPitch());
  293. src += m_texture->GetPitch();
  294. dst += newTexture->GetPitch();
  295. }
  296. delete m_texture;
  297. }
  298. m_textureStatus = TEXTURE_REALLOCATED;
  299. return newTexture;
  300. }
  301. bool CGUIFontTTFGL::CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
  302. {
  303. FT_Bitmap bitmap = bitGlyph->bitmap;
  304. unsigned char* source = bitmap.buffer;
  305. unsigned char* target = m_texture->GetPixels() + y1 * m_texture->GetPitch() + x1;
  306. for (unsigned int y = y1; y < y2; y++)
  307. {
  308. memcpy(target, source, x2-x1);
  309. source += bitmap.width;
  310. target += m_texture->GetPitch();
  311. }
  312. switch (m_textureStatus)
  313. {
  314. case TEXTURE_UPDATED:
  315. {
  316. m_updateY1 = std::min(m_updateY1, y1);
  317. m_updateY2 = std::max(m_updateY2, y2);
  318. }
  319. break;
  320. case TEXTURE_READY:
  321. {
  322. m_updateY1 = y1;
  323. m_updateY2 = y2;
  324. m_textureStatus = TEXTURE_UPDATED;
  325. }
  326. break;
  327. case TEXTURE_REALLOCATED:
  328. {
  329. m_updateY2 = std::max(m_updateY2, y2);
  330. }
  331. break;
  332. case TEXTURE_VOID:
  333. default:
  334. break;
  335. }
  336. return true;
  337. }
  338. void CGUIFontTTFGL::DeleteHardwareTexture()
  339. {
  340. if (m_textureStatus != TEXTURE_VOID)
  341. {
  342. if (glIsTexture(m_nTexture))
  343. CServiceBroker::GetGUI()->GetTextureManager().ReleaseHwTexture(m_nTexture);
  344. m_textureStatus = TEXTURE_VOID;
  345. m_updateY1 = m_updateY2 = 0;
  346. }
  347. }
  348. void CGUIFontTTFGL::CreateStaticVertexBuffers(void)
  349. {
  350. if (m_staticVertexBufferCreated)
  351. return;
  352. // Bind a new buffer to the OpenGL context's GL_ELEMENT_ARRAY_BUFFER binding point
  353. glGenBuffers(1, &m_elementArrayHandle);
  354. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementArrayHandle);
  355. // Create an array holding the mesh indices to convert quads to triangles
  356. GLushort index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
  357. for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
  358. {
  359. index[i][0] = 4*i;
  360. index[i][1] = 4*i+1;
  361. index[i][2] = 4*i+2;
  362. index[i][3] = 4*i+1;
  363. index[i][4] = 4*i+3;
  364. index[i][5] = 4*i+2;
  365. }
  366. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof index, index, GL_STATIC_DRAW);
  367. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  368. m_staticVertexBufferCreated = true;
  369. }
  370. void CGUIFontTTFGL::DestroyStaticVertexBuffers(void)
  371. {
  372. if (!m_staticVertexBufferCreated)
  373. return;
  374. glDeleteBuffers(1, &m_elementArrayHandle);
  375. m_staticVertexBufferCreated = false;
  376. }
  377. GLuint CGUIFontTTFGL::m_elementArrayHandle;
  378. bool CGUIFontTTFGL::m_staticVertexBufferCreated;