FilterMode Bluriness and Workarounds

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Redless
Prole
Posts: 1
Joined: Sun Dec 17, 2017 12:56 am

FilterMode Bluriness and Workarounds

Post by Redless »

Hey all,

When adjusting the FilterMode parameter (https://love2d.org/wiki/FilterMode), I can't get it the way I want. The "nearest" filter mode distorts the size of the pixels and the "linear" filter makes the image too blurry. What I'd really like is a linear filter that only interpolates the pixels on the border between the two colors, and doesn't interpolate all the pixels.

For example, imagine a 2x1 grayscale image with one white and one black pixel. It looks like [1,0]. If I try to interpolate that up to be 11 pixels wide, with the nearest filter, there would be a different number of white pixels and black pixels, six of one and five of the other. With the linear filter, there would be an equal number of black and white pixels, but there would also be 5 pixels in the middle at different stages of interpolation between white and black. What I'd like is a filter mode where there are 5 white pixels, 5 black pixels, and only the very middle pixel (on the border between black and white) is a gray value. This preserves the balance between the two colors while also not making the image unduly blurry.

Here are a couple of strategies I've been considering to get around this:
1. Store larger versions of the image. When scaling down, things aren't even close to as bad. Solves the problem perfectly but feels super cludgy. Lmao at storing a 640 by 640 version of a 64 by 64 piece of pixel art simply to get around this issue.
2. Manually scale the image up with the "nearest" filter to a multiple of the image's original size, draw it to a canvas, and then scale that canvas back down using the "linear" filter to reduce it to the size I want. Also feels cludgy, but probably going with this one. But then that's a bunch of boilerplate I have to call every time I load an image...

local scaleFactor = math.ceil(size / img:getHeight())
local canv = love.graphics.newCanvas(scaleFactor * img:getHeight(),
scaleFactor * img:getHeight())
love.graphics.setCanvas(canv)
img:setFilter("nearest")
love.graphics.setColor(1, 1, 1)
love.graphics.draw(img, 0, 0, 0, scaleFactor)
canv:setFilter("linear")

I'm wondering what veterans of LOVE2D would do here, if anything. Also, how easy would it be to submit a PR implementing a new filter that only interpolates on the border? Is that the kind of thing that would get merged, assuming that I wrote it properly?
Andlac028
Party member
Posts: 174
Joined: Fri Dec 14, 2018 2:27 pm
Location: Slovakia

Re: FilterMode Bluriness and Workarounds

Post by Andlac028 »

Redless wrote: Wed May 03, 2023 10:29 am I'm wondering what veterans of LOVE2D would do here, if anything. Also, how easy would it be to submit a PR implementing a new filter that only interpolates on the border? Is that the kind of thing that would get merged, assuming that I wrote it properly?
You can submit it as feature request in LÖVE's issue tracker.
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: FilterMode Bluriness and Workarounds

Post by zorg »

There was a thread before that kinda had issues with blockyness and rotation, and there was a few solutions given in there, including a shader one... might be somewhat relevant here as well.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
pgimeno
Party member
Posts: 3673
Joined: Sun Oct 18, 2015 2:58 pm

Re: FilterMode Bluriness and Workarounds

Post by pgimeno »

You can also upscale instead of downscale. If your zoom magnification factor is F, find floor(F) and upscale the image with the nearest filter using that factor (to a canvas, for example). Then you can scale the image to the target scale by applying a factor of F/floor(F) using the linear filter and you will get the desired effect.

For example, if your image is 64x64 and your zoom factor is 3.6, scale the image by a factor of floor(3.6) = 3 with nearest, obtaining a 192x192 image. Then upscale that with a factor of 3.6 / 3 = 1.2 with linear to obtain the final image.

I've run into this issue too and I thought about making a shader for it, but never got into that; I went with the scaling solution.
User avatar
slime
Solid Snayke
Posts: 3166
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: FilterMode Bluriness and Workarounds

Post by slime »

Andlac028 wrote: Wed May 03, 2023 2:20 pm
Redless wrote: Wed May 03, 2023 10:29 am I'm wondering what veterans of LOVE2D would do here, if anything. Also, how easy would it be to submit a PR implementing a new filter that only interpolates on the border? Is that the kind of thing that would get merged, assuming that I wrote it properly?
You can submit it as feature request in LÖVE's issue tracker.
This wouldn't go anywhere I think. The filter modes love currently exposes are part of GPU hardware itself, they're fixed-function and there aren't any more of them.

Using a shader (or your other approaches if they work) in user code seems like a fine choice though.
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: FilterMode Bluriness and Workarounds

Post by Sasha264 »

Redless wrote: Wed May 03, 2023 10:29 am 2. Manually scale the image up with the "nearest" filter to a multiple of the image's original size, draw it to a canvas, and then scale that canvas back down using the "linear" filter to reduce it to the size I want.
I also did something like that in 2017 viewtopic.php?f=4&t=84579&p=216914
Nowadays I would do that with shader:

Code: Select all

local image
local shader
local time = 0
local scale = 10
local mult = 1.0 -- in case of pixel graphics
                 -- it can be meaningful to use mult value about 1.25 - 2.0
                 -- to make result less-blurry-more-pixelly
                 -- with 1.0 it's just basic bilinear interpolation

function love.load()
	image = love.graphics.newImage("spaceship.png")
	image:setFilter("nearest", "nearest")

	local code = love.filesystem.read("shader.frag")
	shader = love.graphics.newShader(code)

	love.window.setMode(1200, 600, { resizable = false, vsync = true, borderless = false })
end

function love.update(dt)
	time = time + 0.1 * dt
	love.window.setTitle("Nearest vs NearestLinear " .. love.timer.getFPS() .. " fps")
end

function love.draw()
	local ox = math.floor(image:getWidth() / 2)
	local oy = math.floor(image:getHeight() / 2)

	love.graphics.setShader()
	love.graphics.draw(image, 300, 300, time, scale, scale, ox, oy)

	love.graphics.setShader(shader)
	shader:send("size", { mult * scale * image:getWidth(), mult * scale * image:getHeight() })
	love.graphics.draw(image, 900, 300, time, scale, scale, ox, oy)
end

Code: Select all

extern vec2 size;

vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
	vec2 step = vec2(1.0) / size;
	vec2 center = texcoord * size - 0.5;

	vec2 coord00 = floor(center) + 0.5;
	vec2 coord10 = coord00 + vec2(1.0, 0.0);
	vec2 coord01 = coord00 + vec2(0.0, 1.0);
	vec2 coord11 = coord00 + vec2(1.0, 1.0);

	vec4 texel00 = Texel(tex, coord00 * step);
	vec4 texel10 = Texel(tex, coord10 * step);
	vec4 texel01 = Texel(tex, coord01 * step);
	vec4 texel11 = Texel(tex, coord11 * step);

	vec2 lerp = fract(center);
	vec4 texel0 = mix(texel00, texel10, lerp.x);
	vec4 texel1 = mix(texel01, texel11, lerp.x);
	vec4 texel = mix(texel0, texel1, lerp.y);

	return vcolor * texel;
}
result.png
result.png (69.3 KiB) Viewed 2741 times
NearestLinear.love
(1.7 KiB) Downloaded 78 times
Some coordinates are hardcoded for code clarity.
There are 3 caveats though:
  1. This is slightly slower than hardware bilinear texture filtering, but that shouldn't be a problem.
  2. You still need to supply the shader with value based on particular image size, which is "not optimal" when you have a lot of images with different sizes.
  3. In my example image has 1 pixel transparent borders around it. Without them there will be issues around image rectangle edges. Which is important only in the case of rotating + scaling, not only scaling, I think...
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: FilterMode Bluriness and Workarounds

Post by zorg »

By the way, i found the thread i mentioned before in my bookmarks: viewtopic.php?f=5&t=89442
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: FilterMode Bluriness and Workarounds

Post by Sasha264 »

zorg wrote: Fri May 05, 2023 4:57 am By the way, i found the thread i mentioned before in my bookmarks: viewtopic.php?f=5&t=89442
There is an interesting idea in that thread: to expand the sprite rect in the vertex shader in order to get rid of border problems, nice! :nyu:
I wonder if there's a way to modify it so that it doesn't rely on the angle parameter supplied by the user, which can be little uncomfortable. There is transform_projection in the vertex shader already, which kind of includes angle info...
User avatar
pgimeno
Party member
Posts: 3673
Joined: Sun Oct 18, 2015 2:58 pm

Re: FilterMode Bluriness and Workarounds

Post by pgimeno »

Sasha264 wrote: Fri May 05, 2023 6:37 pm I wonder if there's a way to modify it so that it doesn't rely on the angle parameter supplied by the user, which can be little uncomfortable. There is transform_projection in the vertex shader already, which kind of includes angle info...
Is there?

I mean, I thought autobatching screws that up, because the transform is now performed CPU-side, and the shader receives a plain an identity matrix.

But if it's really there, you can just transform a unit vector pointing right with the view matrix, and you get the sine and the cosine of the angle.
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: FilterMode Bluriness and Workarounds

Post by Sasha264 »

pgimeno wrote: Fri May 05, 2023 8:42 pm I mean, I thought autobatching screws that up, because the transform is now performed CPU-side, and the shader receives a plain an identity matrix.
This is quite intriguing...
It appears to be a crucial aspect to consider for anyone planning to develop a vertex shader! :awesome:
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 5 guests