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