Download

Scaling an image to the size of parent Canvas Panel in realtime

Hi,

I came recently from Unity world, and I felt that UMG is somewhat similar to Unity UI, but more powerful.
But UMG programming seems to be more complicated than I expected.

I need to do a very basic task. I have a simple hierarchy of Canvas Panel and a child image. I just want to scale the child image to the screen size no matter what resolution changes.
I understand I can just use anchors to the corners of the screen and I will get it working, but I want to understand how size works in UMG for more advanced task I want to do after this.
Here is my dead simple setup

In the designer, setting image size to 1920x1080 while having the anchor at left-upper corner seems to do the trick.
But when I play the game in the viewport I see black borders, or extending image beyond the view depending on the viewport size. (I tried to attach an image of how it looks in playmode, but the forum gives me <p>Database Error</p>)

I tried writing this code in my C++ widget class, and setting the comicImage->Slot size to viewport size, resolution size, ratio size, and it’s always wrong. comicImage is a pointer to the Image in the hierarchy.

I want to understand what coordinates does the UCanvasPanelSlot::SetSize method use, also what is the x,y coordinate for the lower-right corner of the widget?



UCanvasPanelSlot* imageSlot = dynamic_cast<UCanvasPanelSlot*>(comicImage->Slot);
	
FVector2D ViewportSize = FVector2D(GEngine->GameViewport->Viewport->GetSizeXY());
FVector2D Resolution = FVector2D(GSystemResolution.ResX, GSystemResolution.ResY);

FVector2D newSize(Resolution.X * DesignTimeSize.X / ViewportSize.X, Resolution.Y * DesignTimeSize.Y / ViewportSize.Y);

imageSlot->SetSize(newSize); //setting it to ViewportSize or Resolution, also gives weird results


Thanks for advance.

  1. To keep the image matching the aspect ratio, you should put the image inside a ScaleBox and set the scaling rule to be Size to Fit, or Size to Fill, depending on if you want it cropped.

  2. Widgets are not in pixel or Viewport coordinates. They’re in Slate Units, which are just an arbitrary space essentially because they may be inside a widget that scales its contents. As is the case for all widgets in the game by default because of the DPI scale curve in your project settings, so you’ve got to always convert pixel units to slate unit space of the widget you’re dealing with in order to be accurate.

I was going to suggest you use the utility functions to convert viewport to local geometry space of your widget in Tick, but turns out I don’t have those functions written; You can use these, I haven’t tested them, but they aught to get the job done.


void USlateBlueprintLibrary::ScreenToWidgetLocal(UObject* WorldContextObject, const FGeometry& Geometry, FVector2D ScreenPosition, FVector2D& LocalCoordinate)
{
	FVector2D AbsoluteCoordinate;
	ScreenToWidgetAbsolute(WorldContextObject, ScreenPosition, AbsoluteCoordinate);

	LocalCoordinate = Geometry.AbsoluteToLocal(AbsoluteCoordinate);
}

void USlateBlueprintLibrary::ScreenToWidgetAbsolute(UObject* WorldContextObject, FVector2D ScreenPosition, FVector2D& AbsoluteCoordinate)
{
	UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject);
	if ( World && World->IsGameWorld() )
	{
		if ( UGameViewportClient* ViewportClient = World->GetGameViewport() )
		{
			if ( FViewport* Viewport = ViewportClient->Viewport )
			{
				FVector2D ViewportSize;
				ViewportClient->GetViewportSize(ViewportSize);

				const FVector2D NormalizedViewportCoordinates = ScreenPosition / ViewportSize;

				const FIntPoint VirtualDesktopPoint = Viewport->ViewportToVirtualDesktopPixel(NormalizedViewportCoordinates);
				AbsoluteCoordinate = FVector2D(VirtualDesktopPoint);
				return;
			}
		}
	}

	AbsoluteCoordinate = FVector2D(0, 0);
}

So if you wanted to know what the bottom right of the viewport was in your local space, just feed the viewport size into ViewportToLocal.

Thank you very much.
However I am not sure what to pass to the geometry parameter.
I tried to pass the UImage geometry, but that didn’t work correctly.



FGeometry Geometry;
	
bool result = canvasPanel->GetGeometryForSlot(0, Geometry);
	
UComicViewerWidget::ScreenToWidgetLocal(this, Geometry, ViewportSize, newSize);//passing ViewportSize to get the lower right corner. this is UUserWidget reference

imageSlot->SetPosition(newSize);//I expect the image to disappear, because it's position is to the lower-right corner, with the anchor to the left upper corner, but it doesn't disappear, it just jumps around.


Is there a geometry associated with UUserWidget or UCanvasPanel? How can I get it?

My bad. The tick event takes FGeometry parameter, I used it and finally it worked!
Now I can position stuff relative to the upper left corner and size of the widget, which exactly what I needed.