CanvasLayer offset behavior

Godot Version

v4.2.2.stable.official.15073afe3

Question

How does CanvasLayer offset work in relation to viewport?

Im trying to center my “level” on the y axis of viewport. For that Im calculating a scaling factor by givin a margin in pixels on the X axis.

I then calculate the middle point with a simple formula of d = (y - y.a)/2 where “a” is the scalling and “y” is the viewport.y

This is the result and its clearly not in the middle of the screen. The pink line is a draw_line positioned at “viewport.y/2”.

This is the structure (Im trying to mimic my main project structure).

This is the Node code:

extends Node

var viewport_size_rec
var viewport_size
var device_safe_area
var safe_area: Rect2i
var safe_area_top
var scaleFactor
var new_position
var dpi
@export var CanvasLayer_Level: CanvasLayer

func _ready():
	_handle_screen_resize() 
	

	
func clamp_safe_area(sa:Vector2i, vp: Vector2i) -> Vector2i:
	if sa.x > vp.x:
		sa.x = vp.x
	if sa.y > vp.y:
		sa.y = vp.y
	return sa
	
	
func _handle_screen_resize():

	var os_name = OS.get_name()
	print(os_name)
	if os_name == "Android" || os_name == "Windows"  :
		
		viewport_size_rec 	= get_viewport().get_visible_rect().size
		viewport_size 		= get_viewport().size
		device_safe_area 	= DisplayServer.get_display_safe_area()	
		
		if os_name == "Windows"  :
			device_safe_area.position = Vector2i(0,0) #this depends on which screen will game launch
		device_safe_area.size = clamp_safe_area(device_safe_area.size, viewport_size)
		
		safe_area.position 	= device_safe_area.position
		safe_area.size 		= device_safe_area.size - Vector2i(960,0) # added safe marging on X axis
		scaleFactor 		= float(safe_area.size.x)/float(viewport_size.x)
		new_position 		= Vector2(
			safe_area.position.x,
			(viewport_size.y-(viewport_size.y * scaleFactor))*0.5)
			
		CanvasLayer_Level.set_scale(Vector2(scaleFactor,scaleFactor))
		CanvasLayer_Level.set_offset(new_position)	

		

		
		print("viewport_size_rec>",viewport_size_rec)
		print("viewport_size>",viewport_size)
		print("device_safe_area>",device_safe_area)
		print("safe_area>",safe_area)
		print("scaleFactor>",scaleFactor)
		print("new_position>",new_position)



and the draw_line code:

extends Control

func _draw():
	var viewport_size = get_viewport().get_visible_rect().size
	var mid_y = viewport_size.y / 2
	draw_line(Vector2(0, mid_y), Vector2(viewport_size.x, mid_y), Color.DEEP_PINK, 10)

The relevant project settings are:

Viewport Width: 800
Viewport Height: 360
Mode: Exclusive Fullscreen
Stretch Mode: canvas_items
Aspect: keep

This is my output:

Windows
viewport_size_rec>(800, 360)
viewport_size>(1920, 1080)
device_safe_area>[P: (0, 0), S: (1920, 1032)]
safe_area>[P: (0, 0), S: (960, 1032)]
scaleFactor>0.5
new_position>(0, 270)

My screen resolution is 1920x1080 and I set the “Safe Margin” to be -960 so that the scalling factor is 0.5 to make it easier to understand the pixel values.

My calculated new_position is (0, 270) which is correct given the formula but the offset is way off screen. What am I missing?

Thank you

SOLVED:
You have to divide

d = (y - y.a)/2

by the scaling of the viewport in relation to the rect. In this case:

d = (y - y.a) / 2
d = d / (viewport_size.y/viewport_size_rec.y)

I don’t know where in the docs it says that “offset” is affected by the viewport scaling but at least its fixed.