summaryrefslogtreecommitdiff
path: root/vcl/inc/graphite_cache.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/inc/graphite_cache.hxx')
-rw-r--r--vcl/inc/graphite_cache.hxx289
1 files changed, 289 insertions, 0 deletions
diff --git a/vcl/inc/graphite_cache.hxx b/vcl/inc/graphite_cache.hxx
new file mode 100644
index 000000000000..af1392ed4d4b
--- /dev/null
+++ b/vcl/inc/graphite_cache.hxx
@@ -0,0 +1,289 @@
+/*************************************************************************
+ *
+ * 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
+ * <http://www.openoffice.org/license.html>
+ * 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 <tools/solar.h>
+#include <rtl/ustring.h>
+
+#define GRCACHE_REUSE_VECTORS 1
+
+//#include <rope>
+#include <hash_map>
+
+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<int> vCharDxs,
+ std::vector<int> & vChar2Base, std::vector<int> & 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<int> & charDxs() const { return mvCharDxs; }
+ const std::vector<int> & char2BaseGlyph() const { return mvChar2BaseGlyph; }
+ const std::vector<int> & 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<int> mvCharDxs; // right hand side x offset of each glyph
+ std::vector<int> mvChar2BaseGlyph;
+ std::vector<int> mvGlyph2Char;
+ bool mbIsRtl;
+ int m_lockCount;
+ friend class GraphiteSegmentCache;
+};
+
+typedef std::hash_map<long, GrSegRecord*, std::hash<long> > GraphiteSegMap;
+typedef std::hash_multimap<size_t, GrSegRecord*> GraphiteRopeMap;
+typedef std::pair<GraphiteRopeMap::iterator, GraphiteRopeMap::iterator> GrRMEntry;
+
+/**
+* GraphiteSegmentCache contains the cached Segments for one particular font size
+*/
+class GraphiteSegmentCache
+{
+public:
+ enum {
+ // not really sure what good values are here,
+ // bucket size should be >> cache size
+ SEG_BUCKET_FACTOR = 4,
+ SEG_DEFAULT_CACHE_SIZE = 2047
+ };
+ GraphiteSegmentCache(sal_uInt32 nSegCacheSize)
+ : m_segMap(nSegCacheSize * SEG_BUCKET_FACTOR),
+ m_nSegCacheSize(nSegCacheSize),
+ 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<long>(layoutArgs.mpStr +
+ layoutArgs.mnMinCharPos));
+ if (iMap != m_segMap.end())
+ {
+ found = iMap->second;
+ }
+ else
+ {
+ iMap = m_segMap.find(reinterpret_cast<long>(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<int>(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;
+ sal_uInt32 m_nSegCacheSize;
+ const xub_Unicode * m_oldestKey;
+ const xub_Unicode * m_prevKey;
+};
+
+typedef std::hash_map<int, GraphiteSegmentCache *, std::hash<int> > GraphiteCacheMap;
+
+/**
+* GraphiteCacheHandler maps a particular font, style, size to a GraphiteSegmentCache
+*/
+class GraphiteCacheHandler
+{
+public:
+ GraphiteCacheHandler() : m_cacheMap(255)
+ {
+ const char * pEnvCache = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
+ if (pEnvCache != NULL)
+ {
+ int envCacheSize = atoi(pEnvCache);
+ if (envCacheSize <= 0)
+ m_nSegCacheSize = GraphiteSegmentCache::SEG_DEFAULT_CACHE_SIZE;
+ else
+ {
+ m_nSegCacheSize = envCacheSize;
+ }
+ }
+ else
+ {
+ m_nSegCacheSize = GraphiteSegmentCache::SEG_DEFAULT_CACHE_SIZE;
+ }
+ };
+ ~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_nSegCacheSize);
+ m_cacheMap[fontHash] = pCache;
+ return pCache;
+ }
+private:
+ GraphiteCacheMap m_cacheMap;
+ sal_uInt32 m_nSegCacheSize;
+};
+
+#endif
+