修复Linux下启动界面中文显示为方框的补丁

因为是使用Deepseek V4 Flash生成的,放官方仓库上大概率不给过(禁AI),我也不懂没法review,就直接放这里了,有需要的自己打补丁后编译就可以了。具体方法直接把补丁内容复制过去问AI就可以了。

diff --git a/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformSplash.cpp b/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformSplash.cpp
index 2b6c97eed..c7e33083d 100644
--- a/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformSplash.cpp
+++ b/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformSplash.cpp
@@ -63,20 +63,21 @@ private:
 
 #if WITH_EDITOR
 	void DrawCharacter(int32 penx, int32 peny, FT_GlyphSlot Glyph, int32 CurTypeIndex, float Red, float Green, float Blue);
 	void RenderStrings();
 	bool OpenFonts();
 
 	FT_Library FontLibrary = nullptr;
 	FT_Face FontSmall = nullptr;
 	FT_Face FontNormal = nullptr;
 	FT_Face FontLarge = nullptr;
+	FT_Face FontFallback = nullptr;
 
 	SDL_Renderer *SplashRenderer = nullptr;
 	SDL_Texture *SplashTexture = nullptr;
 	FText SplashText[ SplashTextType::NumTextTypes ];
 	Rect SplashTextRects[ SplashTextType::NumTextTypes ];
 
 	unsigned char *ScratchSpace = nullptr;
 	bool bStringsChanged = false;
 #endif // WITH_EDITOR
 
@@ -128,20 +129,25 @@ FLinuxSplashState::~FLinuxSplashState()
 	if (FontNormal)
 	{
 		FT_Done_Face(FontNormal);
 	}
 
 	if (FontLarge)
 	{
 		FT_Done_Face(FontLarge);
 	}
 
+	if (FontFallback)
+	{
+		FT_Done_Face(FontFallback);
+	}
+
 	if (FontLibrary)
 	{
 		FT_Done_FreeType(FontLibrary);
 	}
 #endif // WITH_EDITOR
 
 	// do not deinit SDL here
 }
 
 #if WITH_EDITOR
@@ -184,20 +190,33 @@ bool FLinuxSplashState::OpenFonts()
 
 	if (FT_New_Face(FontLibrary, TCHAR_TO_UTF8(*FontPath), 0, &FontLarge))
 	{
 		UE_LOGF(LogHAL, Error, "*** Unable to open large font face for splash screen.");
 	}
 	else
 	{
 		FT_Set_Pixel_Sizes(FontLarge, 0, 32);
 	}
 
+	// fallback font face, used for characters not covered by the fonts above (e.g. CJK),
+	// matching the fallback font used by Slate's composite font chain.
+	FontPath = FPaths::ConvertRelativePathToFull(FPaths::EngineContentDir() / TEXT("Slate/Fonts/DroidSansFallback.ttf"));
+
+	if (FT_New_Face(FontLibrary, TCHAR_TO_UTF8(*FontPath), 0, &FontFallback))
+	{
+		UE_LOGF(LogHAL, Error, "*** Unable to open fallback font face for splash screen.");
+	}
+	else
+	{
+		FT_Set_Pixel_Sizes(FontFallback, 0, 10);
+	}
+
 	return true;
 }
 
 
 //---------------------------------------------------------
 void FLinuxSplashState::DrawCharacter(int32 PenX, int32 PenY, FT_GlyphSlot Glyph, int32 CurTypeIndex, float Red, float Green, float Blue)
 {
 	// drawing boundaries
 	const int32 MinX = SplashTextRects[CurTypeIndex].Left;
 	const int32 MaxX = SplashTextRects[CurTypeIndex].Right;
@@ -242,20 +261,21 @@ void FLinuxSplashState::DrawCharacter(int32 PenX, int32 PenY, FT_GlyphSlot Glyph
 			ScratchSpace[DestIndex] = (uint8)(ScratchSpace[DestIndex]*(1.0 - Alpha) + Alpha*Blue);
 		}
 	}
 }
 
 
 //---------------------------------------------------------
 void FLinuxSplashState::RenderStrings()
 {
 	FT_UInt LastGlyph = 0;
+	FT_Face LastGlyphFont = nullptr;
 	FT_Vector Kerning;
 	bool bRightJustify = false;
 	float Red, Blue, Green; // font color
 
 	if (!bStringsChanged)
 	{
 		return;
 	}
 
 	bStringsChanged = false;
@@ -344,53 +364,82 @@ void FLinuxSplashState::RenderStrings()
 				if (bRightJustify)
 				{
 					CharacterCode = (Uint32)(Text[Text.Len() - i - 1]);
 				}
 				else
 				{
 					CharacterCode = (Uint32)(Text[i]);
 				}
 
 				FT_UInt GlyphIndex = FT_Get_Char_Index(Font, CharacterCode);
-				FT_Load_Glyph(Font, GlyphIndex, FT_LOAD_DEFAULT);
 
-				if (Font->glyph->format != FT_GLYPH_FORMAT_BITMAP)
+				// If the primary font doesn't cover this character (e.g. CJK), fall back to the
+				// engine's bundled fallback font, mirroring Slate's composite font chain.
+				FT_Face GlyphFont = Font;
+				if (GlyphIndex == 0 && FontFallback != nullptr)
 				{
-					FT_Render_Glyph(Font->glyph, FT_RENDER_MODE_NORMAL);
+					FT_UInt FallbackGlyphIndex = FT_Get_Char_Index(FontFallback, CharacterCode);
+					if (FallbackGlyphIndex != 0)
+					{
+						GlyphIndex = FallbackGlyphIndex;
+						GlyphFont = FontFallback;
+
+						// match the primary font's pixel size so the fallback glyph renders at the same size
+						if (Font->size != nullptr)
+						{
+							FT_Set_Pixel_Sizes(FontFallback, 0, Font->size->metrics.y_ppem);
+						}
+					}
+				}
+
+				FT_Load_Glyph(GlyphFont, GlyphIndex, FT_LOAD_DEFAULT);
+
+				if (GlyphFont->glyph->format != FT_GLYPH_FORMAT_BITMAP)
+				{
+					FT_Render_Glyph(GlyphFont->glyph, FT_RENDER_MODE_NORMAL);
 				}
 
-				// pen advance and kerning
+				// pen advance and kerning (kerning only applies between glyphs from the same face)
 				if (bRightJustify)
 				{
-					if (LastGlyph != 0)
+					if (LastGlyph != 0 && LastGlyphFont == GlyphFont)
+					{
+						FT_Get_Kerning(GlyphFont, GlyphIndex, LastGlyph, FT_KERNING_DEFAULT, &Kerning);
+					}
+					else
 					{
-						FT_Get_Kerning(Font, GlyphIndex, LastGlyph, FT_KERNING_DEFAULT, &Kerning);
+						Kerning.x = 0;
 					}
 
-					PenX -= (Font->glyph->metrics.horiAdvance - Kerning.x) >> 6;
+					PenX -= (GlyphFont->glyph->metrics.horiAdvance - Kerning.x) >> 6;
 				}
 				else
 				{
-					if (LastGlyph != 0)
+					if (LastGlyph != 0 && LastGlyphFont == GlyphFont)
+					{
+						FT_Get_Kerning(GlyphFont, LastGlyph, GlyphIndex, FT_KERNING_DEFAULT, &Kerning);
+					}
+					else
 					{
-						FT_Get_Kerning(Font, LastGlyph, GlyphIndex, FT_KERNING_DEFAULT, &Kerning);
+						Kerning.x = 0;
 					}
 				}
 
 				LastGlyph = GlyphIndex;
+				LastGlyphFont = GlyphFont;
 
 				// draw character
-				DrawCharacter(PenX, PenY, Font->glyph, CurTypeIndex, Red, Green, Blue);
+				DrawCharacter(PenX, PenY, GlyphFont->glyph, CurTypeIndex, Red, Green, Blue);
 
 				if (!bRightJustify)
 				{
-					PenX += (Font->glyph->metrics.horiAdvance - Kerning.x) >> 6;
+					PenX += (GlyphFont->glyph->metrics.horiAdvance - Kerning.x) >> 6;
 				}
 			}
 		}
 
 		// store rendered text as texture
 		SDL_UpdateTexture(SplashTexture, NULL, ScratchSpace, SplashWidth * SplashBPP);
 	}
 }
 
 #endif // WITH_EDITOR