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