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 "GUIFontTTF.h"
10 #include "windowing/GraphicContext.h"
11 
12 #include <stdint.h>
13 #include <vector>
14 
15 template<class Position, class Value>
16 class CGUIFontCacheImpl
17 {
18   struct EntryList
19   {
20     using HashMap = std::multimap<size_t, CGUIFontCacheEntry<Position, Value>*>;
21     using HashIter = typename HashMap::iterator;
22     using AgeMap = std::multimap<size_t, HashIter>;
23 
~EntryListCGUIFontCacheImpl::EntryList24     ~EntryList()
25     {
26       Flush();
27     }
InsertCGUIFontCacheImpl::EntryList28     HashIter Insert(size_t hash, CGUIFontCacheEntry<Position, Value> *v)
29     {
30       auto r (hashMap.insert(typename HashMap::value_type(hash, v)));
31       if (r->second)
32         ageMap.insert(typename AgeMap::value_type(r->second->m_lastUsedMillis, r));
33       return r;
34     }
FlushCGUIFontCacheImpl::EntryList35     void Flush()
36     {
37       ageMap.clear();
38       for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
39         delete(it->second);
40       hashMap.clear();
41     }
FindKeyCGUIFontCacheImpl::EntryList42     typename HashMap::iterator FindKey(CGUIFontCacheKey<Position> key)
43     {
44       CGUIFontCacheHash<Position> hashGen;
45       CGUIFontCacheKeysMatch<Position> keyMatch;
46       auto range = hashMap.equal_range(hashGen(key));
47       for (auto ret = range.first; ret != range.second; ++ret)
48       {
49         if (keyMatch(ret->second->m_key, key))
50         {
51           return ret;
52         }
53       }
54       return hashMap.end();
55     }
UpdateAgeCGUIFontCacheImpl::EntryList56     void UpdateAge(HashIter it, size_t millis)
57     {
58       auto range = ageMap.equal_range(it->second->m_lastUsedMillis);
59       for (auto ageit = range.first; ageit != range.second; ++ageit)
60       {
61         if (ageit->second == it)
62         {
63           ageMap.erase(ageit);
64           ageMap.insert(typename AgeMap::value_type(millis, it));
65           it->second->m_lastUsedMillis = millis;
66           return;
67         }
68       }
69     }
70 
71     HashMap hashMap;
72     AgeMap ageMap;
73   };
74 
75   EntryList m_list;
76   CGUIFontCache<Position, Value> *m_parent;
77 
78 public:
79 
CGUIFontCacheImpl(CGUIFontCache<Position,Value> * parent)80   explicit CGUIFontCacheImpl(CGUIFontCache<Position, Value>* parent) : m_parent(parent) {}
81   Value &Lookup(Position &pos,
82                 const std::vector<UTILS::Color> &colors, const vecText &text,
83                 uint32_t alignment, float maxPixelWidth,
84                 bool scrolling,
85                 unsigned int nowMillis, bool &dirtyCache);
86   void Flush();
87 };
88 
89 template<class Position, class Value>
~CGUIFontCacheEntry()90 CGUIFontCacheEntry<Position, Value>::~CGUIFontCacheEntry()
91 {
92   delete &m_key.m_colors;
93   delete &m_key.m_text;
94   m_value.clear();
95 }
96 
97 template<class Position, class Value>
Assign(const CGUIFontCacheKey<Position> & key,unsigned int nowMillis)98 void CGUIFontCacheEntry<Position, Value>::Assign(const CGUIFontCacheKey<Position> &key, unsigned int nowMillis)
99 {
100   m_key.m_pos = key.m_pos;
101   m_key.m_colors.assign(key.m_colors.begin(), key.m_colors.end());
102   m_key.m_text.assign(key.m_text.begin(), key.m_text.end());
103   m_key.m_alignment = key.m_alignment;
104   m_key.m_maxPixelWidth = key.m_maxPixelWidth;
105   m_key.m_scrolling = key.m_scrolling;
106   m_matrix = key.m_matrix;
107   m_key.m_scaleX = key.m_scaleX;
108   m_key.m_scaleY = key.m_scaleY;
109   m_lastUsedMillis = nowMillis;
110   m_value.clear();
111 }
112 
113 template<class Position, class Value>
CGUIFontCache(CGUIFontTTF & font)114 CGUIFontCache<Position, Value>::CGUIFontCache(CGUIFontTTF& font)
115   : m_impl(new CGUIFontCacheImpl<Position, Value>(this)), m_font(font)
116 {
117 }
118 
119 template<class Position, class Value>
~CGUIFontCache()120 CGUIFontCache<Position, Value>::~CGUIFontCache()
121 {
122   delete m_impl;
123 }
124 
125 template<class Position, class Value>
Lookup(Position & pos,const std::vector<UTILS::Color> & colors,const vecText & text,uint32_t alignment,float maxPixelWidth,bool scrolling,unsigned int nowMillis,bool & dirtyCache)126 Value &CGUIFontCache<Position, Value>::Lookup(Position &pos,
127                                               const std::vector<UTILS::Color> &colors, const vecText &text,
128                                               uint32_t alignment, float maxPixelWidth,
129                                               bool scrolling,
130                                               unsigned int nowMillis, bool &dirtyCache)
131 {
132   if (m_impl == nullptr)
133     m_impl = new CGUIFontCacheImpl<Position, Value>(this);
134 
135   return m_impl->Lookup(pos, colors, text, alignment, maxPixelWidth, scrolling, nowMillis, dirtyCache);
136 }
137 
138 template<class Position, class Value>
Lookup(Position & pos,const std::vector<UTILS::Color> & colors,const vecText & text,uint32_t alignment,float maxPixelWidth,bool scrolling,unsigned int nowMillis,bool & dirtyCache)139 Value &CGUIFontCacheImpl<Position, Value>::Lookup(Position &pos,
140                                                   const std::vector<UTILS::Color> &colors, const vecText &text,
141                                                   uint32_t alignment, float maxPixelWidth,
142                                                   bool scrolling,
143                                                   unsigned int nowMillis, bool &dirtyCache)
144 {
145   const CGUIFontCacheKey<Position> key(pos,
146                                        const_cast<std::vector<UTILS::Color> &>(colors), const_cast<vecText &>(text),
147                                        alignment, maxPixelWidth,
148                                        scrolling, CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIMatrix(),
149                                        CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX(), CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY());
150 
151   auto i = m_list.FindKey(key);
152   if (i == m_list.hashMap.end())
153   {
154     // Cache miss
155     dirtyCache = true;
156     CGUIFontCacheEntry<Position, Value> *entry = nullptr;
157     if (!m_list.ageMap.empty() && (nowMillis - m_list.ageMap.begin()->first) > FONT_CACHE_TIME_LIMIT)
158     {
159       entry = m_list.ageMap.begin()->second->second;
160       m_list.hashMap.erase(m_list.ageMap.begin()->second);
161       m_list.ageMap.erase(m_list.ageMap.begin());
162     }
163 
164     // add new entry
165     CGUIFontCacheHash<Position> hashgen;
166     if (!entry)
167       entry = new CGUIFontCacheEntry<Position, Value>(*m_parent, key, nowMillis);
168     else
169       entry->Assign(key, nowMillis);
170     return m_list.Insert(hashgen(key), entry)->second->m_value;
171   }
172   else
173   {
174     // Cache hit
175     // Update the translation arguments so that they hold the offset to apply
176     // to the cached values (but only in the dynamic case)
177     pos.UpdateWithOffsets(i->second->m_key.m_pos, scrolling);
178 
179     // Update time in entry and move to the back of the list
180     m_list.UpdateAge(i, nowMillis);
181 
182     dirtyCache = false;
183     return i->second->m_value;
184   }
185 }
186 
187 template<class Position, class Value>
Flush()188 void CGUIFontCache<Position, Value>::Flush()
189 {
190   m_impl->Flush();
191 }
192 
193 template<class Position, class Value>
Flush()194 void CGUIFontCacheImpl<Position, Value>::Flush()
195 {
196   m_list.Flush();
197 }
198 
199 template CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::CGUIFontCache(
200     CGUIFontTTF& font);
201 template CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCache();
202 template CGUIFontCacheEntry<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::~CGUIFontCacheEntry();
203 template CGUIFontCacheStaticValue &CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Lookup(CGUIFontCacheStaticPosition &, const std::vector<UTILS::Color> &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
204 template void CGUIFontCache<CGUIFontCacheStaticPosition, CGUIFontCacheStaticValue>::Flush();
205 
206 template CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::CGUIFontCache(
207     CGUIFontTTF& font);
208 template CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCache();
209 template CGUIFontCacheEntry<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::~CGUIFontCacheEntry();
210 template CGUIFontCacheDynamicValue &CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Lookup(CGUIFontCacheDynamicPosition &, const std::vector<UTILS::Color> &, const vecText &, uint32_t, float, bool, unsigned int, bool &);
211 template void CGUIFontCache<CGUIFontCacheDynamicPosition, CGUIFontCacheDynamicValue>::Flush();
212 
clear()213 void CVertexBuffer::clear()
214 {
215   if (m_font != NULL)
216     m_font->DestroyVertexBuffer(*this);
217 }
218