PageRenderTime 80ms CodeModel.GetById 17ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

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