xref: /trunk/main/vcl/inc/graphite_cache.hxx (revision 161f4cd1)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // Description: Classes to cache Graphite Segments to try to improve
25 //              rendering performance.
26 
27 #ifndef GraphiteSegmentCache_h
28 #define GraphiteSegmentCache_h
29 
30 #include <tools/solar.h>
31 #include <rtl/ustring.h>
32 
33 #define GRCACHE_REUSE_VECTORS 1
34 
35 //#include <rope>
36 #include <hash_map>
37 
38 class TextSourceAdaptor;
39 /**
40 * GrSegRecord stores a Graphite Segment and its associated text
41 */
42 class GrSegRecord {
43 public:
44     GrSegRecord(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl);
45 
46     ~GrSegRecord();
47 
48     void reuse(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl);
49 
50     void clearVectors();
51     void clear();
52 #ifdef GRCACHE_REUSE_VECTORS
53     void setGlyphVectors(long nWidth, GraphiteLayout::Glyphs & vGlyphs, std::vector<int> vCharDxs,
54                          std::vector<int> & vChar2Base, std::vector<int> & vGlyph2Char, float fScale)
55     {
56         clearVectors();
57         mnWidth = nWidth;
58         m_fontScale = fScale;
59         mvGlyphs.insert(mvGlyphs.begin(), vGlyphs.begin(), vGlyphs.end());
60         mvCharDxs.insert(mvCharDxs.begin(),vCharDxs.begin(),vCharDxs.end());
61         mvChar2BaseGlyph.insert(mvChar2BaseGlyph.begin(),vChar2Base.begin(),vChar2Base.end());
62         mvGlyph2Char.insert(mvGlyph2Char.begin(),vGlyph2Char.begin(),vGlyph2Char.end());
63     }
64 #endif
65     gr::Segment * getSegment() { return m_seg; }
66     TextSourceAdaptor * getTextSrc() { return m_text; }
67     void unlock() { --m_lockCount; }
68     bool isRtl() const { return mbIsRtl; }
69 #ifdef GRCACHE_REUSE_VECTORS
70     const long & width() const { return mnWidth; }
71     const GraphiteLayout::Glyphs & glyphs() const { return mvGlyphs; }
72     const std::vector<int> & charDxs() const { return mvCharDxs; }
73     const std::vector<int> & char2BaseGlyph() const { return mvChar2BaseGlyph; }
74     const std::vector<int> & glyph2Char() const { return mvGlyph2Char; }
75     float & fontScale() { return m_fontScale; }
76 #endif
77 private:
78     rtl::OUString * m_rope;
79     TextSourceAdaptor * m_text;
80     gr::Segment * m_seg;
81     const xub_Unicode * m_nextKey;
82     const xub_Unicode*  m_pStr;
83     size_t m_startChar;
84     float m_fontScale;
85     long mnWidth;
86     GraphiteLayout::Glyphs mvGlyphs; // glyphs in display order
87     std::vector<int> mvCharDxs; // right hand side x offset of each glyph
88     std::vector<int> mvChar2BaseGlyph;
89     std::vector<int> mvGlyph2Char;
90     bool mbIsRtl;
91     int m_lockCount;
92     friend class GraphiteSegmentCache;
93 };
94 
95 typedef std::hash_map<long, GrSegRecord*, std::hash<long> > GraphiteSegMap;
96 typedef std::hash_multimap<size_t, GrSegRecord*> GraphiteRopeMap;
97 typedef std::pair<GraphiteRopeMap::iterator, GraphiteRopeMap::iterator> GrRMEntry;
98 
99 /**
100 * GraphiteSegmentCache contains the cached Segments for one particular font size
101 */
102 class GraphiteSegmentCache
103 {
104 public:
105   enum {
106     // not really sure what good values are here,
107     // bucket size should be >> cache size
108     SEG_BUCKET_FACTOR = 4,
109     SEG_DEFAULT_CACHE_SIZE = 2047
110   };
111   GraphiteSegmentCache(sal_uInt32 nSegCacheSize)
112     : m_segMap(nSegCacheSize * SEG_BUCKET_FACTOR),
113 	m_nSegCacheSize(nSegCacheSize),
114     m_oldestKey(NULL) {};
115   ~GraphiteSegmentCache()
116   {
117     m_ropeMap.clear();
118     GraphiteSegMap::iterator i = m_segMap.begin();
119     while (i != m_segMap.end())
120     {
121       GrSegRecord *r = i->second;
122       delete r;
123       ++i;
124     }
125     m_segMap.clear();
126   };
127   GrSegRecord * getSegment(ImplLayoutArgs & layoutArgs, bool bIsRtl, int segCharLimit)
128   {
129     GrSegRecord * found = NULL;
130     // try to find a segment starting at correct place, if not, try to find a
131     //  match for the complete buffer
132     GraphiteSegMap::iterator iMap =
133       m_segMap.find(reinterpret_cast<long>(layoutArgs.mpStr +
134                                            layoutArgs.mnMinCharPos));
135     if (iMap != m_segMap.end())
136     {
137       found = iMap->second;
138     }
139     else
140     {
141       iMap = m_segMap.find(reinterpret_cast<long>(layoutArgs.mpStr));
142       if (iMap != m_segMap.end())
143       {
144         found = iMap->second;
145       }
146     }
147     if (found)
148     {
149       if (found->m_seg->startCharacter() <= layoutArgs.mnMinCharPos &&
150           found->m_seg->stopCharacter() >= layoutArgs.mnEndCharPos)
151       {
152         DBG_ASSERT(found && found->m_seg, "null entry in GraphiteSegmentCache");
153         // restore original start character, in case it has changed
154         found->m_seg->setTextSourceOffset(found->m_startChar);
155         // check that characters are the same, at least in the range of
156         // interest
157         // We could use substr and ==, but substr does a copy,
158         // so its probably faster to do it like this
159         for (int i = layoutArgs.mnMinCharPos; i < segCharLimit; i++)
160         {
161           //if (!found->m_rope->match(rtl::OUString(layoutArgs.mpStr[i], layoutArgs.mnLength), i - found->m_seg->startCharacter()))
162           if (found->m_rope->getStr()[i-found->m_seg->startCharacter()] != layoutArgs.mpStr[i])
163             return NULL;
164         }
165         if (found->isRtl() != bIsRtl)
166         {
167             return NULL;
168         }
169         if (found->m_seg->stopCharacter() > layoutArgs.mnEndCharPos &&
170             static_cast<int>(found->char2BaseGlyph().size()) > layoutArgs.mnEndCharPos)
171         {
172             // check that the requested end character isn't mid cluster
173             if (found->char2BaseGlyph()[layoutArgs.mnEndCharPos-layoutArgs.mnMinCharPos] == -1)
174             {
175                 return NULL;
176             }
177         }
178 //        if (found->m_lockCount != 0)
179 //          OutputDebugString("Multple users of SegRecord!");
180         found->m_lockCount++;
181       }
182       else found = NULL;
183     }
184     else
185     {
186       // the pointers aren't the same, but we might still have the same text in a segment
187       // this is expecially needed when editing a large paragraph
188       // each edit changes the pointers, but if we don't reuse any segments it gets very
189       // slow.
190       rtl::OUString * rope = new rtl::OUString(layoutArgs.mpStr + layoutArgs.mnMinCharPos,
191                                          segCharLimit - layoutArgs.mnMinCharPos);
192       if (!rope) return NULL;
193       size_t nHash = (*(rope)).hashCode();
194       GrRMEntry range = m_ropeMap.equal_range(nHash);
195       while (range.first != range.second)
196       {
197         found = range.first->second;
198         if (found->m_lockCount == 0)
199         {
200           if(rope->match(*(found->m_rope)))
201           {
202             // found, but the pointers are all wrong
203             found->m_seg->setTextSourceOffset(layoutArgs.mnMinCharPos);
204             // the switch is done in graphite_layout.cxx
205             //found->m_text->switchLayoutArgs(layoutArgs);
206             found->m_lockCount++;
207             break;
208           }
209           else
210             found = NULL;
211         }
212         else
213           found = NULL;
214         ++(range.first);
215       }
216       delete rope;
217     }
218     return found;
219   };
220   GrSegRecord * cacheSegment(TextSourceAdaptor * adapter, gr::Segment * seg, bool bIsRtl);
221 private:
222   GraphiteSegMap m_segMap;
223   GraphiteRopeMap m_ropeMap;
224   sal_uInt32 m_nSegCacheSize;
225   const xub_Unicode * m_oldestKey;
226   const xub_Unicode * m_prevKey;
227 };
228 
229 typedef std::hash_map<int, GraphiteSegmentCache *, std::hash<int> > GraphiteCacheMap;
230 
231 /**
232 * GraphiteCacheHandler maps a particular font, style, size to a GraphiteSegmentCache
233 */
234 class GraphiteCacheHandler
235 {
236 public:
237   GraphiteCacheHandler() : m_cacheMap(255)
238   {
239 	const char * pEnvCache = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
240 	if (pEnvCache != NULL)
241 	{
242 		int envCacheSize = atoi(pEnvCache);
243 		if (envCacheSize <= 0)
244 			m_nSegCacheSize = GraphiteSegmentCache::SEG_DEFAULT_CACHE_SIZE;
245 		else
246 		{
247 			m_nSegCacheSize = envCacheSize;
248 		}
249 	}
250 	else
251 	{
252 		m_nSegCacheSize = GraphiteSegmentCache::SEG_DEFAULT_CACHE_SIZE;
253 	}
254   };
255   ~GraphiteCacheHandler()
256   {
257     GraphiteCacheMap::iterator i = m_cacheMap.begin();
258     while (i != m_cacheMap.end())
259     {
260       GraphiteSegmentCache *r = i->second;
261       delete r;
262       ++i;
263     }
264     m_cacheMap.clear();
265   };
266 
267   static GraphiteCacheHandler instance;
268 
269   GraphiteSegmentCache * getCache(sal_Int32 & fontHash)
270   {
271     if (m_cacheMap.count(fontHash) > 0)
272     {
273       return m_cacheMap.find(fontHash)->second;
274     }
275     GraphiteSegmentCache *pCache = new GraphiteSegmentCache(m_nSegCacheSize);
276     m_cacheMap[fontHash] = pCache;
277     return pCache;
278   }
279 private:
280   GraphiteCacheMap m_cacheMap;
281   sal_uInt32 m_nSegCacheSize;
282 };
283 
284 #endif
285 
286