[Libreoffice-commits] core.git: Branch 'feature/fixes14' - vcl/inc vcl/win
Tor Lillqvist
tml at collabora.com
Fri Jan 15 03:39:02 PST 2016
vcl/inc/win/salgdi.h | 12
vcl/win/source/gdi/salgdi.cxx | 5
vcl/win/source/gdi/salgdi3.cxx | 40 +
vcl/win/source/gdi/winlayout.cxx | 944 +++++++++++++++++++++++++++++++++++++--
vcl/win/source/gdi/winlayout.hxx | 45 +
5 files changed, 1020 insertions(+), 26 deletions(-)
New commits:
commit 061faf9d40503d7410224001be5c962a724e26ed
Author: Tor Lillqvist <tml at collabora.com>
Date: Thu Jan 7 15:05:58 2016 +0200
tdf#96420: Re-introduce SimpleWinLayout
Should fix lots of the problems with glyph layout. The UniscribeLayout
class turned out to be much less correct that one might have hoped.
Use OpenGL glyph caching also for SimpleWinLayout to avoid performance
decrease.
Change-Id: Ie3c00304ce58f84c7f54051f4c230b32bfc47d9f
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 4ff01fa..3d0046e 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -187,6 +187,7 @@ class WinSalGraphics : public SalGraphics
friend class ScopedFont;
friend class OpenGLCompatibleDC;
friend class WinLayout;
+ friend class SimpleWinLayout;
friend class UniscribeLayout;
protected:
@@ -214,6 +215,9 @@ private:
RGNDATA* mpClipRgnData; // ClipRegion-Data
RGNDATA* mpStdClipRgnData; // Cache Standard-ClipRegion-Data
ImplFontAttrCache* mpFontAttrCache; // Cache font attributes from files in so/share/fonts
+ bool mbFontKernInit; // FALSE: FontKerns must be queried
+ KERNINGPAIR* mpFontKernPairs; // Kerning Pairs of the current Font
+ sal_uIntPtr mnFontKernPairCount;// Number of Kerning Pairs of the current Font
int mnPenWidth; // Linienbreite
public:
@@ -330,6 +334,12 @@ protected:
const SalBitmap* pAlphaBitmap) override;
virtual bool drawAlphaRect( long nX, long nY, long nWidth, long nHeight, sal_uInt8 nTransparency ) override;
+private:
+ // local helpers
+
+ // get kernign pairs of the current font
+ sal_uLong GetKernPairs();
+
public:
// public SalGraphics methods, the interface to the independent vcl part
@@ -451,7 +461,7 @@ void ImplGetLogFontFromFontSelect( HDC, const FontSelectPattern*,
#define MAX_64KSALPOINTS ((((sal_uInt16)0xFFFF)-8)/sizeof(POINTS))
// #102411# Win's GCP mishandles kerning => we need to do it ourselves
-// kerning pairs is sorted by
+// SalGraphicsData::mpFontKernPairs is sorted by
inline bool ImplCmpKernData( const KERNINGPAIR& a, const KERNINGPAIR& b )
{
if( a.wFirst < b.wFirst )
diff --git a/vcl/win/source/gdi/salgdi.cxx b/vcl/win/source/gdi/salgdi.cxx
index 5f29820..42ab90e 100644
--- a/vcl/win/source/gdi/salgdi.cxx
+++ b/vcl/win/source/gdi/salgdi.cxx
@@ -631,6 +631,9 @@ WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hW
mhDefPal(0),
mpStdClipRgnData(NULL),
mpFontAttrCache(NULL),
+ mpFontKernPairs(NULL),
+ mnFontKernPairCount(0),
+ mbFontKernInit(false),
mnPenWidth(GSL_PEN_WIDTH)
{
if (OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter)
@@ -660,6 +663,8 @@ WinSalGraphics::~WinSalGraphics()
// delete cache data
delete [] (BYTE*)mpStdClipRgnData;
+
+ delete [] mpFontKernPairs;
}
SalGraphicsImpl* WinSalGraphics::GetImpl() const
diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx
index 9412799..4de6530 100644
--- a/vcl/win/source/gdi/salgdi3.cxx
+++ b/vcl/win/source/gdi/salgdi3.cxx
@@ -1559,6 +1559,17 @@ sal_uInt16 WinSalGraphics::SetFont( FontSelectPattern* pFont, int nFallbackLevel
if( mpWinFontData[ nFallbackLevel ] )
mpWinFontData[ nFallbackLevel ]->UpdateFromHDC( getHDC() );
+ if( !nFallbackLevel )
+ {
+ mbFontKernInit = TRUE;
+ if ( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+ }
+
// some printers have higher internal resolution, so their
// text output would be different from what we calculated
// => suggest DrawTextArray to workaround this problem
@@ -1645,6 +1656,35 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricData* pMetric, int nFallbackLe
pMetric->mnMinKashida = GetMinKashidaWidth();
}
+sal_uLong WinSalGraphics::GetKernPairs()
+{
+ if ( mbFontKernInit )
+ {
+ if( mpFontKernPairs )
+ {
+ delete[] mpFontKernPairs;
+ mpFontKernPairs = NULL;
+ }
+ mnFontKernPairCount = 0;
+
+ KERNINGPAIR* pPairs = NULL;
+ int nCount = ::GetKerningPairsW( getHDC(), 0, NULL );
+ if( nCount )
+ {
+ pPairs = new KERNINGPAIR[ nCount+1 ];
+ mpFontKernPairs = pPairs;
+ mnFontKernPairCount = nCount;
+ ::GetKerningPairsW( getHDC(), nCount, pPairs );
+ }
+
+ mbFontKernInit = FALSE;
+
+ std::sort( mpFontKernPairs, mpFontKernPairs + mnFontKernPairCount, ImplCmpKernData );
+ }
+
+ return mnFontKernPairCount;
+}
+
const FontCharMapPtr WinSalGraphics::GetFontCharMap() const
{
if( !mpWinFontData[0] )
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
index cef4f8a..f1c2203 100644
--- a/vcl/win/source/gdi/winlayout.cxx
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -47,6 +47,8 @@
#include <unordered_map>
+typedef std::unordered_map<int,int> IntMap;
+
// Graphite headers
#include <config_graphite.h>
#if ENABLE_GRAPHITE
@@ -73,6 +75,7 @@ struct OpenGLGlyphCacheChunk
int mnAscent;
int mnHeight;
bool mbVertical;
+ bool mbRealGlyphIndices;
int getExtraSpace() const
{
@@ -97,6 +100,15 @@ private:
// TODO: also add HFONT??? Watch out for issues with too many active fonts...
public:
+ bool HasKernData() const;
+ void SetKernData( int, const KERNINGPAIR* );
+ int GetKerning( sal_Unicode, sal_Unicode ) const;
+
+private:
+ KERNINGPAIR* mpKerningPairs;
+ int mnKerningPairs;
+
+public:
SCRIPT_CACHE& GetScriptCache() const
{ return maScriptCache; }
private:
@@ -104,6 +116,9 @@ private:
std::vector<OpenGLGlyphCacheChunk> maOpenGLGlyphCache;
public:
+ int GetCachedGlyphWidth( int nCharCode ) const;
+ void CacheGlyphWidth( int nCharCode, int nCharWidth );
+
bool InitKashidaHandling( HDC );
int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
@@ -113,10 +128,11 @@ public:
GLyphyDemo::demo_font_t* mpGLyphyFont;
bool GlyphIsCached(int nGlyphIndex) const;
- bool AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
+ bool AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
const OpenGLGlyphCacheChunk& GetCachedGlyphChunkFor(int nGlyphIndex) const;
private:
+ IntMap maWidthMap;
mutable int mnMinKashidaWidth;
mutable int mnMinKashidaGlyph;
bool mbGLyphySetupCalled;
@@ -192,6 +208,19 @@ inline std::basic_ostream<charT, traits> & operator <<(
return stream << "}";
}
+inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+ maWidthMap[ nCharCode ] = nCharWidth;
+}
+
+inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
+{
+ IntMap::const_iterator it = maWidthMap.find( nCharCode );
+ if( it == maWidthMap.end() )
+ return -1;
+ return it->second;
+}
+
bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
{
if (nGlyphIndex == DROPPED_OUTGLYPH)
@@ -205,7 +234,7 @@ bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
return false;
}
-bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
+bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
{
const int DEFAULT_CHUNK_SIZE = 20;
@@ -241,6 +270,7 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
OpenGLGlyphCacheChunk aChunk;
aChunk.mnFirstGlyph = nGlyphIndex;
aChunk.mnGlyphCount = nCount;
+ aChunk.mbRealGlyphIndices = bRealGlyphIndices;
std::vector<WORD> aGlyphIndices(nCount);
for (int i = 0; i < nCount; i++)
@@ -262,21 +292,40 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
SIZE aSize;
- if (!GetTextExtentExPointI(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+ std::vector<ABC> aABC(nCount);
+ if (bRealGlyphIndices)
{
- SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
- SelectObject(hDC, hOrigFont);
- DeleteDC(hDC);
- return false;
+ if (!GetTextExtentExPointI(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+ {
+ SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
+ SelectObject(hDC, hOrigFont);
+ DeleteDC(hDC);
+ return false;
+ }
+ if (!GetCharABCWidthsI(hDC, 0, nCount, aGlyphIndices.data(), aABC.data()))
+ {
+ SAL_WARN("vcl.gdi", "GetCharABCWidthsI failed: " << WindowsErrorString(GetLastError()));
+ SelectObject(hDC, hOrigFont);
+ DeleteDC(hDC);
+ return false;
+ }
}
-
- std::vector<ABC> aABC(nCount);
- if (!GetCharABCWidthsI(hDC, 0, nCount, aGlyphIndices.data(), aABC.data()))
+ else
{
- SAL_WARN("vcl.gdi", "GetCharABCWidthsI failed: " << WindowsErrorString(GetLastError()));
- SelectObject(hDC, hOrigFont);
- DeleteDC(hDC);
- return false;
+ if (!GetTextExtentExPointW(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
+ {
+ SAL_WARN("vcl.gdi", "GetTextExtentExPoint failed: " << WindowsErrorString(GetLastError()));
+ SelectObject(hDC, hOrigFont);
+ DeleteDC(hDC);
+ return false;
+ }
+ if (!GetCharABCWidths(hDC, nGlyphIndex, nGlyphIndex+nCount-1, aABC.data()))
+ {
+ SAL_WARN("vcl.gdi", "GetCharABCWidths failed: " << WindowsErrorString(GetLastError()));
+ SelectObject(hDC, hOrigFont);
+ DeleteDC(hDC);
+ return false;
+ }
}
std::ostringstream sLine;
@@ -396,7 +445,12 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
int nX = nY;
if (aChunk.mbVertical)
nX += aDX[0];
- if (!ExtTextOutW(aDC.getCompatibleHDC(), nX, nY, ETO_GLYPH_INDEX, NULL, aGlyphIndices.data(), nCount, aDX.data()))
+ if (!ExtTextOutW(aDC.getCompatibleHDC(),
+ nX, nY,
+ bRealGlyphIndices ? ETO_GLYPH_INDEX : 0,
+ NULL,
+ aGlyphIndices.data(), nCount,
+ aDX.data()))
{
SAL_WARN("vcl.gdi", "ExtTextOutW failed: " << WindowsErrorString(GetLastError()));
SelectFont(aDC.getCompatibleHDC(), hOrigFont);
@@ -443,6 +497,705 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayou
return true;
}
+SimpleWinLayout::SimpleWinLayout(HDC hDC, BYTE nCharSet, const ImplWinFontData& rWinFontData,
+ ImplWinFontEntry& rWinFontEntry, bool bUseOpenGL)
+: WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL),
+ mnGlyphCount( 0 ),
+ mnCharCount( 0 ),
+ mpOutGlyphs( NULL ),
+ mpGlyphAdvances( NULL ),
+ mpGlyphOrigAdvs( NULL ),
+ mpCharWidths( NULL ),
+ mpChars2Glyphs( NULL ),
+ mpGlyphs2Chars( NULL ),
+ mpGlyphRTLFlags( NULL ),
+ mnWidth( 0 ),
+ mnNotdefWidth( -1 ),
+ mnCharSet( nCharSet )
+{
+}
+
+SimpleWinLayout::~SimpleWinLayout()
+{
+ delete[] mpGlyphRTLFlags;
+ delete[] mpGlyphs2Chars;
+ delete[] mpChars2Glyphs;
+ if( mpCharWidths != mpGlyphAdvances )
+ delete[] mpCharWidths;
+ delete[] mpGlyphOrigAdvs;
+ delete[] mpGlyphAdvances;
+ delete[] mpOutGlyphs;
+}
+
+bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ // prepare layout
+ // TODO: fix case when recyclying old SimpleWinLayout object
+ mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
+
+ // TODO: use a cached value for bDisableAsianKern from upper layers
+ if( rArgs.mnFlags & SalLayoutFlags::KerningAsian )
+ {
+ TEXTMETRICA aTextMetricA;
+ if( GetTextMetricsA( mhDC, &aTextMetricA )
+ && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
+ rArgs.mnFlags &= ~SalLayoutFlags::KerningAsian;
+ }
+
+ // layout text
+ int i, j;
+
+ mnGlyphCount = 0;
+ bool bVertical(rArgs.mnFlags & SalLayoutFlags::Vertical);
+
+ // count the number of chars to process if no RTL run
+ rArgs.ResetPos();
+ bool bHasRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
+ mnGlyphCount += j - i;
+
+ // if there are RTL runs we need room to remember individual BiDi flags
+ if( bHasRTL )
+ {
+ mpGlyphRTLFlags = new bool[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpGlyphRTLFlags[i] = false;
+ }
+
+ // rewrite the logical string if needed to prepare for the API calls
+ const sal_Unicode* pBidiStr = rArgs.mrStr.pData->buffer + rArgs.mnMinCharPos;
+ if( (mnGlyphCount != mnCharCount) || bVertical )
+ {
+ // we need to rewrite the pBidiStr when any of
+ // - BiDirectional layout
+ // - vertical layout
+ // - partial runs (e.g. with control chars or for glyph fallback)
+ // are involved
+ sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
+ pBidiStr = pRewrittenStr;
+
+ // note: glyph to char mapping is relative to first character
+ mpChars2Glyphs = new int[ mnCharCount ];
+ mpGlyphs2Chars = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
+
+ mnGlyphCount = 0;
+ rArgs.ResetPos();
+ bool bIsRTL = false;
+ while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
+ {
+ do
+ {
+ // get the next leftmost character in this run
+ int nCharPos = bIsRTL ? --j : i++;
+ sal_UCS4 cChar = rArgs.mrStr[ nCharPos ];
+
+ // in the RTL case mirror the character and remember its RTL status
+ if( bIsRTL )
+ {
+ cChar = GetMirroredChar( cChar );
+ mpGlyphRTLFlags[ mnGlyphCount ] = true;
+ }
+
+ // rewrite the original string
+ // update the mappings between original and rewritten string
+ // TODO: support surrogates in rewritten strings
+ pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
+ mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
+ mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
+ ++mnGlyphCount;
+ } while( i < j );
+ }
+ }
+
+ mpOutGlyphs = new WCHAR[ mnGlyphCount ];
+ mpGlyphAdvances = new int[ mnGlyphCount ];
+
+ if( rArgs.mnFlags & (SalLayoutFlags::KerningPairs | SalLayoutFlags::KerningAsian) )
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpOutGlyphs[i] = pBidiStr[ i ];
+ mnWidth = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ // get the current UCS-4 code point, check for surrogate pairs
+ const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
+ unsigned nCharCode = pCodes[0];
+ bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
+ if( bSurrogate )
+ {
+ // ignore high surrogates, they were already processed with their low surrogates
+ if( nCharCode >= 0xDC00 )
+ continue;
+ // check the second half of the surrogate pair
+ bSurrogate &= (0xDC00 <= pCodes[1]) && (pCodes[1] <= 0xDFFF);
+ // calculate the UTF-32 code of valid surrogate pairs
+ if( bSurrogate )
+ nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
+ else // or fall back to a replacement character
+ nCharCode = '?';
+ }
+
+ // get the advance width for the current UTF-32 code point
+ int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
+ if( nGlyphWidth == -1 )
+ {
+ ABC aABC;
+ SIZE aExtent;
+ if( GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
+ nGlyphWidth = aExtent.cx;
+ else if( GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
+ nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
+ else if( !GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
+ && !GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
+ nGlyphWidth = 0;
+ mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
+ }
+ mpGlyphAdvances[ i ] = nGlyphWidth;
+ mnWidth += nGlyphWidth;
+
+ // the second half of surrogate pair gets a zero width
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpGlyphAdvances[ i+1 ] = 0;
+
+ // check with the font face if glyph fallback is needed
+ if( mrWinFontData.HasChar( nCharCode ) )
+ continue;
+
+ // request glyph fallback at this position in the string
+ bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
+ int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
+ rArgs.NeedFallback( nCharPos, bRTL );
+ if( bSurrogate && ((nCharPos+1) < rArgs.mrStr.getLength()) )
+ rArgs.NeedFallback( nCharPos+1, bRTL );
+
+ // replace the current glyph shape with the NotDef glyph shape
+ if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
+ {
+ // when we already are layouting for glyph fallback
+ // then a new unresolved glyph is not interesting
+ mnNotdefWidth = 0;
+ mpOutGlyphs[i] = DROPPED_OUTGLYPH;
+ }
+ else
+ {
+ if( mnNotdefWidth < 0 )
+ {
+ // get the width of the NotDef glyph
+ SIZE aExtent;
+ WCHAR cNotDef = rArgs.mrStr[ nCharPos ];
+ mnNotdefWidth = 0;
+ if( GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
+ mnNotdefWidth = aExtent.cx;
+ }
+ }
+ if( bSurrogate && ((i+1) < mnGlyphCount) )
+ mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
+
+ // adjust the current glyph width to the NotDef glyph width
+ mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
+ mpGlyphAdvances[i] = mnNotdefWidth;
+ if( mpGlyphOrigAdvs )
+ mpGlyphOrigAdvs[i] = mnNotdefWidth;
+ }
+
+ // apply kerning if the layout engine has not yet done it
+ if( rArgs.mnFlags & (SalLayoutFlags::KerningAsian|SalLayoutFlags::KerningPairs) )
+ {
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
+
+ // #99658# also apply asian kerning on the substring border
+ int nLen = mnGlyphCount;
+ if( rArgs.mnMinCharPos + nLen < rArgs.mrStr.getLength() )
+ ++nLen;
+ for( i = 1; i < nLen; ++i )
+ {
+ if( rArgs.mnFlags & SalLayoutFlags::KerningPairs )
+ {
+ int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
+ mpGlyphAdvances[ i-1 ] += nKernAmount;
+ mnWidth += nKernAmount;
+ }
+ else if( rArgs.mnFlags & SalLayoutFlags::KerningAsian )
+
+ if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
+ && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
+ {
+ long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
+ long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
+
+ long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
+ if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
+ {
+ nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
+ mpGlyphAdvances[i-1] += nDelta;
+ mnWidth += nDelta;
+ }
+ }
+ }
+ }
+
+ // calculate virtual char widths
+ if( !mpGlyphs2Chars )
+ mpCharWidths = mpGlyphAdvances;
+ else
+ {
+ mpCharWidths = new int[ mnCharCount ];
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int k = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( k >= 0 )
+ mpCharWidths[ k ] += mpGlyphAdvances[ i ];
+ }
+ }
+
+ // scale layout metrics if needed
+ // TODO: does it make the code more simple if the metric scaling
+ // is moved to the methods that need metric scaling (e.g. FillDXArray())?
+ if( mfFontScale != 1.0 )
+ {
+ mnWidth = (long)(mnWidth * mfFontScale);
+ mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
+ if( mpGlyphAdvances != mpCharWidths )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
+ if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
+ }
+
+ return true;
+}
+
+int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIds, Point& rPos, int& nStart,
+ DeviceCoordinate* pGlyphAdvances, int* pCharIndexes,
+ const PhysicalFontFace** /*pFallbackFonts*/ ) const
+{
+ // return zero if no more glyph found
+ if( nStart >= mnGlyphCount )
+ return 0;
+
+ // calculate glyph position relative to layout base
+ // TODO: avoid for nStart!=0 case by reusing rPos
+ long nXOffset = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXOffset += mpGlyphAdvances[ i ];
+
+ // calculate absolute position in pixel units
+ Point aRelativePos( nXOffset, 0 );
+ rPos = GetDrawPosition( aRelativePos );
+
+ int nCount = 0;
+ while( nCount < nLen )
+ {
+ // update return values {aGlyphId,nCharPos,nGlyphAdvance}
+ sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
+ if( mnLayoutFlags & SalLayoutFlags::Vertical )
+ {
+ const sal_UCS4 cChar = static_cast<sal_UCS4>(aGlyphId & GF_IDXMASK);
+ if( mrWinFontData.HasGSUBstitutions( mhDC )
+ && mrWinFontData.IsGSUBstituted( cChar ) )
+ aGlyphId |= GF_GSUB | GF_ROTL;
+ else
+ {
+ aGlyphId |= GetVerticalFlags( cChar );
+ if( (aGlyphId & GF_ROTMASK) == 0 )
+ aGlyphId |= GF_VERT;
+ }
+ }
+ aGlyphId |= GF_ISCHAR;
+
+ ++nCount;
+ *(pGlyphIds++) = aGlyphId;
+ if( pGlyphAdvances )
+ *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
+ if( pCharIndexes )
+ {
+ int nCharPos;
+ if( !mpGlyphs2Chars )
+ nCharPos = nStart + mnMinCharPos;
+ else
+ nCharPos = mpGlyphs2Chars[nStart];
+ *(pCharIndexes++) = nCharPos;
+ }
+
+ // stop at last glyph
+ if( ++nStart >= mnGlyphCount )
+ break;
+
+ // stop when next x-position is unexpected
+ if( !pGlyphAdvances && mpGlyphOrigAdvs )
+ if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
+ break;
+ }
+
+ return nCount;
+}
+
+bool SimpleWinLayout::DrawTextImpl(HDC hDC,
+ const Rectangle* pRectToErase,
+ Point* /* pPos */,
+ int* /* pGetNextGlypInfo */) const
+{
+ if (pRectToErase)
+ {
+ RECT aRect = { pRectToErase->Left(), pRectToErase->Top(), pRectToErase->Left()+pRectToErase->GetWidth(), pRectToErase->Top()+pRectToErase->GetHeight() };
+ FillRect(hDC, &aRect, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
+ }
+
+ if( mnGlyphCount <= 0 )
+ return false;
+
+ HFONT hOrigFont = DisableFontScaling();
+ Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+ // #108267#, break up into glyph portions of a limited size required by Win32 API
+ const unsigned int maxGlyphCount = 8192;
+ UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
+ UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
+
+ if( numGlyphPortions )
+ {
+ // #108267#,#109387# break up string into smaller chunks
+ // the output positions will be updated by windows (SetTextAlign)
+ POINT oldPos;
+ UINT oldTa = GetTextAlign(hDC);
+ SetTextAlign(hDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP);
+ MoveToEx(hDC, aPos.X(), aPos.Y(), &oldPos);
+ unsigned int i = 0;
+ for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount )
+ {
+ ExtTextOutW(hDC, 0, 0, 0, NULL, mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i);
+ }
+ ExtTextOutW(hDC, 0, 0, 0, NULL, mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i);
+ MoveToEx(hDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
+ SetTextAlign(hDC, oldTa);
+ }
+ else
+ ExtTextOutW(hDC, aPos.X(), aPos.Y(), 0, NULL, mpOutGlyphs, mnGlyphCount, mpGlyphAdvances);
+
+ if( hOrigFont )
+ DeleteFont(SelectFont(hDC, hOrigFont));
+
+ return false;
+}
+
+DeviceCoordinate SimpleWinLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
+{
+ if( !mnWidth )
+ {
+ mnWidth = mnBaseAdv;
+ for( int i = 0; i < mnGlyphCount; ++i )
+ mnWidth += mpGlyphAdvances[ i ];
+ }
+
+ if( pDXArray != NULL )
+ {
+ for( int i = 0; i < mnCharCount; ++i )
+ pDXArray[ i ] = mpCharWidths[ i ];
+ }
+
+ return mnWidth;
+}
+
+sal_Int32 SimpleWinLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
+// NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
+{
+ if( mnWidth )
+ if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
+ return -1;
+
+ long nExtraWidth = mnBaseAdv * nFactor;
+ for( int n = 0; n < mnCharCount; ++n )
+ {
+ // skip unused characters
+ if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
+ continue;
+ // add char widths until max
+ nExtraWidth += mpCharWidths[ n ] * nFactor;
+ if( nExtraWidth > nMaxWidth )
+ return (mnMinCharPos + n);
+ nExtraWidth += nCharExtra;
+ }
+
+ return -1;
+}
+
+void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
+{
+ long nXPos = mnBaseAdv;
+
+ if( !mpGlyphs2Chars )
+ {
+ for( int i = 0; i < nMaxIdx; i += 2 )
+ {
+ pCaretXArray[ i ] = nXPos;
+ nXPos += mpGlyphAdvances[ i>>1 ];
+ pCaretXArray[ i+1 ] = nXPos;
+ }
+ }
+ else
+ {
+ int i;
+ for( i = 0; i < nMaxIdx; ++i )
+ pCaretXArray[ i ] = -1;
+
+ // assign glyph positions to character positions
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
+ nCurrIdx *= 2;
+ if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
+ {
+ // normal positions for LTR case
+ pCaretXArray[ nCurrIdx ] = nXPos;
+ pCaretXArray[ nCurrIdx+1 ] = nXRight;
+ }
+ else
+ {
+ // reverse positions for RTL case
+ pCaretXArray[ nCurrIdx ] = nXRight;
+ pCaretXArray[ nCurrIdx+1 ] = nXPos;
+ }
+ nXPos += mpGlyphAdvances[ i ];
+ }
+ }
+}
+
+void SimpleWinLayout::Justify( DeviceCoordinate nNewWidth )
+{
+ DeviceCoordinate nOldWidth = mnWidth;
+ mnWidth = nNewWidth;
+
+ if( mnGlyphCount <= 0 )
+ return;
+
+ if( nNewWidth == nOldWidth )
+ return;
+
+ // the rightmost glyph cannot be stretched
+ const int nRight = mnGlyphCount - 1;
+ nOldWidth -= mpGlyphAdvances[ nRight ];
+ nNewWidth -= mpGlyphAdvances[ nRight ];
+
+ // count stretchable glyphs
+ int nStretchable = 0, i;
+ for( i = 0; i < nRight; ++i )
+ if( mpGlyphAdvances[i] >= 0 )
+ ++nStretchable;
+
+ // stretch these glyphs
+ DeviceCoordinate nDiffWidth = nNewWidth - nOldWidth;
+ for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
+ {
+ if( mpGlyphAdvances[i] <= 0 )
+ continue;
+ DeviceCoordinate nDeltaWidth = nDiffWidth / nStretchable;
+ mpGlyphAdvances[i] += nDeltaWidth;
+ --nStretchable;
+ nDiffWidth -= nDeltaWidth;
+ }
+}
+
+void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ SalLayout::AdjustLayout( rArgs );
+
+ // adjust positions if requested
+ if( rArgs.mpDXArray )
+ ApplyDXArray( rArgs );
+ else if( rArgs.mnLayoutWidth )
+ Justify( rArgs.mnLayoutWidth );
+ else
+ return;
+
+ // recalculate virtual char widths if they were changed
+ if( mpCharWidths != mpGlyphAdvances )
+ {
+ int i;
+ if( !mpGlyphs2Chars )
+ {
+ // standard LTR case
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpCharWidths[ i ] = mpGlyphAdvances[ i ];
+ }
+ else
+ {
+ // BiDi or complex case
+ for( i = 0; i < mnCharCount; ++i )
+ mpCharWidths[ i ] = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
+ if( j >= 0 )
+ mpCharWidths[ j ] += mpGlyphAdvances[ i ];
+ }
+ }
+ }
+}
+
+void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
+{
+ // try to avoid disturbance of text flow for LSB rounding case;
+ const long* pDXArray = rArgs.mpDXArray;
+
+ int i = 0;
+ long nOldWidth = mnBaseAdv;
+ for(; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ {
+ nOldWidth += mpGlyphAdvances[ j ];
+ long nDiff = nOldWidth - pDXArray[ i ];
+
+ // disabled because of #104768#
+ // works great for static text, but problems when typing
+ // if( nDiff>+1 || nDiff<-1 )
+ // only bother with changing anything when something moved
+ if( nDiff != 0 )
+ break;
+ }
+ }
+ if( i >= mnCharCount )
+ return;
+
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( i = 0; i < mnGlyphCount; ++i )
+ mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
+ }
+
+ mnWidth = mnBaseAdv;
+ for( i = 0; i < mnCharCount; ++i )
+ {
+ int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
+ if( j >= 0 )
+ mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
+ mnWidth = pDXArray[i];
+ }
+}
+
+void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
+{
+ if( nStart > mnGlyphCount )
+ return;
+
+ // calculate the current x-position of the requested glyph
+ // TODO: cache absolute positions
+ int nXPos = mnBaseAdv;
+ for( int i = 0; i < nStart; ++i )
+ nXPos += mpGlyphAdvances[i];
+
+ // calculate the difference to the current glyph position
+ int nDelta = nNewXPos - nXPos;
+
+ // adjust the width of the layout if it was already cached
+ if( mnWidth )
+ mnWidth += nDelta;
+
+ // depending on whether the requested glyph is leftmost in the layout
+ // adjust either the layout's or the requested glyph's relative position
+ if( nStart > 0 )
+ mpGlyphAdvances[ nStart-1 ] += nDelta;
+ else
+ mnBaseAdv += nDelta;
+}
+
+void SimpleWinLayout::DropGlyph( int nStart )
+{
+ mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
+}
+
+void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
+{
+ // return early if no glyph has been dropped
+ int i = mnGlyphCount;
+ while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
+ if( i < 0 )
+ return;
+
+ // convert the layout to a sparse layout if it is not already
+ if( !mpGlyphs2Chars )
+ {
+ mpGlyphs2Chars = new int[ mnGlyphCount ];
+ mpCharWidths = new int[ mnCharCount ];
+ // assertion: mnGlyphCount == mnCharCount
+ for( int k = 0; k < mnGlyphCount; ++k )
+ {
+ mpGlyphs2Chars[ k ] = mnMinCharPos + k;
+ mpCharWidths[ k ] = mpGlyphAdvances[ k ];
+ }
+ }
+
+ // remove dropped glyphs that are rightmost in the layout
+ for( i = mnGlyphCount; --i >= 0; )
+ {
+ if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
+ break;
+ if( mnWidth )
+ mnWidth -= mpGlyphAdvances[ i ];
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ mnGlyphCount = i + 1;
+
+ // keep original glyph widths around
+ if( !mpGlyphOrigAdvs )
+ {
+ mpGlyphOrigAdvs = new int[ mnGlyphCount ];
+ for( int k = 0; k < mnGlyphCount; ++k )
+ mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
+ }
+
+ // remove dropped glyphs inside the layout
+ int nNewGC = 0;
+ for( i = 0; i < mnGlyphCount; ++i )
+ {
+ if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
+ {
+ // adjust relative position to last valid glyph
+ int nDroppedWidth = mpGlyphAdvances[ i ];
+ mpGlyphAdvances[ i ] = 0;
+ if( nNewGC > 0 )
+ mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
+ else
+ mnBaseAdv += nDroppedWidth;
+
+ // zero the virtual char width for the char that has a fallback
+ int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
+ if( nRelCharPos >= 0 )
+ mpCharWidths[ nRelCharPos ] = 0;
+ }
+ else
+ {
+ if( nNewGC != i )
+ {
+ // rearrange the glyph array to get rid of the dropped glyph
+ mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
+ mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
+ mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
+ mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
+ }
+ ++nNewGC;
+ }
+ }
+
+ mnGlyphCount = nNewGC;
+ if( mnGlyphCount <= 0 )
+ mnWidth = mnBaseAdv = 0;
+}
+
const OpenGLGlyphCacheChunk& ImplWinFontEntry::GetCachedGlyphChunkFor(int nGlyphIndex) const
{
auto i = maOpenGLGlyphCache.cbegin();
@@ -656,6 +1409,74 @@ void WinLayout::DrawText(SalGraphics& rGraphics) const
}
}
+bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const
+{
+ static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == NULL);
+
+ if (!bDoGlyphCaching)
+ return false;
+
+ for (int i = 0; i < mnGlyphCount; i++)
+ {
+ if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
+ continue;
+
+ if (!mrWinFontEntry.AddChunkOfGlyphs(false, mpOutGlyphs[i], *this, rGraphics))
+ return false;
+ }
+
+ return true;
+}
+
+bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
+{
+ WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
+ HDC hDC = rWinGraphics.getHDC();
+
+ Rectangle aRect;
+ GetBoundRect(rGraphics, aRect);
+
+ COLORREF color = GetTextColor(hDC);
+ SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color));
+
+ WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get());
+ if (!pImpl)
+ return false;
+
+ pImpl->PreDraw();
+
+ HFONT hOrigFont = DisableFontScaling();
+ Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
+
+ int nAdvance = 0;
+
+ for (int i = 0; i < mnGlyphCount; i++)
+ {
+ if (mpOutGlyphs[i] == DROPPED_OUTGLYPH)
+ continue;
+
+ assert(mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]));
+
+ const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(mpOutGlyphs[i]);
+ const int n = mpOutGlyphs[i] - rChunk.mnFirstGlyph;
+
+ SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
+ rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
+ nAdvance + aPos.X() - rChunk.getExtraOffset(), aPos.Y() - rChunk.mnAscent - rChunk.getExtraOffset(),
+ rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
+ pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+
+ nAdvance += mpGlyphAdvances[i];
+ }
+
+ if( hOrigFont )
+ DeleteFont(SelectFont(hDC, hOrigFont));
+
+ pImpl->PostDraw();
+
+ return true;
+}
+
struct VisualItem
{
public:
@@ -1862,7 +2683,7 @@ bool UniscribeLayout::CacheGlyphs(SalGraphics& rGraphics) const
if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
continue;
- if (!mrWinFontEntry.AddChunkOfGlyphs(mpOutGlyphs[i], *this, rGraphics))
+ if (!mrWinFontEntry.AddChunkOfGlyphs(true, mpOutGlyphs[i], *this, rGraphics))
return false;
}
}
@@ -2869,7 +3690,7 @@ void GraphiteWinLayout::Simplify( bool is_base )
}
#endif // ENABLE_GRAPHITE
-SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int nFallbackLevel )
+SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
{
if (!mpWinFontEntry[nFallbackLevel]) return nullptr;
@@ -2884,18 +3705,56 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int nFallba
if (!bUspInited)
InitUSP();
-#if ENABLE_GRAPHITE
- if (rFontFace.SupportsGraphite())
+
+ if( !(rArgs.mnFlags & SalLayoutFlags::ComplexDisabled) )
{
- pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+#if ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ {
+ pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+ }
+ else
+#endif // ENABLE_GRAPHITE
+ {
+ // script complexity is determined in upper layers
+ pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+ // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+ // the created UniscribeLayout, otherwise the data passed into the
+ // constructor might become invalid too early
+ }
}
else
-#endif // ENABLE_GRAPHITE
{
- pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
- // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
- // the created UniscribeLayout, otherwise the data passed into the
- // constructor might become invalid too early
+#if ENABLE_GRAPHITE
+ if (rFontFace.SupportsGraphite())
+ {
+ pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+ }
+ else
+#endif // ENABLE_GRAPHITE
+ {
+ static bool bAvoidSimpleWinLayout = (std::getenv("VCL_NO_SIMPLEWINLAYOUT") != NULL);
+
+ if (!bAvoidSimpleWinLayout)
+ {
+ if( (rArgs.mnFlags & SalLayoutFlags::KerningPairs) && !rFontInstance.HasKernData() )
+ {
+ // TODO: directly cache kerning info in the rFontInstance
+ // TODO: get rid of kerning methods+data in WinSalGraphics object
+ GetKernPairs();
+ rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
+ }
+
+ pWinLayout = new SimpleWinLayout(getHDC(), ANSI_CHARSET, rFontFace, rFontInstance, bUseOpenGL);
+ }
+ else
+ {
+ pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
+ // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
+ // the created UniscribeLayout, otherwise the data passed into the
+ // constructor might become invalid too early
+ }
+ }
}
if( mfFontScale[nFallbackLevel] != 1.0 )
@@ -2917,6 +3776,9 @@ ImplWinFontEntry::ImplWinFontEntry( FontSelectPattern& rFSD )
: ImplFontEntry( rFSD )
, mpGLyphyAtlas( nullptr )
, mpGLyphyFont( nullptr )
+, mpKerningPairs( NULL )
+, mnKerningPairs( -1 )
+, maWidthMap( 512 )
, mnMinKashidaWidth( -1 )
, mnMinKashidaGlyph( -1 )
{
@@ -2929,6 +3791,38 @@ ImplWinFontEntry::~ImplWinFontEntry()
{
if( maScriptCache != NULL )
ScriptFreeCache( &maScriptCache );
+ delete[] mpKerningPairs;
+}
+
+bool ImplWinFontEntry::HasKernData() const
+{
+ return (mnKerningPairs >= 0);
+}
+
+void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
+{
+ mnKerningPairs = nPairCount;
+ mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
+ memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
+}
+
+int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ int nKernAmount = 0;
+ if( mpKerningPairs )
+ {
+ const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
+ const KERNINGPAIR* pFirstPair = mpKerningPairs;
+ const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
+ const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
+ pEndPair, aRefPair, ImplCmpKernData );
+ if( (pPair != pEndPair)
+ && (pPair->wFirst == aRefPair.wFirst)
+ && (pPair->wSecond == aRefPair.wSecond) )
+ nKernAmount = pPair->iKernAmount;
+ }
+
+ return nKernAmount;
}
bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
diff --git a/vcl/win/source/gdi/winlayout.hxx b/vcl/win/source/gdi/winlayout.hxx
index ff4e5cc..1d2a776 100644
--- a/vcl/win/source/gdi/winlayout.hxx
+++ b/vcl/win/source/gdi/winlayout.hxx
@@ -68,6 +68,51 @@ public:
ImplWinFontEntry& mrWinFontEntry;
};
+class SimpleWinLayout : public WinLayout
+{
+public:
+ SimpleWinLayout(HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry&, bool bUseOpenGL);
+ virtual ~SimpleWinLayout();
+
+ virtual bool LayoutText( ImplLayoutArgs& ) override;
+ virtual void AdjustLayout( ImplLayoutArgs& ) override;
+ virtual bool DrawTextImpl(HDC hDC, const Rectangle* pRectToErase, Point* pPos, int* pGetNextGlypInfo) const override;
+
+ virtual bool CacheGlyphs(SalGraphics& rGraphics) const override;
+ virtual bool DrawCachedGlyphs(SalGraphics& rGraphics) const override;
+ virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
+ DeviceCoordinate* pGlyphAdvances, int* pCharIndexes,
+ const PhysicalFontFace** pFallbackFonts = NULL ) const override;
+
+ virtual DeviceCoordinate FillDXArray( DeviceCoordinate* pDXArray ) const override;
+ virtual sal_Int32 GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const override;
+ virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const override;
+
+ // for glyph+font+script fallback
+ virtual void MoveGlyph( int nStart, long nNewXPos ) override;
+ virtual void DropGlyph( int nStart ) override;
+ virtual void Simplify( bool bIsBase ) override;
+
+protected:
+ void Justify( DeviceCoordinate nNewWidth );
+ void ApplyDXArray( const ImplLayoutArgs& );
+
+private:
+ int mnGlyphCount;
+ int mnCharCount;
+ WCHAR* mpOutGlyphs;
+ int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
+ int* mpGlyphOrigAdvs;
+ int* mpCharWidths; // map rel char pos to char width
+ int* mpChars2Glyphs; // map rel char pos to abs glyph pos
+ int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
+ bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
+ mutable long mnWidth;
+
+ int mnNotdefWidth;
+ BYTE mnCharSet;
+};
+
class UniscribeLayout : public WinLayout
{
public:
More information about the Libreoffice-commits
mailing list