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