Page 1 of 1

Need help with upscaling to fullscreen resolution

Posted: Wed Nov 08, 2017 11:55 pm
by AlanGmnz
Hi! I'm making a little demo on LÖVE to learn the basics and I can't figure out how to make the game upscale when going into fullscreen. I'm aware that there's already many topics regarding the subject, but I need instructions step-by-step as I'm new to programming and math is definitely not my strongest point.

The native resolution of my demo is 480x270 -- which should be easy to scale to 1080p or even 4k -- so I set t.window.width and t.window.height in conf.lua accordingly. Everything seems normal when running on a window (see attachment demo-windowed), but in fullscreen (see demo-1366x768), contrary to what I'd expect, no stretching/scaling is done and the demo is simply displayed at the upper-left corner.

What I want to do are the following:
  1. Upscale (always with nearest neighbor both x and y) the native resolution (and of course all game assets/coordinates¹/etc) by 4x if the fullscreen resolution (based on desktop res) is 1920x1080, or 8x if it's 3840x2160.
  2. If fullscreen resolution is 1024x768 or 1366x768, keep it that way, upscale native res by 2x and center the image -- so it maintains both 1:1 scaling and aspect ratio. See demo-center.
  3. Undo everything when going back to windowed mode -- as I presume LÖVE won't do this automatically.
¹ So far I only got a sprite moving left/right but I think the engine gets coordinates from the current screen size instead of native resolution, is that right?

Things to consider:
  1. Windowed mode is always 480x270 not resizable.
  2. I don't need to support resolutions below 1024x768.
And I guess that's all. I hope I'm not asking anything complicated or laborsome. Any help will be appreciated!

P.S.: Here's my main.lua just in case:

Code: Select all

player = { x = 15, y = 140, speed = 150, img = nil }

function love.load()
	bg = love.graphics.newImage("assets/bg.png")
	
	player.img = love.graphics.newImage('assets/player.png')
end

function love.update(dt)
	if love.keyboard.isDown('escape') then
		love.event.push('quit')
	end
	
	if love.keyboard.isDown('left','a') then
		if player.x > 0 then
			player.x = player.x - (player.speed*dt)
		end
	elseif love.keyboard.isDown('right','d') then
		if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
			player.x = player.x + (player.speed*dt)
		end
	end
	
	if love.keyboard.isDown('up','w') then
		if player.y > 0 then
			player.y = player.y - (player.speed*dt)
		end
	elseif love.keyboard.isDown('down','s') then
		if player.y < (love.graphics.getHeight() - player.img:getHeight()) then
			player.y = player.y + (player.speed*dt)
		end
	end
end

function love.keypressed(key, isrepeat)
	if key == "f" then
		love.window.setFullscreen(not love.window.getFullscreen(), "desktop")
	end
end

function love.draw()
	love.graphics.draw(bg)

	love.graphics.draw(player.img, player.x, player.y)
end

Re: Need help with upscaling to fullscreen resolution

Posted: Thu Nov 09, 2017 11:58 am
by bartbes
Well, let's see how we could calculate the scale factor and offset:

Code: Select all

local xscale = actual_width/desired_width
local yscale = actual_height/desired_height
local scale = math.min(xscale, yscale)
-- Maybe also call math.floor, if you want
local xoffset = (actual_width-desired_width*scale)/2
local yoffset = (actual_height-desired_height*scale)/2
Now, given that scale we can make sure we draw in the right position, so put something like this at the start of draw:

Code: Select all

love.graphics.translate(xoffset, yoffset)
love.graphics.scale(scale, scale)
You may want to also use love.graphics.setScissor if you end up drawing things outside of the "box". An alternative might be to draw everything to a canvas, then scale that up, but the end result is the same, of course.

Re: Need help with upscaling to fullscreen resolution

Posted: Fri Nov 10, 2017 1:09 pm
by AlanGmnz
It worked! Thanks a lot. I've adapted your code to my needs and am leaving it here in case anyone else is looking for such a solution:

Code: Select all

function love.load()
	love.graphics.setDefaultFilter('nearest', 'nearest', 0)
end

function love.draw()
	-- Store desktop resolution
	local width, height = love.window.getDesktopDimensions()
	
	-- Resolution to be upscaled
	local xscale = 480
	local yscale = 270
	local scale = math.min(xscale, yscale)
	
	-- 2x windowed mode if below 720p
	if height < 720 then
		love.graphics.scale(2, 2) -- Integer scaling

	-- Otherwise go fullscreen and output the largest 1:1 image possible; x/yscale, width/height and scaling multipliers need to be manually adjusted
	elseif width == 1280 and height == 720 then
		love.window.setFullscreen(true)
		local xoffset = (width-xscale*2)/2 -- 960x540 in 1280x720 will produce black borders
		local yoffset = (height-yscale*2)/2 -- so image needs to be centered
		love.graphics.translate(xoffset, yoffset) -- needed when centering so coordinates remain consistent
		love.graphics.setScissor(xoffset, yoffset, width*2/2, height*2/2) -- keeps out-of-bound objects hidden, needs testing
		love.graphics.scale(2, 2)
	elseif width == 1600 and height == 900 then
		love.window.setFullscreen(true)
		local xoffset = (width-xscale*3)/2
		local yoffset = (height-yscale*3)/2
		love.graphics.setScissor(xoffset, yoffset, width*3/2, height*3/2)
		love.graphics.translate(xoffset, yoffset)
		love.graphics.scale(3, 3)
	-- and so on
		
	-- Perfect fullsize image, no centering needed
	elseif width == 1920 and height == 1080 then
		love.window.setFullscreen(true)
		love.graphics.scale(4, 4)
	end
	
	-- Draw your stuff
end