/** * For the given text, constructs a mesh to be used by the vertex factory for rendering. */ bool FTextRenderSceneProxy::BuildStringMesh( TArray<FDynamicMeshVertex>& OutVertices, TArray<uint16>& OutIndices ) { if(!Font || Text.IsEmpty()) { return false; } float FirstLineHeight = -1; // Only kept around for legacy positioning support float StartY = 0; const float CharIncrement = ( (float)Font->Kerning + HorizSpacingAdjust ) * XScale; float LineX = 0; const int32 PageIndex = 0; FTextIterator It(*Text.ToString()); while (It.NextLine()) { FVector2D LineSize = ComputeTextSize(It, Font, XScale, YScale, HorizSpacingAdjust); float StartX = ComputeHorizontalAlignmentOffset(LineSize, HorizontalAlignment); if (FirstLineHeight < 0) { FirstLineHeight = LineSize.Y; } LineX = 0; int32 Ch; while (It.NextCharacterInLine(Ch)) { Ch = (int32)Font->RemapChar(Ch); if(!Font->Characters.IsValidIndex(Ch + PageIndex)) { continue; } FFontCharacter& Char = Font->Characters[Ch + PageIndex]; if(!Font->Textures.IsValidIndex(Char.TextureIndex)) { continue; } UTexture2D* Tex = Font->Textures[Char.TextureIndex]; if(Tex) { FIntPoint ImportedTextureSize = Tex->GetImportedSize(); FVector2D InvTextureSize(1.0f / (float)ImportedTextureSize.X, 1.0f / (float)ImportedTextureSize.Y); const float X = LineX + StartX; const float Y = StartY + Char.VerticalOffset * YScale; float SizeX = Char.USize * XScale; const float SizeY = Char.VSize * YScale; const float U = Char.StartU * InvTextureSize.X; const float V = Char.StartV * InvTextureSize.Y; const float SizeU = Char.USize * InvTextureSize.X; const float SizeV = Char.VSize * InvTextureSize.Y; float Left = X; float Top = Y; float Right = X + SizeX; float Bottom = Y + SizeY; // axis choice and sign to get good alignment when placed on surface FVector4 V0 = FVector4(0, -Left, -Top, 0); FVector4 V1 = FVector4(0, -Right, -Top, 0); FVector4 V2 = FVector4(0, -Left, -Bottom, 0); FVector4 V3 = FVector4(0, -Right, -Bottom, 0); FVector TangentX(0, -1, 0); FVector TangentY(0, 0, -1); FVector TangentZ(1, 0, 0); int32 V00 = OutVertices.Add(FDynamicMeshVertex(V0, TangentX, TangentZ, FVector2D(U, V), TextRenderColor)); int32 V10 = OutVertices.Add(FDynamicMeshVertex(V1, TangentX, TangentZ, FVector2D(U + SizeU, V), TextRenderColor)); int32 V01 = OutVertices.Add(FDynamicMeshVertex(V2, TangentX, TangentZ, FVector2D(U, V + SizeV), TextRenderColor)); int32 V11 = OutVertices.Add(FDynamicMeshVertex(V3, TangentX, TangentZ, FVector2D(U + SizeU, V + SizeV), TextRenderColor)); check(V00 < 65536); check(V10 < 65536); check(V01 < 65536); check(V11 < 65536); OutIndices.Add(V00); OutIndices.Add(V11); OutIndices.Add(V10); OutIndices.Add(V00); OutIndices.Add(V01); OutIndices.Add(V11); // if we have another non-whitespace character to render, add the font's kerning. int32 NextChar; if( It.Peek(NextChar) && !FChar::IsWhitespace(NextChar) ) { SizeX += CharIncrement; } LineX += SizeX; } } // Move Y position down to next line. If the current line is empty, move by max char height in font StartY += LineSize.Y > 0 ? LineSize.Y : Font->GetMaxCharHeight(); } // Avoid initializing RHI resources when no vertices are generated. return (OutVertices.Num() > 0); }
// @param It must be a valid initialized text iterator // @param Font 0 is silently ignored FVector2D ComputeTextSize(FTextIterator It, class UFont* Font, float XScale, float YScale, float HorizSpacingAdjust) { FVector2D Ret(0, 0); if(!Font) { return Ret; } const float CharIncrement = ( (float)Font->Kerning + HorizSpacingAdjust ) * XScale; float LineX = 0; const int32 PageIndex = 0; int32 Ch; while (It.NextCharacterInLine(Ch)) { Ch = (int32)Font->RemapChar(Ch); if(!Font->Characters.IsValidIndex(Ch + PageIndex)) { continue; } FFontCharacter& Char = Font->Characters[Ch + PageIndex]; if(!Font->Textures.IsValidIndex(Char.TextureIndex)) { continue; } UTexture2D* Tex = Font->Textures[Char.TextureIndex]; if(Tex) { FIntPoint ImportedTextureSize = Tex->GetImportedSize(); FVector2D InvTextureSize(1.0f / (float)ImportedTextureSize.X, 1.0f / (float)ImportedTextureSize.Y); const float X = LineX; const float Y = Char.VerticalOffset * YScale; float SizeX = Char.USize * XScale; const float SizeY = Char.VSize * YScale; const float U = Char.StartU * InvTextureSize.X; const float V = Char.StartV * InvTextureSize.Y; const float SizeU = Char.USize * InvTextureSize.X; const float SizeV = Char.VSize * InvTextureSize.Y; float Right = X + SizeX; float Bottom = Y + SizeY; Ret.X = FMath::Max(Ret.X, Right); Ret.Y = FMath::Max(Ret.Y, Bottom); // if we have another non-whitespace character to render, add the font's kerning. int32 NextCh; if( It.Peek(NextCh) && !FChar::IsWhitespace(NextCh) ) { SizeX += CharIncrement; } LineX += SizeX; } } return Ret; }
void FCanvasTextItem::DrawStringInternal( class FCanvas* InCanvas, const FVector2D& DrawPos, const FLinearColor& InColour ) { DrawnSize.X = 0.0f; FHitProxyId HitProxyId = InCanvas->GetHitProxyId(); FTexture* LastTexture = NULL; UTexture2D* Tex = NULL; FVector2D InvTextureSize(1.0f,1.0f); const TArray< TCHAR >& Chars = Text.ToString().GetCharArray(); // Draw all characters in string. for( int32 i=0; i < TextLen; i++ ) { int32 Ch = (int32)Font->RemapChar(Chars[i]); // Process character if it's valid. if( Font->Characters.IsValidIndex(Ch) ) { FFontCharacter& Char = Font->Characters[Ch]; if( Font->Textures.IsValidIndex(Char.TextureIndex) && (Tex=Font->Textures[Char.TextureIndex])!=NULL && Tex->Resource != NULL ) { if( LastTexture != Tex->Resource || BatchedElements == NULL ) { FBatchedElementParameters* BatchedElementParameters = NULL; BatchedElements = InCanvas->GetBatchedElements(FCanvas::ET_Triangle, BatchedElementParameters, Tex->Resource, BlendMode, FontRenderInfo.GlowInfo); check(BatchedElements != NULL); // trade-off between memory and performance by pre-allocating more reserved space // for the triangles/vertices of the batched elements used to render the text tiles //BatchedElements->AddReserveTriangles(TextLen*2,Tex->Resource,BlendMode); //BatchedElements->AddReserveVertices(TextLen*4); FIntPoint ImportedTextureSize = Tex->GetImportedSize(); InvTextureSize.X = 1.0f / (float)ImportedTextureSize.X; InvTextureSize.Y = 1.0f / (float)ImportedTextureSize.Y; } LastTexture = Tex->Resource; const float X = DrawnSize.X + DrawPos.X; const float Y = DrawPos.Y + + Char.VerticalOffset * YScale; float SizeX = Char.USize * XScale; const float SizeY = Char.VSize * YScale; const float U = Char.StartU * InvTextureSize.X; const float V = Char.StartV * InvTextureSize.Y; const float SizeU = Char.USize * InvTextureSize.X; const float SizeV = Char.VSize * InvTextureSize.Y; float Left, Top, Right, Bottom; Left = X * Depth; Top = Y * Depth; Right = (X + SizeX) * Depth; Bottom = (Y + SizeY) * Depth; int32 V00 = BatchedElements->AddVertex( FVector4( Left, Top, 0.f, Depth ), FVector2D( U, V ), InColour, HitProxyId ); int32 V10 = BatchedElements->AddVertex( FVector4( Right, Top, 0.0f, Depth ), FVector2D( U + SizeU, V ), InColour, HitProxyId ); int32 V01 = BatchedElements->AddVertex( FVector4( Left, Bottom, 0.0f, Depth ), FVector2D( U, V + SizeV ), InColour, HitProxyId); int32 V11 = BatchedElements->AddVertex( FVector4( Right, Bottom, 0.0f, Depth ), FVector2D( U + SizeU, V + SizeV ), InColour, HitProxyId); BatchedElements->AddTriangle(V00, V10, V11, Tex->Resource, BlendMode, FontRenderInfo.GlowInfo); BatchedElements->AddTriangle(V00, V11, V01, Tex->Resource, BlendMode, FontRenderInfo.GlowInfo); // if we have another non-whitespace character to render, add the font's kerning. if ( Chars[i+1] && !FChar::IsWhitespace(Chars[i+1]) ) { SizeX += CharIncrement; } // Update the current rendering position DrawnSize.X += SizeX; } } } }
bool FSpriteEditorViewportClient::ConvertMarqueeToSourceTextureSpace(/*out*/ FIntPoint& OutStartPos, /*out*/ FIntPoint& OutDimension) { FSpriteGeometryEditMode* GeometryEditMode = ModeTools->GetActiveModeTyped<FSpriteGeometryEditMode>(FSpriteGeometryEditMode::EM_SpriteGeometry); check(GeometryEditMode); const FVector2D MarqueeStartPos = GeometryEditMode->GetMarqueeStartPos(); const FVector2D MarqueeEndPos = GeometryEditMode->GetMarqueeEndPos(); bool bSuccessful = false; UPaperSprite* Sprite = SourceTextureViewComponent->GetSprite(); UTexture2D* SpriteSourceTexture = Sprite->GetSourceTexture(); if (SpriteSourceTexture != nullptr) { // Calculate world space positions FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(Viewport, GetScene(), EngineShowFlags)); FSceneView* View = CalcSceneView(&ViewFamily); const FVector StartPos = View->PixelToWorld(MarqueeStartPos.X, MarqueeStartPos.Y, 0); const FVector EndPos = View->PixelToWorld(MarqueeEndPos.X, MarqueeEndPos.Y, 0); // Convert to source texture space to work out the pixels dragged FVector2D TextureSpaceStartPos = Sprite->ConvertWorldSpaceToTextureSpace(StartPos); FVector2D TextureSpaceEndPos = Sprite->ConvertWorldSpaceToTextureSpace(EndPos); if (TextureSpaceStartPos.X > TextureSpaceEndPos.X) { Swap(TextureSpaceStartPos.X, TextureSpaceEndPos.X); } if (TextureSpaceStartPos.Y > TextureSpaceEndPos.Y) { Swap(TextureSpaceStartPos.Y, TextureSpaceEndPos.Y); } const FIntPoint SourceTextureSize(SpriteSourceTexture->GetImportedSize()); const int32 SourceTextureWidth = SourceTextureSize.X; const int32 SourceTextureHeight = SourceTextureSize.Y; FIntPoint TSStartPos; TSStartPos.X = FMath::Clamp<int32>((int32)TextureSpaceStartPos.X, 0, SourceTextureWidth - 1); TSStartPos.Y = FMath::Clamp<int32>((int32)TextureSpaceStartPos.Y, 0, SourceTextureHeight - 1); FIntPoint TSEndPos; TSEndPos.X = FMath::Clamp<int32>((int32)TextureSpaceEndPos.X, 0, SourceTextureWidth - 1); TSEndPos.Y = FMath::Clamp<int32>((int32)TextureSpaceEndPos.Y, 0, SourceTextureHeight - 1); const FIntPoint TextureSpaceDimensions = TSEndPos - TSStartPos; if ((TextureSpaceDimensions.X > 0) || (TextureSpaceDimensions.Y > 0)) { OutStartPos = TSStartPos; OutDimension = TextureSpaceDimensions; bSuccessful = true; } } return bSuccessful; }