RichTextのアウトラインによるスペース問題の修正について

この質問は、以下のスレッドに関連して作成されました: [Yet another Rich Text / Outline spacing [Content removed]

お世話になっております。

関連スレッドに​て修正されているRichTextでアウトラインを使用した際のスペース問題ですが、5.6.1環境において修正を確認できましたが、自動改行(Auto Wrap)と併用した際に以下のいくつかの問題を発見いたしました。

1.改行位置の計算​にスペースを打ち消す計算が考慮されていない

void FTextLayout::FlowLineLayout(const int32 LineModelIndex, const float WrappingDrawWidth, TArray<TSharedRef<ILayoutBlock>>& SoftLine)​上記の関数内で​改行の候補位置ごとに幅を加算して改行位置を決定しておりますが、こちらのコードは問題となっていたアウトライン分のサイズを含んだ幅になっておりますので、想定より幅が大きく見積もられ、早い位置で改行される場合がございます。

2.自動改行が行われた場合にスペースを打ち消す計算に誤りが発生する

​void FTextLayout::CreateLineViewBlocks( int32 LineModelIndex, const int32 StopIndex, const float WrappedLineWidth, const TOptional<float>& JustificationWidth, int32& OutRunIndex, int32& OutRendererIndex, int32& OutPreviousBlockEnd, TArray< TSharedRef< ILayoutBlock > >& OutSoftLine )上記の関数内で​アウトラインサイズ分を打ち消し、

if (Index > 0)
{
	// Scooch adjacent blocks with different outline sizes as needed to keep the actual glyph spacing correct 
	const float PrevBlockOutlineAdjustment = (PrevBlockOutlineWidth - BlockOutlineSize.X) * 0.5f;
	CurrentHorizontalPos += PrevBlockOutlineAdjustment;
}

上記の処理によって前後のブロック間のアウトラインの差を調整していると見受けられます。

​自動改行が行われたブロックに着目すると、PrevBlockOutlineWidthの値は前ブロックのアウトラインの幅の2倍(ブロックの前後分のスペース)になり、BlockOutlineSize.Xはアウトラインの幅と同じ(ブロックの前方向分のスペース)になります。

どちらのブロックもアウトラインサイズが同一だとすると、ブロックの間にスペースは不要なはずですが、上記の計算によってアウトラインサイズの半分の値のスペースが入ることになります。

これらの問題について、具体的な修正方法や回避策をご教示いただけないでしょうか。

再現できるWidgetBlueprintを用意いたしましたので添付いたします。​

以上、​よろしくお願いいたします。

ご連絡が遅くなりまして申し訳ございません。

また、サンプルプロジェクトもご共有頂きありがとうございます。

> 1.改行位置の計算​にスペースを打ち消す計算が考慮されていない

正しいご指摘かと思われます。改行候補の幅は装飾を差し引かない計測結果を用いており、折り返しが早まる可能性があります。

> 2. 自動改行時にスペース打ち消し計算に誤りがある

同一アウトライン幅同士で余計な半分のスペースが入ることは現行コード上は起きないように見受けられます。

​1.については、CreateBreakCandidate 内の各スライス計測結果から装飾を差し引くように FTextLayout::CreateBreakCandidate を修正する必要がございます。

FTextLayout::FBreakCandidate FTextLayout::CreateBreakCandidate( int32& OutRunIndex, FLineModel& Line, int32 PreviousBreak, int32 CurrentBreak )
{
	//...
 
		if ( BeginIndex == StopIndex )
		{
			// This slice is empty, no need to adjust anything
			SliceSize = SliceSizeWithoutTrailingWhitespace = FVector2D::ZeroVector;
		}
		else if ( BeginIndex == WhitespaceStopIndex )
		{
			// This slice contains only whitespace, no need to adjust SliceSizeWithoutTrailingWhitespace
			SliceSize = Run.Measure( BeginIndex, StopIndex, Scale, RunTextContext );
//<-------------- Add-Start
			// Subtract decoration sizes (shadow/outline) to align wrap measurement with final placement width
			const auto ShadowSize = Run.GetRun()->GetShadowSize(BeginIndex, StopIndex, Scale);
			const auto OutlineSize = Run.GetRun()->GetOutlineSize(BeginIndex, StopIndex, Scale);
			SliceSize.X = FMath::Max(0.0f, SliceSize.X - (ShadowSize.X + OutlineSize.X));
//<-------------- Add-End
 
			SliceSizeWithoutTrailingWhitespace = FVector2D::ZeroVector;
		}
		else if ( WhitespaceStopIndex != StopIndex )
		{
			// This slice contains trailing whitespace, measure the text size, then add on the whitespace size
			SliceSize = SliceSizeWithoutTrailingWhitespace = Run.Measure( BeginIndex, WhitespaceStopIndex, Scale, RunTextContext );
			const float WhitespaceWidth = Run.Measure( WhitespaceStopIndex, StopIndex, Scale, RunTextContext ).X;
			SliceSize.X += WhitespaceWidth;
 
			// Subtract decoration sizes for the ranges we measured
//<-------------- Add-Start
			// For trimmed width (no trailing whitespace)
			const auto ShadowSizeTrimmed = Run.GetRun()->GetShadowSize(BeginIndex, WhitespaceStopIndex, Scale);
			const auto OutlineSizeTrimmed = Run.GetRun()->GetOutlineSize(BeginIndex, WhitespaceStopIndex, Scale);
			SliceSizeWithoutTrailingWhitespace.X = FMath::Max(0.0f, SliceSizeWithoutTrailingWhitespace.X - (ShadowSizeTrimmed.X + OutlineSizeTrimmed.X));
 
			// For actual size (includes trailing whitespace)
			const auto ShadowSizeActual = Run.GetRun()->GetShadowSize(BeginIndex, StopIndex, Scale);
			const auto OutlineSizeActual = Run.GetRun()->GetOutlineSize(BeginIndex, StopIndex, Scale);
			SliceSize.X = FMath::Max(0.0f, SliceSize.X - (ShadowSizeActual.X + OutlineSizeActual.X));
//<-------------- Add-End
 
			// We also need to measure the width of the first piece of trailing whitespace
			if ( WhitespaceStopIndex + 1 == StopIndex )
			{
				// Only have one piece of whitespace
				FirstTrailingWhitespaceCharWidth = WhitespaceWidth;
			}
			else
			{
				// Deliberately use the run version of Measure as we don't want the run model to cache this measurement since it may be out of order and break the binary search
				FirstTrailingWhitespaceCharWidth = Run.GetRun()->Measure( WhitespaceStopIndex, WhitespaceStopIndex + 1, Scale, RunTextContext ).X;
			}
		}
		else
		{
			// This slice contains no whitespace, both sizes are the same and can use the same measurement
			SliceSize = SliceSizeWithoutTrailingWhitespace = Run.Measure( BeginIndex, StopIndex, Scale, RunTextContext );
			// Subtract decoration sizes (shadow/outline)
//<-------------- Add-Start
			const auto ShadowSize = Run.GetRun()->GetShadowSize(BeginIndex, StopIndex, Scale);
			const auto OutlineSize = Run.GetRun()->GetOutlineSize(BeginIndex, StopIndex, Scale);
			const float DecorationX = ShadowSize.X + OutlineSize.X;
			SliceSize.X = FMath::Max(0.0f, SliceSize.X - DecorationX);
			SliceSizeWithoutTrailingWhitespace.X = FMath::Max(0.0f, SliceSizeWithoutTrailingWhitespace.X - DecorationX);
//<-------------- Add-End
		}

修正の前後を比較したものがこちらになりますが、期待するテキスト表示の内容を十分に理解できていたか自信がないため、もし私がご質問の意図を正しく把握できていない箇所がございましたらお知らせください。​

[Image Removed]