diff options
author | Norbert Thiebaud <nthiebaud@gmail.com> | 2014-04-07 02:01:39 -0500 |
---|---|---|
committer | Norbert Thiebaud <nthiebaud@gmail.com> | 2014-04-09 16:33:06 +0000 |
commit | b8aa1f23645bbe81093ff436b82a58766f3157af (patch) | |
tree | bdd2146c9a1e5ed0f74946228699380bb4eae03d /vcl | |
parent | 8facc14e88251d9356995d9e8e213084212e88a5 (diff) |
vcl quartz: cache per-run glyphs information
GetNextGlyphs could only deal with 1 glyph at the time
and was recalculing a lot of thing while iterating on the glyphs
This is not just a performance issue.. the notiong of keeping
per run glyphs information will be useful to re-establish the
proper support of glyphs display positionning by SetDXArray()
which today is completely ignored, in favor or letting
CoreText spread the extra free space itself.
Change-Id: Ib267c3e490619b650d4149f4b15b5758802942ba
Reviewed-on: https://gerrit.libreoffice.org/8879
Tested-by: Norbert Thiebaud <nthiebaud@gmail.com>
Reviewed-by: Norbert Thiebaud <nthiebaud@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/quartz/CTRunData.cxx | 90 | ||||
-rw-r--r-- | vcl/quartz/CTRunData.hxx | 42 | ||||
-rw-r--r-- | vcl/quartz/ctlayout.cxx | 269 |
4 files changed, 271 insertions, 131 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index d7c13f184298..487a53b5c3ef 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -349,6 +349,7 @@ vcl_quartz_code= \ vcl/quartz/salvd \ vcl_coretext_code= \ + vcl/quartz/CTRunData \ vcl/quartz/ctfonts \ vcl/quartz/ctlayout \ vcl/quartz/salgdi \ diff --git a/vcl/quartz/CTRunData.cxx b/vcl/quartz/CTRunData.cxx new file mode 100644 index 000000000000..609449bddbd9 --- /dev/null +++ b/vcl/quartz/CTRunData.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/types.h> +#include <cassert> + +#include "premac.h" +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> +#include "postmac.h" + +#include "CTRunData.hxx" + +CTRunData::CTRunData( CTRunRef pRun, int start) + : ownership_flags(0) + , m_StartPos(start) + , m_pRun(pRun) +{ + assert(pRun); + + CFDictionaryRef pRunAttributes = CTRunGetAttributes( m_pRun ); + m_pFont = (CTFontRef)CFDictionaryGetValue( pRunAttributes, kCTFontAttributeName ); + + m_nGlyphs = CTRunGetGlyphCount(m_pRun); + m_EndPos = m_StartPos + m_nGlyphs; + const CFRange aAll = CFRangeMake( 0, m_nGlyphs ); + + m_pAdvances = CTRunGetAdvancesPtr( pRun ); + if( !m_pAdvances ) + { + m_pAdvances = new CGSize[m_nGlyphs]; + ownership_flags |= CTRUNDATA_F_OWN_ADVANCES; + CTRunGetAdvances( pRun, aAll, (CGSize*)m_pAdvances ); + } + + m_pGlyphs = CTRunGetGlyphsPtr( m_pRun ); + if( !m_pGlyphs ) + { + m_pGlyphs = new CGGlyph[m_nGlyphs]; + ownership_flags |= CTRUNDATA_F_OWN_GLYPHS; + CTRunGetGlyphs( pRun, aAll, (CGGlyph*)m_pGlyphs); + } + + m_pStringIndices = CTRunGetStringIndicesPtr( pRun ); + if( !m_pStringIndices ) + { + m_pStringIndices = new CFIndex[m_nGlyphs]; + ownership_flags |= CTRUNDATA_F_OWN_INDICES; + CTRunGetStringIndices( pRun, aAll, (CFIndex*)m_pStringIndices ); + } + + m_pPositions = CTRunGetPositionsPtr( pRun ); + if( !m_pPositions ) + { + m_pPositions = new CGPoint[m_nGlyphs]; + ownership_flags |= CTRUNDATA_F_OWN_POSITIONS; + CTRunGetPositions( pRun, aAll, (CGPoint*)m_pPositions ); + } +} + +CTRunData::~CTRunData() +{ + if(ownership_flags & CTRUNDATA_F_OWN_ADVANCES) + { + delete [] m_pAdvances; + } + + if(ownership_flags & CTRUNDATA_F_OWN_GLYPHS) + { + delete [] m_pGlyphs; + } + + if(ownership_flags & CTRUNDATA_F_OWN_INDICES) + { + delete [] m_pStringIndices; + } + + if(ownership_flags & CTRUNDATA_F_OWN_POSITIONS) + { + delete [] m_pPositions; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/CTRunData.hxx b/vcl/quartz/CTRunData.hxx new file mode 100644 index 000000000000..2fb1912cebc0 --- /dev/null +++ b/vcl/quartz/CTRunData.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifndef CTRunData_Included +#define CTRunData_Included + +#include "premac.h" +#include <CoreGraphics/CoreGraphics.h> +#include <CoreText/CoreText.h> +#include "postmac.h" + +class CTRunData +{ +public: + int ownership_flags; +#define CTRUNDATA_F_OWN_ADVANCES (1<<0) +#define CTRUNDATA_F_OWN_GLYPHS (1<<1) +#define CTRUNDATA_F_OWN_INDICES (1<<2) +#define CTRUNDATA_F_OWN_POSITIONS (1<<3) + + int m_nGlyphs; + int m_StartPos; + int m_EndPos; + CTRunRef m_pRun; + CTFontRef m_pFont; + const CGGlyph* m_pGlyphs; + const CGPoint* m_pPositions; + const CGSize* m_pAdvances; + const CFIndex* m_pStringIndices; + + CTRunData(CTRunRef pRun, int start); + ~CTRunData(void); +}; + +#endif /* NDef CTRunData_Included */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/quartz/ctlayout.cxx b/vcl/quartz/ctlayout.cxx index b8b1b98e6ce0..a9cac895ae1b 100644 --- a/vcl/quartz/ctlayout.cxx +++ b/vcl/quartz/ctlayout.cxx @@ -17,12 +17,15 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <sal/types.h> +#include <boost/ptr_container/ptr_vector.hpp> #include "tools/debug.hxx" #include "ctfonts.hxx" +#include "CTRunData.hxx" -class CTLayout -: public SalLayout + +class CTLayout : public SalLayout { public: explicit CTLayout( const CoreTextStyle* ); @@ -52,6 +55,7 @@ private: void drawCTLine(AquaSalGraphics& rAquaGraphics, CTLineRef ctline, const CoreTextStyle* const pStyle) const; CGPoint GetTextDrawPosition(void) const; double GetWidth(void) const; + bool CacheGlyphLayout(void) const; const CoreTextStyle* const mpTextStyle; @@ -70,6 +74,10 @@ private: // x-offset relative to layout origin // currently only used in RTL-layouts mutable double mfBaseAdv; + + mutable bool bLayouted; // true if the glyph layout information are cached and current; + mutable boost::ptr_vector<CTRunData> m_vRunData; + }; CTLayout::CTLayout( const CoreTextStyle* pTextStyle ) @@ -81,6 +89,7 @@ CTLayout::CTLayout( const CoreTextStyle* pTextStyle ) , mfTrailingSpaceWidth( 0.0 ) , mfCachedWidth( -1 ) , mfBaseAdv( 0 ) + , bLayouted( false ) { } @@ -94,6 +103,9 @@ CTLayout::~CTLayout() bool CTLayout::LayoutText( ImplLayoutArgs& rArgs ) { + m_vRunData.release(); + bLayouted = false; + if( mpAttrString ) CFRelease( mpAttrString ); mpAttrString = NULL; @@ -226,7 +238,7 @@ void CTLayout::AdjustLayout( ImplLayoutArgs& rArgs ) // any unforeseen side effects. CGPoint CTLayout::GetTextDrawPosition(void) const { - float fPosX, fPosY; + CGFloat fPosX, fPosY; if (mnLayoutFlags & SAL_LAYOUT_RIGHT_ALIGN) { @@ -335,125 +347,123 @@ void CTLayout::DrawText( SalGraphics& rGraphics ) const drawCTLine(rAquaGraphics, mpCTLine, mpTextStyle); } +bool CTLayout::CacheGlyphLayout(void) const // eew! +{ + m_vRunData.release(); + if(!mpCTLine) + { + return false; + } + CFArrayRef aRuns = CTLineGetGlyphRuns( mpCTLine ); + const int nRun = CFArrayGetCount( aRuns ); + int nPos = 0; + + for( int i = 0; i < nRun; ++i ) + { + CTRunRef pRun = (CTRunRef)CFArrayGetValueAtIndex( aRuns, i ); + CTRunData* pRunData = new CTRunData(pRun, nPos); + m_vRunData.push_back(pRunData); + nPos += pRunData->m_nGlyphs; + } + bLayouted = true; + return true; +} + int CTLayout::GetNextGlyphs( int nLen, sal_GlyphId* pOutGlyphIds, Point& rPos, int& nStart, sal_Int32* pGlyphAdvances, int* pCharIndexes, const PhysicalFontFace** pFallbackFonts ) const { if( !mpCTLine ) + { return 0; + } + if(!bLayouted) + { + CacheGlyphLayout(); + } if( nStart < 0 ) // first glyph requested? + { nStart = 0; - nLen = 1; // TODO: handle nLen>1 below - - // prepare to iterate over the glyph runs - int nCount = 0; - int nSubIndex = nStart; - - typedef std::vector<CGGlyph> CGGlyphVector; - typedef std::vector<CGPoint> CGPointVector; - typedef std::vector<CGSize> CGSizeVector; - typedef std::vector<CFIndex> CFIndexVector; - CGGlyphVector aCGGlyphVec; - CGPointVector aCGPointVec; - CGSizeVector aCGSizeVec; - CFIndexVector aCFIndexVec; + } + const PhysicalFontFace* pFallbackFont = NULL; + CTFontRef pFont = NULL; + CTFontDescriptorRef pFontDesc = NULL; + ImplDevFontAttributes rDevFontAttr; - // TODO: iterate over cached layout - CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine ); - const int nRunCount = CFArrayGetCount( aGlyphRuns ); + boost::ptr_vector<CTRunData>::const_iterator iter = m_vRunData.begin(); - for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) + while(iter != m_vRunData.end() && iter->m_EndPos <= nStart) { - CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex ); - const CFIndex nGlyphsInRun = CTRunGetGlyphCount( pGlyphRun ); - // skip to the first glyph run of interest - if( nSubIndex >= nGlyphsInRun ) + iter++; + } + if(iter == m_vRunData.end()) + { + return 0; + } + else + { + if( pFallbackFonts ) { - nSubIndex -= nGlyphsInRun; - continue; + pFont = (CTFontRef)CFDictionaryGetValue( mpTextStyle->GetStyleDict(), kCTFontAttributeName ); + pFontDesc = CTFontCopyFontDescriptor( iter->m_pFont ); + rDevFontAttr = DevFontFromCTFontDescriptor( pFontDesc, NULL ); } - const CFRange aFullRange = CFRangeMake( 0, nGlyphsInRun ); - - // get glyph run details - const CGGlyph* pCGGlyphIdx = CTRunGetGlyphsPtr( pGlyphRun ); - if( !pCGGlyphIdx ) + } + int i = nStart; + int count = 0; + while( i < nStart + nLen ) + { + // convert glyph details for VCL + int j = i - iter->m_StartPos; + *(pOutGlyphIds++) = iter->m_pGlyphs[ j ]; + if( pGlyphAdvances ) { - aCGGlyphVec.reserve( nGlyphsInRun ); - CTRunGetGlyphs( pGlyphRun, aFullRange, &aCGGlyphVec[0] ); - pCGGlyphIdx = &aCGGlyphVec[0]; + *(pGlyphAdvances++) = lrint(iter->m_pAdvances[ j ].width); } - const CGPoint* pCGGlyphPos = CTRunGetPositionsPtr( pGlyphRun ); - if( !pCGGlyphPos ) + if( pCharIndexes ) { - aCGPointVec.reserve( nGlyphsInRun ); - CTRunGetPositions( pGlyphRun, aFullRange, &aCGPointVec[0] ); - pCGGlyphPos = &aCGPointVec[0]; + *(pCharIndexes++) = iter->m_pStringIndices[ j ] + mnMinCharPos; } - - const CGSize* pCGGlyphAdvs = NULL; - if( pGlyphAdvances) + if( pFallbackFonts ) { - pCGGlyphAdvs = CTRunGetAdvancesPtr( pGlyphRun ); - if( !pCGGlyphAdvs) + if ( !CFEqual( iter->m_pFont, pFont ) ) { - aCGSizeVec.reserve( nGlyphsInRun ); - CTRunGetAdvances( pGlyphRun, aFullRange, &aCGSizeVec[0] ); - pCGGlyphAdvs = &aCGSizeVec[0]; + pFallbackFont = new CoreTextFontData( rDevFontAttr, (sal_IntPtr)pFontDesc ); + *(pFallbackFonts++) = pFallbackFont; } - } - - const CFIndex* pCGGlyphStrIdx = NULL; - if( pCharIndexes) - { - pCGGlyphStrIdx = CTRunGetStringIndicesPtr( pGlyphRun ); - if( !pCGGlyphStrIdx) + else { - aCFIndexVec.reserve( nGlyphsInRun ); - CTRunGetStringIndices( pGlyphRun, aFullRange, &aCFIndexVec[0] ); - pCGGlyphStrIdx = &aCFIndexVec[0]; + *(pFallbackFonts++) = NULL; } } - - const PhysicalFontFace* pFallbackFont = NULL; - if( pFallbackFonts ) + if( i == nStart ) { - CFDictionaryRef pRunAttributes = CTRunGetAttributes( pGlyphRun ); - CTFontRef pRunFont = (CTFontRef)CFDictionaryGetValue( pRunAttributes, kCTFontAttributeName ); - - CFDictionaryRef pAttributes = mpTextStyle->GetStyleDict(); - CTFontRef pFont = (CTFontRef)CFDictionaryGetValue( pAttributes, kCTFontAttributeName ); - if ( !CFEqual( pRunFont, pFont ) ) - { - CTFontDescriptorRef pFontDesc = CTFontCopyFontDescriptor( pRunFont ); - ImplDevFontAttributes rDevFontAttr = DevFontFromCTFontDescriptor( pFontDesc, NULL ); - pFallbackFont = new CoreTextFontData( rDevFontAttr, (sal_IntPtr)pFontDesc ); - } + const CGPoint& rFirstPos = iter->m_pPositions[ j ]; + rPos = GetDrawPosition( Point( rFirstPos.x, rFirstPos.y) ); } - - // get the details for each interesting glyph - // TODO: handle nLen>1 - for(; (--nLen >= 0) && (nSubIndex < nGlyphsInRun); ++nSubIndex, ++nStart ) + i += 1; + count += 1; + if(i == iter->m_EndPos) { - // convert glyph details for VCL - *(pOutGlyphIds++) = pCGGlyphIdx[ nSubIndex ]; - if( pGlyphAdvances ) - *(pGlyphAdvances++) = lrint(pCGGlyphAdvs[ nSubIndex ].width); - if( pCharIndexes ) - *(pCharIndexes++) = pCGGlyphStrIdx[ nSubIndex] + mnMinCharPos; + // note: we assume that we do not have empty runs in the middle of things + ++iter; + if( iter == m_vRunData.end()) + { + break; + } if( pFallbackFonts ) - *(pFallbackFonts++) = pFallbackFont; - if( !nCount++ ) { - const CGPoint& rCurPos = pCGGlyphPos[ nSubIndex ]; - rPos = GetDrawPosition( Point( rCurPos.x, rCurPos.y) ); + pFont = (CTFontRef)CFDictionaryGetValue( mpTextStyle->GetStyleDict(), kCTFontAttributeName ); + pFontDesc = CTFontCopyFontDescriptor( iter->m_pFont ); + rDevFontAttr = DevFontFromCTFontDescriptor( pFontDesc, NULL ); } } - nSubIndex = 0; // prepare for the next glyph run - break; // TODO: handle nLen>1 } + nStart = i; + + return count; - return nCount; } double CTLayout::GetWidth() const @@ -476,60 +486,57 @@ long CTLayout::GetTextWidth() const long CTLayout::FillDXArray( sal_Int32* pDXArray ) const { + long nPixWidth = GetTextWidth(); // short circuit requests which don't need full details if( !pDXArray ) - return GetTextWidth(); + return nPixWidth; - long nPixWidth = GetTextWidth(); - if( pDXArray ) + // prepare the sub-pixel accurate logical-width array + ::std::vector<double> aWidthVector( mnCharCount ); + if( mnTrailingSpaceCount && (mfTrailingSpaceWidth > 0.0) ) { - // prepare the sub-pixel accurate logical-width array - ::std::vector<float> aWidthVector( mnCharCount ); - if( mnTrailingSpaceCount && (mfTrailingSpaceWidth > 0.0) ) - { - const double fOneWidth = mfTrailingSpaceWidth / mnTrailingSpaceCount; - ::std::fill_n(aWidthVector.begin() + (mnCharCount - mnTrailingSpaceCount), - mnTrailingSpaceCount, - fOneWidth); - } - - // handle each glyph run - CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine ); - const int nRunCount = CFArrayGetCount( aGlyphRuns ); - typedef std::vector<CGSize> CGSizeVector; - CGSizeVector aSizeVec; - typedef std::vector<CFIndex> CFIndexVector; - CFIndexVector aIndexVec; + const double fOneWidth = mfTrailingSpaceWidth / mnTrailingSpaceCount; + ::std::fill_n(aWidthVector.begin() + (mnCharCount - mnTrailingSpaceCount), + mnTrailingSpaceCount, + fOneWidth); + } - for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) - { - CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex ); - const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun ); - const CFRange aFullRange = CFRangeMake( 0, nGlyphCount ); + // handle each glyph run + CFArrayRef aGlyphRuns = CTLineGetGlyphRuns( mpCTLine ); + const int nRunCount = CFArrayGetCount( aGlyphRuns ); + typedef std::vector<CGSize> CGSizeVector; + CGSizeVector aSizeVec; + typedef std::vector<CFIndex> CFIndexVector; + CFIndexVector aIndexVec; - aSizeVec.resize( nGlyphCount ); - aIndexVec.resize( nGlyphCount ); - CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] ); - CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] ); + for( int nRunIndex = 0; nRunIndex < nRunCount; ++nRunIndex ) + { + CTRunRef pGlyphRun = (CTRunRef)CFArrayGetValueAtIndex( aGlyphRuns, nRunIndex ); + const CFIndex nGlyphCount = CTRunGetGlyphCount( pGlyphRun ); + const CFRange aFullRange = CFRangeMake( 0, nGlyphCount ); - for( int i = 0; i != nGlyphCount; ++i ) - { - const int nRelIdx = aIndexVec[i]; - aWidthVector[nRelIdx] += aSizeVec[i].width; - } - } + aSizeVec.resize( nGlyphCount ); + aIndexVec.resize( nGlyphCount ); + CTRunGetAdvances( pGlyphRun, aFullRange, &aSizeVec[0] ); + CTRunGetStringIndices( pGlyphRun, aFullRange, &aIndexVec[0] ); - // convert the sub-pixel accurate array into classic pDXArray integers - float fWidthSum = 0.0; - sal_Int32 nOldDX = 0; - for( int i = 0; i < mnCharCount; ++i) + for( int i = 0; i != nGlyphCount; ++i ) { - const sal_Int32 nNewDX = rint( fWidthSum += aWidthVector[i]); - pDXArray[i] = nNewDX - nOldDX; - nOldDX = nNewDX; + const int nRelIdx = aIndexVec[i]; + aWidthVector[nRelIdx] += aSizeVec[i].width; } } + // convert the sub-pixel accurate array into classic pDXArray integers + double fWidthSum = 0.0; + sal_Int32 nOldDX = 0; + for( int i = 0; i < mnCharCount; ++i) + { + const sal_Int32 nNewDX = rint( fWidthSum += aWidthVector[i]); + pDXArray[i] = nNewDX - nOldDX; + nOldDX = nNewDX; + } + return nPixWidth; } |