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