/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // Description: Classes to cache Graphite Segments to try to improve // rendering performance. #ifndef GraphiteSegmentCache_h #define GraphiteSegmentCache_h #include #include #define GRCACHE_REUSE_VECTORS 1 //#include #include class TextSourceAdaptor; /** * GrSegRecord stores a Graphite Segment and its associated text */ class GrSegRecord { public: GrSegRecord(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl); ~GrSegRecord(); void reuse(rtl::OUString * rope, TextSourceAdaptor * textSrc, gr::Segment * seg, bool bIsRtl); void clearVectors(); void clear(); #ifdef GRCACHE_REUSE_VECTORS void setGlyphVectors(long nWidth, GraphiteLayout::Glyphs & vGlyphs, std::vector vCharDxs, std::vector & vChar2Base, std::vector & vGlyph2Char, float fScale) { clearVectors(); mnWidth = nWidth; m_fontScale = fScale; mvGlyphs.insert(mvGlyphs.begin(), vGlyphs.begin(), vGlyphs.end()); mvCharDxs.insert(mvCharDxs.begin(),vCharDxs.begin(),vCharDxs.end()); mvChar2BaseGlyph.insert(mvChar2BaseGlyph.begin(),vChar2Base.begin(),vChar2Base.end()); mvGlyph2Char.insert(mvGlyph2Char.begin(),vGlyph2Char.begin(),vGlyph2Char.end()); } #endif gr::Segment * getSegment() { return m_seg; } TextSourceAdaptor * getTextSrc() { return m_text; } void unlock() { --m_lockCount; } bool isRtl() const { return mbIsRtl; } #ifdef GRCACHE_REUSE_VECTORS const long & width() const { return mnWidth; } const GraphiteLayout::Glyphs & glyphs() const { return mvGlyphs; } const std::vector & charDxs() const { return mvCharDxs; } const std::vector & char2BaseGlyph() const { return mvChar2BaseGlyph; } const std::vector & glyph2Char() const { return mvGlyph2Char; } float & fontScale() { return m_fontScale; } #endif private: rtl::OUString * m_rope; TextSourceAdaptor * m_text; gr::Segment * m_seg; const xub_Unicode * m_nextKey; const xub_Unicode* m_pStr; size_t m_startChar; float m_fontScale; long mnWidth; GraphiteLayout::Glyphs mvGlyphs; // glyphs in display order std::vector mvCharDxs; // right hand side x offset of each glyph std::vector mvChar2BaseGlyph; std::vector mvGlyph2Char; bool mbIsRtl; int m_lockCount; friend class GraphiteSegmentCache; }; typedef std::hash_map > GraphiteSegMap; typedef std::hash_multimap GraphiteRopeMap; typedef std::pair GrRMEntry; /** * GraphiteSegmentCache contains the cached Segments for one particular font size */ class GraphiteSegmentCache { enum { // not really sure what good values are here, // bucket size should be >> cache size SEG_BUCKET_SIZE = 4096, SEG_CACHE_SIZE = 255 }; public: GraphiteSegmentCache() : m_segMap(SEG_BUCKET_SIZE), m_oldestKey(NULL) {}; ~GraphiteSegmentCache() { m_ropeMap.clear(); GraphiteSegMap::iterator i = m_segMap.begin(); while (i != m_segMap.end()) { GrSegRecord *r = i->second; delete r; ++i; } m_segMap.clear(); }; GrSegRecord * getSegment(ImplLayoutArgs & layoutArgs, bool bIsRtl, int segCharLimit) { GrSegRecord * found = NULL; // try to find a segment starting at correct place, if not, try to find a // match for the complete buffer GraphiteSegMap::iterator iMap = m_segMap.find(reinterpret_cast(layoutArgs.mpStr + layoutArgs.mnMinCharPos)); if (iMap != m_segMap.end()) { found = iMap->second; } else { iMap = m_segMap.find(reinterpret_cast(layoutArgs.mpStr)); if (iMap != m_segMap.end()) { found = iMap->second; } } if (found) { if (found->m_seg->startCharacter() <= layoutArgs.mnMinCharPos && found->m_seg->stopCharacter() >= layoutArgs.mnEndCharPos) { DBG_ASSERT(found && found->m_seg, "null entry in GraphiteSegmentCache"); // restore original start character, in case it has changed found->m_seg->setTextSourceOffset(found->m_startChar); // check that characters are the same, at least in the range of // interest // We could use substr and ==, but substr does a copy, // so its probably faster to do it like this for (int i = layoutArgs.mnMinCharPos; i < segCharLimit; i++) { //if (!found->m_rope->match(rtl::OUString(layoutArgs.mpStr[i], layoutArgs.mnLength), i - found->m_seg->startCharacter())) if (found->m_rope->getStr()[i-found->m_seg->startCharacter()] != layoutArgs.mpStr[i]) return NULL; } if (found->isRtl() != bIsRtl) { return NULL; } if (found->m_seg->stopCharacter() > layoutArgs.mnEndCharPos && static_cast(found->char2BaseGlyph().size()) > layoutArgs.mnEndCharPos) { // check that the requested end character isn't mid cluster if (found->char2BaseGlyph()[layoutArgs.mnEndCharPos-layoutArgs.mnMinCharPos] == -1) { return NULL; } } // if (found->m_lockCount != 0) // OutputDebugString("Multple users of SegRecord!"); found->m_lockCount++; } else found = NULL; } else { // the pointers aren't the same, but we might still have the same text in a segment // this is expecially needed when editing a large paragraph // each edit changes the pointers, but if we don't reuse any segments it gets very // slow. rtl::OUString * rope = new rtl::OUString(layoutArgs.mpStr + layoutArgs.mnMinCharPos, segCharLimit - layoutArgs.mnMinCharPos); if (!rope) return NULL; size_t nHash = (*(rope)).hashCode(); GrRMEntry range = m_ropeMap.equal_range(nHash); while (range.first != range.second) { found = range.first->second; if (found->m_lockCount == 0) { if(rope->match(*(found->m_rope))) { // found, but the pointers are all wrong found->m_seg->setTextSourceOffset(layoutArgs.mnMinCharPos); // the switch is done in graphite_layout.cxx //found->m_text->switchLayoutArgs(layoutArgs); found->m_lockCount++; break; } else found = NULL; } else found = NULL; ++(range.first); } delete rope; } return found; }; GrSegRecord * cacheSegment(TextSourceAdaptor * adapter, gr::Segment * seg, bool bIsRtl); private: GraphiteSegMap m_segMap; GraphiteRopeMap m_ropeMap; const xub_Unicode * m_oldestKey; const xub_Unicode * m_prevKey; }; typedef std::hash_map > GraphiteCacheMap; /** * GraphiteCacheHandler maps a particular font, style, size to a GraphiteSegmentCache */ class GraphiteCacheHandler { public: GraphiteCacheHandler() : m_cacheMap(255) {}; ~GraphiteCacheHandler() { GraphiteCacheMap::iterator i = m_cacheMap.begin(); while (i != m_cacheMap.end()) { GraphiteSegmentCache *r = i->second; delete r; ++i; } m_cacheMap.clear(); }; static GraphiteCacheHandler instance; GraphiteSegmentCache * getCache(sal_Int32 & fontHash) { if (m_cacheMap.count(fontHash) > 0) { return m_cacheMap.find(fontHash)->second; } GraphiteSegmentCache *pCache = new GraphiteSegmentCache(); m_cacheMap[fontHash] = pCache; return pCache; } private: GraphiteCacheMap m_cacheMap; }; #endif