summaryrefslogtreecommitdiffstats
path: root/xbmc/guilib/GUIFontCache.h
blob: 8a967fa4a28980eb102e91ec515ab815e544d66c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/*
 *  Copyright (C) 2005-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#pragma once

/*!
\file GUIFontCache.h
\brief
*/

#include "utils/ColorUtils.h"
#include "utils/TransformMatrix.h"

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <memory>
#include <stdint.h>
#include <vector>

constexpr float FONT_CACHE_DIST_LIMIT = 0.01f;

class CGraphicContext;

template<class Position, class Value>
class CGUIFontCache;
class CGUIFontTTF;

template<class Position, class Value>
class CGUIFontCacheImpl;

template<class Position>
struct CGUIFontCacheKey
{
  Position m_pos;
  std::vector<UTILS::COLOR::Color>& m_colors;
  vecText& m_text;
  uint32_t m_alignment;
  float m_maxPixelWidth;
  bool m_scrolling;
  const TransformMatrix& m_matrix;
  float m_scaleX;
  float m_scaleY;

  CGUIFontCacheKey(Position pos,
                   std::vector<UTILS::COLOR::Color>& colors,
                   vecText& text,
                   uint32_t alignment,
                   float maxPixelWidth,
                   bool scrolling,
                   const TransformMatrix& matrix,
                   float scaleX,
                   float scaleY)
    : m_pos(pos),
      m_colors(colors),
      m_text(text),
      m_alignment(alignment),
      m_maxPixelWidth(maxPixelWidth),
      m_scrolling(scrolling),
      m_matrix(matrix),
      m_scaleX(scaleX),
      m_scaleY(scaleY)
  {
  }
};

template<class Position, class Value>
struct CGUIFontCacheEntry
{
  const CGUIFontCache<Position, Value>& m_cache;
  CGUIFontCacheKey<Position> m_key;
  TransformMatrix m_matrix;
  std::chrono::steady_clock::time_point m_lastUsed;
  Value m_value;

  CGUIFontCacheEntry(const CGUIFontCache<Position, Value>& cache,
                     const CGUIFontCacheKey<Position>& key,
                     std::chrono::steady_clock::time_point now)
    : m_cache(cache),
      m_key(key.m_pos,
            *new std::vector<UTILS::COLOR::Color>,
            *new vecText,
            key.m_alignment,
            key.m_maxPixelWidth,
            key.m_scrolling,
            m_matrix,
            key.m_scaleX,
            key.m_scaleY),
      m_lastUsed(now)
  {
    m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end());
    m_key.m_text.assign(key.m_text.begin(), key.m_text.end());
    m_matrix = key.m_matrix;
  }

  ~CGUIFontCacheEntry();

  void Assign(const CGUIFontCacheKey<Position>& key, std::chrono::steady_clock::time_point now);
};

template<class Position>
struct CGUIFontCacheHash
{
  size_t operator()(const CGUIFontCacheKey<Position>& key) const
  {
    /* Not much effort has gone into choosing this hash function */
    size_t hash = 0, i;
    for (i = 0; i < 3 && i < key.m_text.size(); ++i)
      hash += key.m_text[i];
    if (key.m_colors.size())
      hash += key.m_colors[0];
    hash += static_cast<size_t>(MatrixHashContribution(key)); // horrible
    return hash;
  }
};

template<class Position>
struct CGUIFontCacheKeysMatch
{
  bool operator()(const CGUIFontCacheKey<Position>& a, const CGUIFontCacheKey<Position>& b) const
  {
    // clang-format off
    return a.m_text == b.m_text &&
           a.m_colors == b.m_colors &&
           a.m_alignment == b.m_alignment &&
           a.m_scrolling == b.m_scrolling &&
           a.m_maxPixelWidth == b.m_maxPixelWidth &&
           Match(a.m_pos, a.m_matrix, b.m_pos, b.m_matrix, a.m_scrolling) &&
           a.m_scaleX == b.m_scaleX &&
           a.m_scaleY == b.m_scaleY;
    // clang-format on
  }
};


template<class Position, class Value>
class CGUIFontCache
{
  std::unique_ptr<CGUIFontCacheImpl<Position, Value>> m_impl;

  CGUIFontCache(const CGUIFontCache<Position, Value>&) = delete;
  const CGUIFontCache<Position, Value>& operator=(const CGUIFontCache<Position, Value>&) = delete;

public:
  const CGUIFontTTF& m_font;

  explicit CGUIFontCache(CGUIFontTTF& font);

  ~CGUIFontCache();

  Value& Lookup(const CGraphicContext& context,
                Position& pos,
                const std::vector<UTILS::COLOR::Color>& colors,
                const vecText& text,
                uint32_t alignment,
                float maxPixelWidth,
                bool scrolling,
                std::chrono::steady_clock::time_point now,
                bool& dirtyCache);
  void Flush();
};

struct CGUIFontCacheStaticPosition
{
  float m_x;
  float m_y;
  CGUIFontCacheStaticPosition(float x, float y) : m_x(x), m_y(y) {}
  void UpdateWithOffsets(const CGUIFontCacheStaticPosition& cached, bool scrolling) {}
};

struct CGUIFontCacheStaticValue : public std::shared_ptr<std::vector<SVertex>>
{
  void clear()
  {
    if (*this)
      (*this)->clear();
  }
};

inline bool Match(const CGUIFontCacheStaticPosition& a,
                  const TransformMatrix& a_m,
                  const CGUIFontCacheStaticPosition& b,
                  const TransformMatrix& b_m,
                  bool scrolling)
{
  return a.m_x == b.m_x && a.m_y == b.m_y && a_m == b_m;
}

inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheStaticPosition>& a)
{
  /* Ensure horizontally translated versions end up in different buckets */
  return a.m_matrix.m[0][3];
}

struct CGUIFontCacheDynamicPosition
{
  float m_x;
  float m_y;
  float m_z;
  CGUIFontCacheDynamicPosition() = default;
  CGUIFontCacheDynamicPosition(float x, float y, float z) : m_x(x), m_y(y), m_z(z) {}
  void UpdateWithOffsets(const CGUIFontCacheDynamicPosition& cached, bool scrolling)
  {
    if (scrolling)
      m_x = m_x - cached.m_x;
    else
      m_x = floorf(m_x - cached.m_x + FONT_CACHE_DIST_LIMIT);
    m_y = floorf(m_y - cached.m_y + FONT_CACHE_DIST_LIMIT);
    m_z = floorf(m_z - cached.m_z + FONT_CACHE_DIST_LIMIT);
  }
};

struct CVertexBuffer
{
#if defined(HAS_GL) || defined(HAS_GLES)
  typedef unsigned int BufferHandleType;
#define BUFFER_HANDLE_INIT 0
#elif defined(HAS_DX)
  typedef void* BufferHandleType;
#define BUFFER_HANDLE_INIT nullptr
#endif
  BufferHandleType bufferHandle = BUFFER_HANDLE_INIT; // this is really a GLuint
  size_t size = 0;
  CVertexBuffer() : m_font(nullptr) {}
  CVertexBuffer(BufferHandleType bufferHandle, size_t size, const CGUIFontTTF* font)
    : bufferHandle(bufferHandle), size(size), m_font(font)
  {
  }
  CVertexBuffer(const CVertexBuffer& other)
    : bufferHandle(other.bufferHandle), size(other.size), m_font(other.m_font)
  {
    /* In practice, the copy constructor is only called before a vertex buffer
     * has been attached. If this should ever change, we'll need another support
     * function in GUIFontTTFGL/DX to duplicate a buffer, given its handle. */
    assert(other.bufferHandle == 0);
  }
  CVertexBuffer& operator=(CVertexBuffer& other)
  {
    /* This is used with move-assignment semantics for initialising the object in the font cache */
    assert(bufferHandle == 0);
    bufferHandle = other.bufferHandle;
    other.bufferHandle = 0;
    size = other.size;
    m_font = other.m_font;
    return *this;
  }
  void clear();

private:
  const CGUIFontTTF* m_font;
};

typedef CVertexBuffer CGUIFontCacheDynamicValue;

inline bool Match(const CGUIFontCacheDynamicPosition& a,
                  const TransformMatrix& a_m,
                  const CGUIFontCacheDynamicPosition& b,
                  const TransformMatrix& b_m,
                  bool scrolling)
{
  float diffX = a.m_x - b.m_x + FONT_CACHE_DIST_LIMIT;
  float diffY = a.m_y - b.m_y + FONT_CACHE_DIST_LIMIT;
  float diffZ = a.m_z - b.m_z + FONT_CACHE_DIST_LIMIT;
  // clang-format off
  return (scrolling ||
          diffX - floorf(diffX) < 2 * FONT_CACHE_DIST_LIMIT) &&
          diffY - floorf(diffY) < 2 * FONT_CACHE_DIST_LIMIT &&
          diffZ - floorf(diffZ) < 2 * FONT_CACHE_DIST_LIMIT &&
          a_m.m[0][0] == b_m.m[0][0] &&
          a_m.m[1][1] == b_m.m[1][1] &&
          a_m.m[2][2] == b_m.m[2][2];
  // clang-format on
  // We already know the first 3 columns of both matrices are diagonal, so no need to check the other elements
}

inline float MatrixHashContribution(const CGUIFontCacheKey<CGUIFontCacheDynamicPosition>& a)
{
  return 0;
}