Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » scaling low-res screens

Credits go to Chris Katko, dthompson, and Edgar Reynaldo for helping out!
This thread is locked; no one can reply to it. rss feed Print
scaling low-res screens
Peter Hull
Member #1,136
March 2001

I am looking at making a 'retro' thing with pixel graphics - the base resolution is 160x120 pixels. Obviously some scaling will be needed - this is smaller than some icons on modern screens!
What's the best way to this?
Draw everything onto an off-screen bitmap and then stretch it to fill the window?
Or scale all the images as they are loaded and draw them 1:1
Or something else (apply a transformation to the screen?)

Chris Katko
Member #1,881
January 2002
avatar

Render normal then scale up using a selection of pixel filters.

Pixel-perfect (can't remember exact term, just repeat the same source pixel, no blending)
Bilinear (mushy)
2xSAI / HQ2x / HQ4x
CRT shader

That's the usual stuff people offer.

If it's 4:3, add black borders or add side artwork (but definitely give option for black borders).

-> Consider picking a proper widescreen "low res" mode (or supporting it as an option). In 99% of cases, there's no need to leave dead areas in people's monitors. 4:3 isn't some magical ratio, and people will likely "feel" the retro just fine in 16:9--and worse, if you show black bars or artwork it INSTANTLY brings your eyes attention to the fact that it's 4:3, which means they're focusing on the bars and not the game experience! (Usually) the last thing you want in entertainment is to remind everyone that they're sitting in a chair and staring at a screen instead of "in the world".

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

dthompson
Member #5,749
April 2005
avatar

I've got an (albeit rather convoluted) setup for this in my game. The main draw code writes to the backbuffer (such that we can get antialiased primitives etc.), then the backbuffer is copied to a buffer bitmap, which is the same size as your 'retro' resolution, which is itself scaled over the backbuffer.

#SelectExpand
1// eg. 2#define RETRO_W 640 3#define RETRO_H 480 4 5ALLEGRO_BITMAP* buf; 6float x, y, w, h; 7 8void setup_scale() 9{ 10 int factor_x, factor_y, factor; 11 12 buf = al_create_bitmap(RETRO_W, RETRO_H); 13 14 factor_x = al_get_display_width(disp) / RETRO_W; 15 factor_y = al_get_display_height(disp) / RETRO_H; 16 factor = 17 (factor_y < factor_x) 18 ? factor_y 19 : factor_x 20 ; 21 22 w = RETRO_W * factor; 23 h = RETRO_H * factor; 24 x = (al_get_display_width(disp) / 2) - (w/2); 25 y = (al_get_display_height(disp) / 2) - (h/2); 26} 27 28// call this in place of al_flip_display(): 29void scale_and_flip() 30{ 31 al_set_target_bitmap(buf); 32 al_draw_bitmap(al_get_backbuffer(disp), 0, 0, 0); 33 al_set_target_backbuffer(disp); 34 al_clear_to_color(al_map_rgb(0,0,0)); 35 al_draw_scaled_bitmap(buf, 0, 0, RETRO_W, RETRO_H, x, y, w, h, 0); 36 al_flip_display(); 37}

______________________________________________________
Website. It was freakdesign.bafsoft.net.
This isn't a game!

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

dthompson said:

I've got an (albeit rather convoluted) setup for this in my game. The main draw code writes to the backbuffer (such that we can get antialiased primitives etc.)

You can enable anti-aliasing and multi-sampling on other bitmaps. There's no need to draw the backbuffer onto another buffer to draw back to the backbuffer...

https://liballeg.org/a5docs/trunk/graphics.html#al_set_new_bitmap_samples

Oh, well I guess that's part of the UNSTABLE api and only works on OpenGL.

Draw everything onto an off-screen bitmap and then stretch it to fill the window?
Or scale all the images as they are loaded and draw them 1:1
Or something else (apply a transformation to the screen?)

Render everything normal size to the smaller buffer, then transform it or shader it onto the screen.

Peter Hull
Member #1,136
March 2001

Thanks all. Nice to have asked a programming question for once!

dthompson
Member #5,749
April 2005
avatar

Oh, well I guess that's part of the UNSTABLE api and only works on OpenGL.

Yeah, compatibility was my main gripe with al_set_new_bitmap_samples. Would definitely use that once it's stabilised (and D3D-capable), but the back-and-forth method seems fast enough for now

______________________________________________________
Website. It was freakdesign.bafsoft.net.
This isn't a game!

Peter Hull
Member #1,136
March 2001

Has anyone got any code using CRT shaders (as mentioned by Chris Katko)? I've googled some shader code but not sure how to set it up in Allegro - is it as simple as al_use_shader and then blitting a bitmap?

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I've got a simple shader demo lying around somewhere - let me dig it up.

It's pretty straight forward - you build and compile the shader, then you use it whenever you want to draw a bitmap using your shader.

EDIT
ShaderExample1.zip

If you hold the left mouse button, it makes the texture all wavy. Very simple demo. I still don't understand it much myself which is why I haven't done more complicated things with it.

But the basics of it is here :

#SelectExpand
1ALLEGRO_SHADER* create_shader(const char* fileNameVert, const char* fileNameFrag ) 2{ 3 ALLEGRO_SHADER* shader = al_create_shader(ALLEGRO_SHADER_GLSL); 4// if(!al_attach_shader_source(shader, 5// ALLEGRO_VERTEX_SHADER, 6// al_get_default_shader_source(ALLEGRO_SHADER_GLSL, ALLEGRO_VERTEX_SHADER))) 7 if(!al_attach_shader_source_file(shader, ALLEGRO_VERTEX_SHADER, fileNameVert)) 8 { 9 printf("%s\n", al_get_shader_log(shader)); 10 return NULL; 11 } 12 if(!al_attach_shader_source_file(shader, ALLEGRO_PIXEL_SHADER, fileNameFrag)) 13 { 14 printf("%s\n", al_get_shader_log(shader)); 15 return NULL; 16 } 17 if(!al_build_shader(shader)) 18 { 19 printf("%s\n", al_get_shader_log(shader)); 20 return NULL; 21 } 22 std::cout << " >>> "<< al_get_shader_log(shader) << " \n"; 23 24 return shader; 25}

The vertex shader is stupid and just passes on data.

void main(void)
{
    gl_Position = ftransform();
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_FrontColor = gl_Color;
}

The fragment (pixel) shader just messes with the uv coordinates of the source.

#SelectExpand
1#version 110 2 3uniform sampler2D tex; // 0 4uniform float time; // effect elapsed time 5 6void main() 7{ 8 float width = 1.0/4.0;/// In texels 9 float factor = 0.05;/// Factors from 0.01 to 0.1 seem okay 10 factor = factor*(1.0 + sin(time))/2.0; 11 vec2 uv = gl_TexCoord[0].xy; 12 13 float power = 1.0; 14 15 float xfact = (1.0 + sin((2.0*3.14159265*uv.x/width) + time))/2.0;/// [0,1.0) 16 xfact = pow(xfact , power); 17 18 float yfact = (1.0 + cos((2.0*3.14159265*uv.y/width) + time))/2.0;/// [0,1.0) 19 yfact = pow(yfact , power); 20 21 /// Modify x value by sin(2PI*x/50.0) and y value by sin(2PI*y/50.0) 22 uv = vec2(uv.x + factor*yfact , uv.y + factor*xfact); 23 gl_FragColor = texture2D(tex, uv); 24}

Go to: