I want to draw pixel-perfect (lines and pixels) on a VisualElement.
I did this before in uGUI by drawing on a Texture2D that had the same size in device-pixels as the RectTransform.
The same approach would also work for VisualElements. I can create and assign a Texture to the VisualElement and draw on this texture.
Problem is that the result is scaled because I don’t know the final device-pixel size. As a result, everything is blurry.
How do I find the final, rendered size in device-pixels for a VisualElement?
For example, when I have a VisualElement with width “100px” then these 100 px will be scaled for the device such that it may be more than 100 device-pixels.
Are there other ways to draw pixels on a VisualElement?
If you want the elements to scale you should use %. After the element has been created, you can use VisualElement.worldBound.size (or other worldBound properties) to get the width and height. VisualElement.resolvedStyle might also work but I have not tested it yet. To make sure the element has been created you should subscribe to the GeometryChangedEvent or AttatchToPanelEvent.
VisualElement.worldBound.size and VisualElement.resolvedStyle.width is both given in reference resolution (of the PanelSettings of the UIDocument). I want this in device pixels.
However, I think I could multiply resolvedStyle.width by the factor (device-width / reference-width) to get the actual width in device-pixels.
I guess the UIElements API is always using the reference resolution?
You’re right, the layout values are always using scaled points units. The actual scale is a mix of the PanelSettings scaling options and the device’s dpi values.
Right now, to get the pixel-size of an element, you can multiply the resolvedStyle.width/height by the scaling ratio.
This is a bit tricky to get at the moment, but you can fetch the scaling ratio by casting the element’s panel to a
BaseVisualElementPanel, then access its scaledPixelsPerPoint property.
Is there an updated simpler method to get the scaling factor?
I’m having trouble to access the BaseVisualElementPanel properties since its burried in the UIElements namespace.
I can think of two methods to determine the scaling factor without it:
Restrict to a scaling mode i.e. scale by width and then calculate the ratio between reference resolution and screen resolution
Generate a background visual element that spans the whole screen get its size and combine with the screen size.
Both methods are cumbersome or restrictive. Would love a simpler method.
Kinda wish Unity would add something like that to RuntimePanelUtils, but I guess there might be contexts in which it doesn’t work or gets more complicated.
a simple solution, if you know that your UI Document is full-screen, would be: screenToPanelRatio = Screen.width / fullScreenUIDocument.rootVisualElement.resolvedStyle.width;
Then you can find the size of you panel in pixels by multiplying it by this ratio.
Also, don’t forget that the .resolvedStyle will only give you correct values after the on GeometryChangedEvent callback, just like @Nexer8 has mentioned.
Very happy to find this answer, until I discovered BaseRuntimePanel and RuntimePanel are internals
As @burningmime proposed, I wished it would be supported in the RuntimePanelUtils, at least for the classic overlay use case (I saw the more complex use cases ).
public static Rect absRect(Vector2 a, Vector2 b)
{
Vector2 min = new(Math.Min(a.x, b.x), Math.Min(a.y, b.y));
Vector2 size = new(Math.Abs(a.x - b.x), Math.Abs(a.y - b.y));
return new Rect(min, size);
}
But the basic idea is just the same. Get the top-left and bottom-right points in screen space.