2016-12-08 08:52:44 -05:00
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
2015-11-04 16:14:13 -05:00
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
# include "Framework/Text/ShapedTextCache.h"
# include "Fonts/FontCache.h"
# include "Internationalization/IBreakIterator.h"
# include "Internationalization/BreakIterator.h"
2015-11-04 16:14:13 -05:00
FShapedGlyphSequencePtr FShapedTextCache : : FindShapedText ( const FCachedShapedTextKey & InKey ) const
{
FShapedGlyphSequencePtr ShapedText = CachedShapedText . FindRef ( InKey ) ;
if ( ShapedText . IsValid ( ) & & ! ShapedText - > IsDirty ( ) )
{
return ShapedText ;
}
return nullptr ;
}
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef FShapedTextCache : : AddShapedText ( const FCachedShapedTextKey & InKey , const TCHAR * InText )
2015-11-04 16:14:13 -05:00
{
FShapedGlyphSequenceRef ShapedText = FontCache . ShapeBidirectionalText (
InText ,
InKey . TextRange . BeginIndex ,
InKey . TextRange . Len ( ) ,
2016-01-22 08:13:18 -05:00
InKey . FontInfo ,
2015-11-04 16:14:13 -05:00
InKey . Scale ,
InKey . TextContext . BaseDirection ,
InKey . TextContext . TextShapingMethod
) ;
return AddShapedText ( InKey , ShapedText ) ;
}
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef FShapedTextCache : : AddShapedText ( const FCachedShapedTextKey & InKey , const TCHAR * InText , const TextBiDi : : ETextDirection InTextDirection )
2015-11-04 16:14:13 -05:00
{
FShapedGlyphSequenceRef ShapedText = FontCache . ShapeUnidirectionalText (
InText ,
InKey . TextRange . BeginIndex ,
InKey . TextRange . Len ( ) ,
2016-01-22 08:13:18 -05:00
InKey . FontInfo ,
2015-11-04 16:14:13 -05:00
InKey . Scale ,
InTextDirection ,
InKey . TextContext . TextShapingMethod
) ;
return AddShapedText ( InKey , ShapedText ) ;
}
FShapedGlyphSequenceRef FShapedTextCache : : AddShapedText ( const FCachedShapedTextKey & InKey , FShapedGlyphSequenceRef InShapedText )
{
CachedShapedText . Add ( InKey , InShapedText ) ;
return InShapedText ;
}
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef FShapedTextCache : : FindOrAddShapedText ( const FCachedShapedTextKey & InKey , const TCHAR * InText )
2015-11-04 16:14:13 -05:00
{
FShapedGlyphSequencePtr ShapedText = FindShapedText ( InKey ) ;
if ( ! ShapedText . IsValid ( ) )
{
2016-01-22 08:13:18 -05:00
ShapedText = AddShapedText ( InKey , InText ) ;
2015-11-04 16:14:13 -05:00
}
return ShapedText . ToSharedRef ( ) ;
}
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef FShapedTextCache : : FindOrAddShapedText ( const FCachedShapedTextKey & InKey , const TCHAR * InText , const TextBiDi : : ETextDirection InTextDirection )
2015-11-04 16:14:13 -05:00
{
FShapedGlyphSequencePtr ShapedText = FindShapedText ( InKey ) ;
if ( ! ShapedText . IsValid ( ) )
{
2016-01-22 08:13:18 -05:00
ShapedText = AddShapedText ( InKey , InText , InTextDirection ) ;
2015-11-04 16:14:13 -05:00
}
return ShapedText . ToSharedRef ( ) ;
}
void FShapedTextCache : : Clear ( )
{
CachedShapedText . Reset ( ) ;
}
2016-01-22 08:13:18 -05:00
FVector2D ShapedTextCacheUtil : : MeasureShapedText ( const FShapedTextCacheRef & InShapedTextCache , const FCachedShapedTextKey & InRunKey , const FTextRange & InMeasureRange , const TCHAR * InText )
2015-11-04 16:14:13 -05:00
{
// Get the shaped text for the entire run and try and take a sub-measurement from it - this can help minimize the amount of text shaping that needs to be done when measuring text
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef ShapedText = InShapedTextCache - > FindOrAddShapedText ( InRunKey , InText ) ;
2015-11-04 16:14:13 -05:00
TOptional < int32 > MeasuredWidth = ShapedText - > GetMeasuredWidth ( InMeasureRange . BeginIndex , InMeasureRange . EndIndex ) ;
if ( ! MeasuredWidth . IsSet ( ) )
{
FCachedShapedTextKey MeasureKey = InRunKey ;
MeasureKey . TextRange = InMeasureRange ;
// Couldn't measure the sub-range, try and measure from a shape of the specified range
2016-01-22 08:13:18 -05:00
ShapedText = InShapedTextCache - > FindOrAddShapedText ( MeasureKey , InText ) ;
2015-11-04 16:14:13 -05:00
MeasuredWidth = ShapedText - > GetMeasuredWidth ( ) ;
}
check ( MeasuredWidth . IsSet ( ) ) ;
return FVector2D ( MeasuredWidth . GetValue ( ) , ShapedText - > GetMaxTextHeight ( ) ) ;
}
2016-01-22 08:13:18 -05:00
int32 ShapedTextCacheUtil : : FindCharacterIndexAtOffset ( const FShapedTextCacheRef & InShapedTextCache , const FCachedShapedTextKey & InRunKey , const FTextRange & InTextRange , const TCHAR * InText , const int32 InHorizontalOffset )
{
FSlateFontCache & FontCache = InShapedTextCache - > GetFontCache ( ) ;
// Get the shaped text for the entire run and try and take a sub-measurement from it - this can help minimize the amount of text shaping that needs to be done when measuring text
FShapedGlyphSequenceRef ShapedText = InShapedTextCache - > FindOrAddShapedText ( InRunKey , InText ) ;
TOptional < FShapedGlyphSequence : : FGlyphOffsetResult > GlyphOffsetResult = ShapedText - > GetGlyphAtOffset ( FontCache , InTextRange . BeginIndex , InTextRange . EndIndex , InHorizontalOffset ) ;
if ( ! GlyphOffsetResult . IsSet ( ) )
{
FCachedShapedTextKey IndexAtOffsetKey = InRunKey ;
IndexAtOffsetKey . TextRange = InTextRange ;
// Couldn't search the sub-range, try and search from a shape of the specified range
ShapedText = InShapedTextCache - > FindOrAddShapedText ( IndexAtOffsetKey , InText ) ;
GlyphOffsetResult = ShapedText - > GetGlyphAtOffset ( FontCache , InHorizontalOffset ) ;
}
check ( GlyphOffsetResult . IsSet ( ) ) ;
// We need to handle the fact that the found glyph may have been a ligature, and if so, we need to measure each part of the ligature to find the best character index match
const FShapedGlyphSequence : : FGlyphOffsetResult & GlyphOffsetResultValue = GlyphOffsetResult . GetValue ( ) ;
2016-04-07 16:16:52 -04:00
if ( GlyphOffsetResultValue . Glyph & & GlyphOffsetResultValue . Glyph - > NumGraphemeClustersInGlyph > 1 )
2016-01-22 08:13:18 -05:00
{
2016-04-07 16:16:52 -04:00
// Process each grapheme cluster within the ligature
const FString LigatureString = FString ( GlyphOffsetResultValue . Glyph - > NumCharactersInGlyph , InText + GlyphOffsetResultValue . Glyph - > SourceIndex ) ;
TSharedRef < IBreakIterator > GraphemeBreakIterator = FBreakIterator : : CreateCharacterBoundaryIterator ( ) ;
GraphemeBreakIterator - > SetString ( LigatureString ) ;
FCachedShapedTextKey LigatureKey = InRunKey ;
LigatureKey . TextRange = FTextRange ( 0 , LigatureString . Len ( ) ) ;
2016-01-22 08:13:18 -05:00
int32 CurrentOffset = GlyphOffsetResultValue . GlyphOffset ;
2016-04-07 16:16:52 -04:00
if ( GlyphOffsetResultValue . Glyph - > TextDirection = = TextBiDi : : ETextDirection : : LeftToRight )
2016-04-07 14:58:54 -04:00
{
2016-04-07 16:16:52 -04:00
int32 PrevCharIndex = GraphemeBreakIterator - > ResetToBeginning ( ) ;
for ( int32 CurrentCharIndex = GraphemeBreakIterator - > MoveToNext ( ) ; CurrentCharIndex ! = INDEX_NONE ; CurrentCharIndex = GraphemeBreakIterator - > MoveToNext ( ) )
2016-04-07 14:58:54 -04:00
{
2016-04-07 16:16:52 -04:00
FShapedGlyphSequenceRef GraphemeShapedText = GetShapedTextSubSequence ( InShapedTextCache , LigatureKey , FTextRange ( PrevCharIndex , CurrentCharIndex ) , * LigatureString , GlyphOffsetResultValue . Glyph - > TextDirection ) ;
const FShapedGlyphSequence : : FGlyphOffsetResult GraphemeOffsetResult = GraphemeShapedText - > GetGlyphAtOffset ( FontCache , InHorizontalOffset , CurrentOffset ) ;
if ( GraphemeOffsetResult . Glyph )
2016-04-07 14:58:54 -04:00
{
2016-04-07 16:16:52 -04:00
return GlyphOffsetResultValue . Glyph - > SourceIndex + GraphemeOffsetResult . CharacterIndex ;
2016-04-07 14:58:54 -04:00
}
2016-04-07 16:16:52 -04:00
PrevCharIndex = CurrentCharIndex ;
CurrentOffset + = GraphemeShapedText - > GetMeasuredWidth ( ) ;
2016-04-07 14:58:54 -04:00
}
2016-04-07 16:16:52 -04:00
return GlyphOffsetResultValue . Glyph - > SourceIndex + GlyphOffsetResultValue . Glyph - > NumCharactersInGlyph ;
2016-01-22 08:13:18 -05:00
}
else
{
2016-04-07 16:16:52 -04:00
int32 PrevCharIndex = GraphemeBreakIterator - > ResetToEnd ( ) ;
for ( int32 CurrentCharIndex = GraphemeBreakIterator - > MoveToPrevious ( ) ; CurrentCharIndex ! = INDEX_NONE ; CurrentCharIndex = GraphemeBreakIterator - > MoveToPrevious ( ) )
2016-01-22 08:13:18 -05:00
{
2016-04-07 16:16:52 -04:00
FShapedGlyphSequenceRef GraphemeShapedText = GetShapedTextSubSequence ( InShapedTextCache , LigatureKey , FTextRange ( CurrentCharIndex , PrevCharIndex ) , * LigatureString , GlyphOffsetResultValue . Glyph - > TextDirection ) ;
const FShapedGlyphSequence : : FGlyphOffsetResult GraphemeOffsetResult = GraphemeShapedText - > GetGlyphAtOffset ( FontCache , InHorizontalOffset , CurrentOffset ) ;
if ( GraphemeOffsetResult . Glyph )
2016-01-22 08:13:18 -05:00
{
2016-04-07 16:16:52 -04:00
return GlyphOffsetResultValue . Glyph - > SourceIndex + ( ( PrevCharIndex ! = INDEX_NONE ) ? PrevCharIndex : GraphemeOffsetResult . CharacterIndex ) ;
2016-01-22 08:13:18 -05:00
}
2016-04-07 16:16:52 -04:00
PrevCharIndex = CurrentCharIndex ;
CurrentOffset + = GraphemeShapedText - > GetMeasuredWidth ( ) ;
2016-01-22 08:13:18 -05:00
}
2016-04-07 16:16:52 -04:00
return GlyphOffsetResultValue . Glyph - > SourceIndex ;
2016-01-22 08:13:18 -05:00
}
}
return GlyphOffsetResultValue . CharacterIndex ;
}
int8 ShapedTextCacheUtil : : GetShapedGlyphKerning ( const FShapedTextCacheRef & InShapedTextCache , const FCachedShapedTextKey & InRunKey , const int32 InGlyphIndex , const TCHAR * InText )
2015-11-04 16:14:13 -05:00
{
// Get the shaped text for the entire run and try and get the kerning from it - this can help minimize the amount of text shaping that needs to be done when calculating kerning
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef ShapedText = InShapedTextCache - > FindOrAddShapedText ( InRunKey , InText ) ;
2015-11-04 16:14:13 -05:00
TOptional < int8 > Kerning = ShapedText - > GetKerning ( InGlyphIndex ) ;
if ( ! Kerning . IsSet ( ) )
{
FCachedShapedTextKey KerningKey = InRunKey ;
KerningKey . TextRange = FTextRange ( InGlyphIndex , InGlyphIndex + 2 ) ;
// Couldn't get the kerning from the main run data, try and get the kerning from a shape of the specified range
2016-01-22 08:13:18 -05:00
ShapedText = InShapedTextCache - > FindOrAddShapedText ( KerningKey , InText ) ;
2015-11-04 16:14:13 -05:00
Kerning = ShapedText - > GetKerning ( InGlyphIndex ) ;
}
return Kerning . Get ( 0 ) ;
}
2016-01-22 08:13:18 -05:00
FShapedGlyphSequenceRef ShapedTextCacheUtil : : GetShapedTextSubSequence ( const FShapedTextCacheRef & InShapedTextCache , const FCachedShapedTextKey & InRunKey , const FTextRange & InTextRange , const TCHAR * InText , const TextBiDi : : ETextDirection InTextDirection )
2015-11-04 16:14:13 -05:00
{
// Get the shaped text for the entire run and try and make a sub-sequence from it - this can help minimize the amount of text shaping that needs to be done when drawing text
2016-01-22 08:13:18 -05:00
FShapedGlyphSequencePtr ShapedText = InShapedTextCache - > FindOrAddShapedText ( InRunKey , InText ) ;
2015-11-04 16:14:13 -05:00
if ( InRunKey . TextRange ! = InTextRange )
{
FCachedShapedTextKey SubSequenceKey = InRunKey ;
SubSequenceKey . TextRange = InTextRange ;
// Do we already have a cached entry for this? We don't use FindOrAdd here as if it's missing, we first want to try and extract it from our run of shaped text
FShapedGlyphSequencePtr FoundShapedText = InShapedTextCache - > FindShapedText ( SubSequenceKey ) ;
if ( FoundShapedText . IsValid ( ) )
{
ShapedText = FoundShapedText ;
}
else
{
// Didn't find it in the cache, so first try and extract a sub-sequence from the run of shaped text
ShapedText = ShapedText - > GetSubSequence ( InTextRange . BeginIndex , InTextRange . EndIndex ) ;
if ( ShapedText . IsValid ( ) )
{
// Add this new sub-sequence to the cache
InShapedTextCache - > AddShapedText ( SubSequenceKey , ShapedText . ToSharedRef ( ) ) ;
}
else
{
// Couldn't get the sub-sequence, try and make a new shape for it instead
2016-01-22 08:13:18 -05:00
ShapedText = InShapedTextCache - > FindOrAddShapedText ( SubSequenceKey , InText , InTextDirection ) ;
2015-11-04 16:14:13 -05:00
}
}
}
check ( ShapedText . IsValid ( ) ) ;
return ShapedText . ToSharedRef ( ) ;
}