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