Allegro.cc - Online Community

Allegro.cc Forums » The Depot » Simple shader to scale pixel art

This thread is locked; no one can reply to it. rss feed Print
Simple shader to scale pixel art
SiegeLord
Member #7,827
October 2006
avatar

EDIT: Downloads updated

I was thinking about the best way to scale pixel art. Obviously any non-integral scale factor will produce "bad" results, but I wanted to see if I could come up with something that was better than nearest sampling and linear interpolation. The idea I had was to essentially do anti-aliasing on the image: i.e. treat each pixel of the source image as a square polygon and then draw an output pixel by blending between the source image pixels in proportion to the area of the output pixel that they cover. Here's a result of that line of thought... I think it looks better than both linear and nearest sampling and I think I'm going to use it for my games.

You're going to need a 5.1 branch Allegro to compile this (5.1.6 snapshot works). The code and shaders are in public domain.

Source
Windows Binary

To run the example binary you're going to need the D3D runtime installed to use the D3D backend (if you play video games made in this century you'll have it installed most likely), otherwise use allegro.cfg to specify the OpenGL backend ([graphics] driver = opengl).

Controls:

Left/Right - Switch the filtering method (nearest/linear/shader)
B - Toggle bitmap (Between a Sūpā Metoroido screenshot I lifted from Wikipedia and a pixel grid pattern)
Window border - You can resize the window!

Screenshots:

Nearest filtering:
{"name":"607339","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/3\/03283239c326659faf9429d8653ecdbe.png","w":812,"h":631,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/3\/03283239c326659faf9429d8653ecdbe"}607339
Linear filtering:
{"name":"607340","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/791531d4f0750c65e2d2509f0a0c46a3.png","w":812,"h":631,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/7\/9\/791531d4f0750c65e2d2509f0a0c46a3"}607340
Shader filtering:
{"name":"607338","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/7\/174a467f732be49c17926e9bdcabdc41.png","w":812,"h":631,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/1\/7\/174a467f732be49c17926e9bdcabdc41"}607338

EDIT: There was a bug in the shader code that made the output not be identical to nearest filtering for integral scale factors. It is now fixed, I think.

EDIT2: Sample from the center of the pixel, not from the corner. Hopefully fixing the variability in output across platforms.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

pkrcel
Member #14,001
February 2012

This look nice!

You are basicaly interpolating only at the "borders" of the scaled pixel (based on the actual area this scaled pixel covers in the resulting image)?

I've had a look at the GLSL code but I am not that keen on interpreting it.

It is unlikely that Google shares your distaste for capitalism. - Derezo
If one had the eternity of time, one would do things later. - Johan Halmén

SiegeLord
Member #7,827
October 2006
avatar

pkrcel said:

You are basicaly interpolating only at the "borders" of the scaled pixel (based on the actual area this scaled pixel covers in the resulting image)?

Yes, exactly.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Peter Wang
Member #23
April 2000

Looks quite nice in the screenshot but there's something wrong with the y offset here:
{"name":"607342","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/b\/8b39f7a5e67af62cd9426259d5425f16.png","w":800,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/8\/b\/8b39f7a5e67af62cd9426259d5425f16"}607342

I'm also curious why you need to recreate the shader on resize.

SiegeLord
Member #7,827
October 2006
avatar

Looks quite nice in the screenshot but there's something wrong with the y offset here:

Is this with OpenGL or Direct3D? I'm having bizzare outputs with Direct3D at the moment (on a different computer than where I tested this initially)... not 100% sure why.

Quote:

I'm also curious why you need to recreate the shader on resize.

It crashed for Direct3D otherwise... possibly an Allegro bug.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Kris Asick
Member #1,424
July 2001

You know, a similar effect can be achieved by first enlarging with nearest-neighbour filtering by 300% or 400%, then scaling back down with linear filtering. This is the technique I was going to use back when I was considering making some very-low-resolution games. ;)

--- Kris Asick (Gemini)
--- http://www.pixelships.com

SiegeLord
Member #7,827
October 2006
avatar

You know, a similar effect can be achieved by first enlarging with nearest-neighbour filtering by 300% or 400%, then scaling back down with linear filtering. This is the technique I was going to use back when I was considering making some very-low-resolution games. ;)

This is exactly the same, except you first (conceptually) enlarge it infinitely. In fact, this is how I first tested that this might produce something nice. The shader approach has the advantage of using less memory, however.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

Doesn't resizing the display cause a device reset? Pretty sure it does. If so, then you have to create shaders and any other GPU resources that Allegro doesn't do for you. So pretty much anything except bitmaps Allegro created.

Peter Wang
Member #23
April 2000

I was using OpenGL on Linux with nvidia proprietary driver.

As usual, it would be nice for Allegro to handle the device reset (and other platform specific quirks) for you - if it's reasonable.

SiegeLord
Member #7,827
October 2006
avatar

I was using OpenGL on Linux with nvidia proprietary driver.

That is disturbing. I'll have to run these shaders through more stringent tests rather than just compiling for my drivers... hopefully it's some error in the shader and not a fundamental limitation of the approach.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Mark Oates
Member #1,146
March 2001
avatar

Yea, that last one looks really nice. :)

Kris Asick
Member #1,424
July 2001

SiegeLord said:

This is exactly the same, except you first (conceptually) enlarge it infinitely. In fact, this is how I first tested that this might produce something nice. The shader approach has the advantage of using less memory, however.

I'll agree on the using less memory thing. ;)

I did discover at one point though that once you expand the image past the size you intend to resize to during enlargement, going any bigger has no effect on the image quality. IE: 320x240 -> 1280x960 -> 1024x768 looks exactly the same as 320x240 -> 2560x1920 -> 1024x768 :P

One advantage to doing it that way is you don't need to use shaders, granted, now that I actually know how to use shaders myself, I don't think I'd go back to not using them for things like this. 8-)

--- Kris Asick (Gemini)
--- http://www.pixelships.com

SiegeLord
Member #7,827
October 2006
avatar

Peter, could you re-download the shaders and try again? I altered how the texture sampling happens... it fixed my issues with D3D, so it might fix them for you as well.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

I don't know if this is anything like it, but one algorithm that produces pretty good results, especially when scaling up by a small amount is a weighted average scale. It'd probably be easy and fast with a shader...

Basically say for example you're scaling up by 1/4, so an extra pixel for every 3 pixels... the 4th pixel would be 1/2 taken from the 3rd pixel and 1/2 taken from the 5th (IIRC, it's been a while since I implemented it.)

It doesn't produce very much blur, at least compared to linear filtering.

EDIT: Have an old blog post showing some screenshots: http://www.nooskewl.com/content/monster-2-wiz-scaling

SiegeLord
Member #7,827
October 2006
avatar

EDIT: Have an old blog post showing some screenshots: http://www.nooskewl.com/content/monster-2-wiz-scaling

Yep, that's exactly the same algorithm, as far as I can tell.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Trent Gamblin
Member #261
April 2000
avatar

Ah. I had forgotten all about it TBH. I may have another crack at it in shaders instead of software at some point. (BTW, I got the idea from MattyMatt.)

Peter Wang
Member #23
April 2000

SiegeLord said:

Peter, could you re-download the shaders and try again? I altered how the texture sampling happens... it fixed my issues with D3D, so it might fix them for you as well.

Works.

Attached screenshots from a quick-ish hack of Dune Dynasty, 1.5x zoom. It's supposed have no effect at integer scales, yes? (Don't worry about the elements outside the game viewport; those are drawn at scale=2.0 but using the shader with scale=1.5.)

SiegeLord
Member #7,827
October 2006
avatar

It's supposed have no effect at integer scales, yes?

That's correct.

Quote:

Attached screenshots from a quick-ish hack of Dune Dynasty, 1.5x zoom.

Is the first one (*48.png) with the shader and the second with nearest?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Peter Wang
Member #23
April 2000

Yes. I should compare it with linear interpolation as well.

Go to: