/xbmc/guilib/GUIFontTTFDX.cpp

http://github.com/xbmc/xbmc · C++ · 364 lines · 271 code · 60 blank · 33 comment · 45 complexity · 1662fb0e724d7e9dcddd5ec34a341d47 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 "GUIFontTTFDX.h"
  9. #include "GUIFontManager.h"
  10. #include "GUIShaderDX.h"
  11. #include "Texture.h"
  12. #include "rendering/dx/DeviceResources.h"
  13. #include "rendering/dx/RenderContext.h"
  14. #include "utils/log.h"
  15. // stuff for freetype
  16. #include <ft2build.h>
  17. using namespace Microsoft::WRL;
  18. #ifdef TARGET_WINDOWS_STORE
  19. #define generic GenericFromFreeTypeLibrary
  20. #endif
  21. #include FT_FREETYPE_H
  22. #include FT_GLYPH_H
  23. CGUIFontTTFDX::CGUIFontTTFDX(const std::string& strFileName)
  24. : CGUIFontTTFBase(strFileName)
  25. {
  26. m_speedupTexture = nullptr;
  27. m_vertexBuffer = nullptr;
  28. m_vertexWidth = 0;
  29. m_buffers.clear();
  30. DX::Windowing()->Register(this);
  31. }
  32. CGUIFontTTFDX::~CGUIFontTTFDX(void)
  33. {
  34. DX::Windowing()->Unregister(this);
  35. if (m_speedupTexture)
  36. {
  37. delete m_speedupTexture;
  38. m_speedupTexture = nullptr;
  39. }
  40. m_vertexBuffer = nullptr;
  41. m_staticIndexBuffer = nullptr;
  42. if (!m_buffers.empty())
  43. {
  44. std::for_each(m_buffers.begin(), m_buffers.end(), [](CD3DBuffer* buf) {
  45. if (buf) delete buf;
  46. });
  47. }
  48. m_buffers.clear();
  49. m_staticIndexBufferCreated = false;
  50. m_vertexWidth = 0;
  51. }
  52. bool CGUIFontTTFDX::FirstBegin()
  53. {
  54. if (!DX::DeviceResources::Get()->GetD3DContext())
  55. return false;
  56. CGUIShaderDX* pGUIShader = DX::Windowing()->GetGUIShader();
  57. pGUIShader->Begin(SHADER_METHOD_RENDER_FONT);
  58. return true;
  59. }
  60. void CGUIFontTTFDX::LastEnd()
  61. {
  62. ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext();
  63. if (!pContext)
  64. return;
  65. typedef CGUIFontTTFBase::CTranslatedVertices trans;
  66. bool transIsEmpty = std::all_of(m_vertexTrans.begin(), m_vertexTrans.end(),
  67. [](trans& _) { return _.vertexBuffer->size <= 0; });
  68. // no chars to render
  69. if (m_vertex.empty() && transIsEmpty)
  70. return;
  71. CreateStaticIndexBuffer();
  72. unsigned int offset = 0;
  73. unsigned int stride = sizeof(SVertex);
  74. CGUIShaderDX* pGUIShader = DX::Windowing()->GetGUIShader();
  75. // Set font texture as shader resource
  76. pGUIShader->SetShaderViews(1, m_speedupTexture->GetAddressOfSRV());
  77. // Enable alpha blend
  78. DX::Windowing()->SetAlphaBlendEnable(true);
  79. // Set our static index buffer
  80. pContext->IASetIndexBuffer(m_staticIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
  81. // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
  82. pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  83. if (!m_vertex.empty())
  84. {
  85. // Deal with vertices that had to use software clipping
  86. if (!UpdateDynamicVertexBuffer(&m_vertex[0], m_vertex.size()))
  87. return;
  88. // Set the dynamic vertex buffer to active in the input assembler
  89. pContext->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset);
  90. // Do the actual drawing operation, split into groups of characters no
  91. // larger than the pre-determined size of the element array
  92. size_t size = m_vertex.size() / 4;
  93. for (size_t character = 0; size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
  94. {
  95. size_t count = size - character;
  96. count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
  97. // 6 indices and 4 vertices per character
  98. pGUIShader->DrawIndexed(count * 6, 0, character * 4);
  99. }
  100. }
  101. if (!transIsEmpty)
  102. {
  103. // Deal with the vertices that can be hardware clipped and therefore translated
  104. // Store current GPU transform
  105. XMMATRIX view = pGUIShader->GetView();
  106. // Store current scissor
  107. CRect scissor = CServiceBroker::GetWinSystem()->GetGfxContext().StereoCorrection(CServiceBroker::GetWinSystem()->GetGfxContext().GetScissors());
  108. for (size_t i = 0; i < m_vertexTrans.size(); i++)
  109. {
  110. // ignore empty buffers
  111. if (m_vertexTrans[i].vertexBuffer->size == 0)
  112. continue;
  113. // Apply the clip rectangle
  114. CRect clip = DX::Windowing()->ClipRectToScissorRect(m_vertexTrans[i].clip);
  115. // Intersect with current scissors
  116. clip.Intersect(scissor);
  117. // skip empty clip, a little improvement to not render invisible text
  118. if (clip.IsEmpty())
  119. continue;
  120. DX::Windowing()->SetScissors(clip);
  121. // Apply the translation to the model view matrix
  122. XMMATRIX translation = XMMatrixTranslation(m_vertexTrans[i].translateX, m_vertexTrans[i].translateY, m_vertexTrans[i].translateZ);
  123. pGUIShader->SetView(XMMatrixMultiply(translation, view));
  124. CD3DBuffer* vbuffer = reinterpret_cast<CD3DBuffer*>(m_vertexTrans[i].vertexBuffer->bufferHandle);
  125. // Set the static vertex buffer to active in the input assembler
  126. ID3D11Buffer* buffers[1] = { vbuffer->Get() };
  127. pContext->IASetVertexBuffers(0, 1, buffers, &stride, &offset);
  128. // Do the actual drawing operation, split into groups of characters no
  129. // larger than the pre-determined size of the element array
  130. for (size_t character = 0; m_vertexTrans[i].vertexBuffer->size > character; character += ELEMENT_ARRAY_MAX_CHAR_INDEX)
  131. {
  132. size_t count = m_vertexTrans[i].vertexBuffer->size - character;
  133. count = std::min<size_t>(count, ELEMENT_ARRAY_MAX_CHAR_INDEX);
  134. // 6 indices and 4 vertices per character
  135. pGUIShader->DrawIndexed(count * 6, 0, character * 4);
  136. }
  137. }
  138. // restore scissor
  139. DX::Windowing()->SetScissors(scissor);
  140. // Restore the original transform
  141. pGUIShader->SetView(view);
  142. }
  143. pGUIShader->RestoreBuffers();
  144. }
  145. CVertexBuffer CGUIFontTTFDX::CreateVertexBuffer(const std::vector<SVertex> &vertices) const
  146. {
  147. CD3DBuffer* buffer = nullptr;
  148. if (!vertices.empty()) // do not create empty buffers, leave buffer as nullptr, it will be ignored on drawing stage
  149. {
  150. buffer = new CD3DBuffer();
  151. if (!buffer->Create(D3D11_BIND_VERTEX_BUFFER, vertices.size(), sizeof(SVertex), DXGI_FORMAT_UNKNOWN, D3D11_USAGE_IMMUTABLE, &vertices[0]))
  152. CLog::LogF(LOGERROR, "Failed to create vertex buffer.");
  153. else
  154. AddReference((CGUIFontTTFDX*)this, buffer);
  155. }
  156. return CVertexBuffer(reinterpret_cast<void*>(buffer), vertices.size() / 4, this);
  157. }
  158. void CGUIFontTTFDX::AddReference(CGUIFontTTFDX* font, CD3DBuffer* pBuffer)
  159. {
  160. font->m_buffers.push_back(pBuffer);
  161. }
  162. void CGUIFontTTFDX::DestroyVertexBuffer(CVertexBuffer &buffer) const
  163. {
  164. if (nullptr != buffer.bufferHandle)
  165. {
  166. CD3DBuffer* vbuffer = reinterpret_cast<CD3DBuffer*>(buffer.bufferHandle);
  167. ClearReference((CGUIFontTTFDX*)this, vbuffer);
  168. if (vbuffer)
  169. delete vbuffer;
  170. buffer.bufferHandle = 0;
  171. }
  172. }
  173. void CGUIFontTTFDX::ClearReference(CGUIFontTTFDX* font, CD3DBuffer* pBuffer)
  174. {
  175. std::list<CD3DBuffer*>::iterator it = std::find(font->m_buffers.begin(), font->m_buffers.end(), pBuffer);
  176. if (it != font->m_buffers.end())
  177. font->m_buffers.erase(it);
  178. }
  179. CBaseTexture* CGUIFontTTFDX::ReallocTexture(unsigned int& newHeight)
  180. {
  181. assert(newHeight != 0);
  182. assert(m_textureWidth != 0);
  183. if(m_textureHeight == 0)
  184. {
  185. delete m_texture;
  186. m_texture = nullptr;
  187. delete m_speedupTexture;
  188. m_speedupTexture = nullptr;
  189. }
  190. m_staticCache.Flush();
  191. m_dynamicCache.Flush();
  192. CDXTexture* pNewTexture = new CDXTexture(m_textureWidth, newHeight, XB_FMT_A8);
  193. CD3DTexture* newSpeedupTexture = new CD3DTexture();
  194. if (!newSpeedupTexture->Create(m_textureWidth, newHeight, 1, D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8_UNORM))
  195. {
  196. delete newSpeedupTexture;
  197. delete pNewTexture;
  198. return nullptr;
  199. }
  200. // There might be data to copy from the previous texture
  201. if (newSpeedupTexture && m_speedupTexture)
  202. {
  203. CD3D11_BOX rect(0, 0, 0, m_textureWidth, m_textureHeight, 1);
  204. ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetImmediateContext();
  205. pContext->CopySubresourceRegion(newSpeedupTexture->Get(), 0, 0, 0, 0, m_speedupTexture->Get(), 0, &rect);
  206. }
  207. if (m_texture)
  208. {
  209. delete m_texture;
  210. m_texture = nullptr;
  211. }
  212. if (m_speedupTexture)
  213. {
  214. delete m_speedupTexture;
  215. m_speedupTexture = nullptr;
  216. }
  217. m_textureHeight = newHeight;
  218. m_textureScaleY = 1.0f / m_textureHeight;
  219. m_speedupTexture = newSpeedupTexture;
  220. return pNewTexture;
  221. }
  222. bool CGUIFontTTFDX::CopyCharToTexture(FT_BitmapGlyph bitGlyph, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
  223. {
  224. FT_Bitmap bitmap = bitGlyph->bitmap;
  225. ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetImmediateContext();
  226. if (m_speedupTexture && m_speedupTexture->Get() && pContext && bitmap.buffer)
  227. {
  228. CD3D11_BOX dstBox(x1, y1, 0, x2, y2, 1);
  229. pContext->UpdateSubresource(m_speedupTexture->Get(), 0, &dstBox, bitmap.buffer, bitmap.pitch, 0);
  230. return true;
  231. }
  232. return false;
  233. }
  234. void CGUIFontTTFDX::DeleteHardwareTexture()
  235. {
  236. }
  237. bool CGUIFontTTFDX::UpdateDynamicVertexBuffer(const SVertex* pSysMem, unsigned int vertex_count)
  238. {
  239. ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice();
  240. ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext();
  241. if (!pDevice || !pContext)
  242. return false;
  243. unsigned width = sizeof(SVertex) * vertex_count;
  244. if (width > m_vertexWidth) // create or re-create
  245. {
  246. CD3D11_BUFFER_DESC bufferDesc(width, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
  247. D3D11_SUBRESOURCE_DATA initData;
  248. ZeroMemory(&initData, sizeof(D3D11_SUBRESOURCE_DATA));
  249. initData.pSysMem = pSysMem;
  250. if (FAILED(pDevice->CreateBuffer(&bufferDesc, &initData, m_vertexBuffer.ReleaseAndGetAddressOf())))
  251. {
  252. CLog::LogF(LOGERROR, "Failed to create the vertex buffer.");
  253. return false;
  254. }
  255. m_vertexWidth = width;
  256. }
  257. else
  258. {
  259. D3D11_MAPPED_SUBRESOURCE resource;
  260. if (FAILED(pContext->Map(m_vertexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &resource)))
  261. {
  262. CLog::LogF(LOGERROR, "Failed to update the vertex buffer.");
  263. return false;
  264. }
  265. memcpy(resource.pData, pSysMem, width);
  266. pContext->Unmap(m_vertexBuffer.Get(), 0);
  267. }
  268. return true;
  269. }
  270. void CGUIFontTTFDX::CreateStaticIndexBuffer(void)
  271. {
  272. if (m_staticIndexBufferCreated)
  273. return;
  274. ComPtr<ID3D11Device> pDevice = DX::DeviceResources::Get()->GetD3DDevice();
  275. if (!pDevice)
  276. return;
  277. uint16_t index[ELEMENT_ARRAY_MAX_CHAR_INDEX][6];
  278. for (size_t i = 0; i < ELEMENT_ARRAY_MAX_CHAR_INDEX; i++)
  279. {
  280. index[i][0] = 4 * i;
  281. index[i][1] = 4 * i + 1;
  282. index[i][2] = 4 * i + 2;
  283. index[i][3] = 4 * i + 2;
  284. index[i][4] = 4 * i + 3;
  285. index[i][5] = 4 * i + 0;
  286. }
  287. CD3D11_BUFFER_DESC desc(sizeof(index), D3D11_BIND_INDEX_BUFFER, D3D11_USAGE_IMMUTABLE);
  288. D3D11_SUBRESOURCE_DATA initData = { 0 };
  289. initData.pSysMem = index;
  290. if (SUCCEEDED(pDevice->CreateBuffer(&desc, &initData, m_staticIndexBuffer.ReleaseAndGetAddressOf())))
  291. m_staticIndexBufferCreated = true;
  292. }
  293. bool CGUIFontTTFDX::m_staticIndexBufferCreated = false;
  294. ComPtr<ID3D11Buffer> CGUIFontTTFDX::m_staticIndexBuffer = nullptr;
  295. void CGUIFontTTFDX::OnDestroyDevice(bool fatal)
  296. {
  297. m_staticIndexBufferCreated = false;
  298. m_vertexWidth = 0;
  299. m_staticIndexBuffer = nullptr;
  300. m_vertexBuffer = nullptr;
  301. }
  302. void CGUIFontTTFDX::OnCreateDevice(void)
  303. {
  304. }