FVector2D FSlateFontMeasure::MeasureStringInternal( const FString& Text, int32 StartIndex, int32 EndIndex, const FSlateFontInfo& InFontInfo, bool IncludeKerningWithPrecedingChar, float FontScale, int32 StopAfterHorizontalOffset, ELastCharacterIndexFormat CharIndexFormat, int32& OutLastCharacterIndex ) const { SCOPE_CYCLE_COUNTER(STAT_SlateMeasureStringTime); FCharacterList& CharacterList = FontCache->GetCharacterList( InFontInfo, FontScale ); const uint16 MaxHeight = CharacterList.GetMaxHeight(); const bool DoesStartAtBeginning = StartIndex == 0; const bool DoesFinishAtEnd = EndIndex == Text.Len(); const int32 TextRangeLength = EndIndex - StartIndex; if ( EndIndex - StartIndex <= 0 || EndIndex <= 0 || StartIndex < 0 || EndIndex <= StartIndex ) { return FVector2D( 0, MaxHeight ); } #define USE_MEASURE_CACHING 1 #if USE_MEASURE_CACHING TSharedPtr< FMeasureCache > CurrentMeasureCache = NULL; // Do not cache strings which have small sizes or which have complicated measure requirements if( DoesStartAtBeginning && DoesFinishAtEnd && !IncludeKerningWithPrecedingChar && TextRangeLength > 5 && StopAfterHorizontalOffset == INDEX_NONE ) { FSlateFontKey FontKey(InFontInfo,FontScale); TSharedPtr< FMeasureCache > FoundMeasureCache = FontToMeasureCache.FindRef( FontKey ); if ( FoundMeasureCache.IsValid() ) { CurrentMeasureCache = FoundMeasureCache; const FVector2D* CachedMeasurement = CurrentMeasureCache->AccessItem( Text ); if( CachedMeasurement ) { return *CachedMeasurement; } } else { CurrentMeasureCache = MakeShareable( new FMeasureCache( FontMeasureConstants::MeasureCacheSize ) ); FontToMeasureCache.Add( FontKey, CurrentMeasureCache ); } } #endif // The size of the string FVector2D Size(0,0); // Widest line encountered while drawing this text. int32 MaxLineWidth = 0; // The width of the current line so far. int32 CurrentX = 0; // Accumulated height of this block of text int32 StringSizeY = MaxHeight; // The previous char (for kerning) TCHAR PreviousChar = 0; //If we are measuring a range then we should take into account the kerning with the character before the start of the range if ( !DoesStartAtBeginning && IncludeKerningWithPrecedingChar ) { PreviousChar = Text[ StartIndex - 1 ]; } int32 FinalPosX = 0; int32 CharIndex; for( CharIndex = StartIndex; CharIndex < EndIndex; ++CharIndex ) { TCHAR CurrentChar = Text[CharIndex]; const bool IsNewline = (CurrentChar == '\n'); if (IsNewline) { // New line means // 1) we accumulate total height StringSizeY += MaxHeight; // 2) update the longest line we've encountered MaxLineWidth = FMath::Max(CurrentX, MaxLineWidth); // 3) the next line starts at the beginning CurrentX = 0; } else { const FCharacterEntry& Entry = CharacterList[CurrentChar]; int32 Kerning = 0; if( CharIndex > 0 ) { Kerning = CharacterList.GetKerning( PreviousChar, CurrentChar ); } const int32 TotalCharSpacing = Kerning + Entry.HorizontalOffset + // Width is any kerning plus how much to advance the position when drawing a new character Entry.XAdvance; // How far we advance PreviousChar = CurrentChar; CurrentX += Kerning + Entry.XAdvance; // Were we asked to stop measuring after the specified horizontal offset in pixels? if( StopAfterHorizontalOffset != INDEX_NONE ) { if( CharIndexFormat == ELastCharacterIndexFormat::CharacterAtOffset ) { // Round our test toward the character's center position if( StopAfterHorizontalOffset < CurrentX - TotalCharSpacing / 2 ) { // We've reached the stopping point, so bail break; } } else if( CharIndexFormat == ELastCharacterIndexFormat::LastWholeCharacterBeforeOffset ) { if( StopAfterHorizontalOffset < CurrentX ) { --CharIndex; if ( CharIndex < StartIndex ) { CharIndex = INDEX_NONE; } // We've reached the stopping point, so bail break; } } } } } // We just finished a line, so need to update the longest line encountered. MaxLineWidth = FMath::Max(CurrentX, MaxLineWidth); Size.X = MaxLineWidth; Size.Y = StringSizeY; OutLastCharacterIndex = CharIndex; #if USE_MEASURE_CACHING if( StopAfterHorizontalOffset == INDEX_NONE && CurrentMeasureCache.IsValid() ) { CurrentMeasureCache->Add( Text, Size ); } #endif return Size; }