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