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