Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [A5] Screen scrolling causes tile bitmap pixel misplacement

This thread is locked; no one can reply to it. rss feed Print
[A5] Screen scrolling causes tile bitmap pixel misplacement
Eric Johnson
Member #14,841
January 2013
avatar

Hi there.

I've noticed that windowed applications, when incorporating any sort of camera system with vertical scrolling, generates pixel gaps between bitmap tiles.

Here's an image:

{"name":"nlosyh.jpg","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/f\/ef0ebffbd1a685f8ea20e16c27b20319.png","w":482,"h":509,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/f\/ef0ebffbd1a685f8ea20e16c27b20319"}nlosyh.jpg

Notice the al_clear_to_color() call bleeding between the tiles. This does not occur until the camera begins scrolling vertically (horizontal scrolling has no affect).

Here's my crude example code:

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_image.h> 3 4int main(void) { 5 6 bool render = false; 7 bool running = true; 8 9 // Camera vars 10 float camera_x = 0.0; 11 float camera_y = 0.0; 12 13 float smile_x = 0.0; 14 float smile_y = 0.0; 15 16 // Excuse the lack of error checking 17 18 al_init(); 19 20 al_set_new_display_flags(ALLEGRO_WINDOWED); 21 22 ALLEGRO_TIMER *timer = al_create_timer(1.0 / 60.0); 23 ALLEGRO_DISPLAY *display = al_create_display(480, 480); 24 ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue(); 25 26 // Give the window a fancy display title 27 al_set_window_title(display, "Example"); 28 29 al_init_image_addon(); 30 31 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 32 al_register_event_source(event_queue, al_get_display_event_source(display)); 33 34 // Load our beautiful bitmaps 35 ALLEGRO_BITMAP *smile = al_load_bitmap("smile.png"); 36 ALLEGRO_BITMAP *grass = al_load_bitmap("grass.png"); 37 38 al_start_timer(timer); 39 40 // Stretch display 41 ALLEGRO_TRANSFORM t; 42 al_identity_transform(&t); 43 al_scale_transform(&t, 5, 5); // Works OK if scale value is > 5 44 al_use_transform(&t); 45 46 // Primary game loop 47 while (running) { 48 49 ALLEGRO_EVENT event; 50 51 al_wait_for_event(event_queue, &event); 52 53 switch (event.type) { 54 55 case ALLEGRO_EVENT_DISPLAY_CLOSE: 56 57 // Shut the program down 58 running = false; 59 break; 60 61 case ALLEGRO_EVENT_TIMER: 62 63 render = true; 64 65 // Crappy camera calculations 66 camera_x = smile_x; 67 camera_y = smile_y; 68 69 // Move player down screen 70 smile_y += 0.5; 71 break; 72 } 73 74 // Drawing phase 75 if (render && al_is_event_queue_empty(event_queue)) { 76 77 render = false; 78 79 if (smile && grass) { 80 81 al_hold_bitmap_drawing(true); 82 83 for (int y = 0; y < 100; ++y) { 84 85 for (int x = 0; x < 100; ++x) { 86 87 // Draw grass tiles 88 al_draw_bitmap(grass, x * 8 - camera_x, y * 9 - camera_y, 0); 89 } 90 } 91 92 al_hold_bitmap_drawing(false); 93 94 // Draw our smile guy (think of him as player) 95 al_draw_bitmap(smile, smile_x - camera_x, smile_y - camera_y, 0); 96 } 97 98 al_flip_display(); 99 al_clear_to_color(al_map_rgb(0, 0, 255)); 100 } 101 } 102 103 // Garbage collection 104 al_destroy_timer(timer); 105 al_destroy_display(display); 106 al_destroy_event_queue(event_queue); 107 108 return 0; 109}

I am running x86_64 Ubuntu 13.10 with Intel Corporation 7 Series/C210 Series Chipset Family MEI Controller #1 (rev 04) graphics card.

Do you see anything wrong with the way I'm structuring the example code? What could be causing this?

I've attached the example source and bitmap files to this post. Your feedback is most appreciated. :)

Arthur Kalliokoski
Second in Command
February 2005
avatar

Unable to reproduce here, no matter how I set driver vsync etc. Slackware 14.1 with Nvidia driver 330.20 and GeForce GTX 650 card.

{"name":"608217","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/3738f2640796239829ae936f503ccd8f.png","w":480,"h":480,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/7\/3738f2640796239829ae936f503ccd8f"}608217

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

Kris Asick
Member #1,424
July 2001

Sheegoth said:

I am running x86_64 Ubuntu 13.10 with Intel Corporation 7 Series/C210 Series Chipset Family MEI Controller #1 (rev 04) graphics card.

Intel Graphics Chipsets = Severely Lame :P

What's probably happening is your graphics chipset can't properly handle floating-point positions, since you're only moving everything in half-block increments. That said though, you really should round all your coordinates for drawing stuff down to the next whole number in order to avoid aliasing and to keep fine details in the graphics from flickering. This may also solve the issue you're having.

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

Eric Johnson
Member #14,841
January 2013
avatar

Unable to reproduce here, no matter how I set driver vsync etc. Slackware 14.1 with Nvidia driver 330.20 and GeForce GTX 650 card.

Thank you for giving it a go, Arthur. It is good to know that it is an issue with just my machine.

Intel Graphics Chipsets = Severely Lame :P

What's probably happening is your graphics chipset can't properly handle floating-point positions, since you're only moving everything in half-block increments. That said though, you really should round all your coordinates for drawing stuff down to the next whole number in order to avoid aliasing and to keep fine details in the graphics from flickering. This may also solve the issue you're having.

About the floating-point positions, I knew this; I just wanted to ensure that IS the issue (which you've confirmed). I've known that whole value movement (1px instead of 0.5px, for example) works just fine, but a speed of 1 is too fast (for my liking) after transforming the display.

I recall not having this issue several months ago. This all began after messing with some things in Ubuntu. What do you think: would a factory re-install of Linux potentially solve the issue?

Matthew Leverton
Supreme Loser
January 1999
avatar

On my XUbuntu laptop using the i915 driver (Intel 3000?) I get the same gaps.

Kris Asick
Member #1,424
July 2001

Sheegoth said:

What do you think: would a factory re-install of Linux potentially solve the issue?

It's most likely related to your graphics drivers so the simplest thing to do would be to roll back to earlier drivers and see if the problem goes away, or install the latest drivers and see if that helps. I seriously doubt the problem would be related to anything else.

Plus, I didn't say you had to move everything by whole-pixel increments, just round down the coordinates used for drawing when you draw them. You can still store and move positions at the floating-point level.

With my own game objects, because I use a frame interpolation system that can run a game at a fixed framerate yet render at whatever framerate the end user can achieve, all of my game objects track the coordinates they are currently in, the coordinates they were in on the last fixed frame, and the coordinates they should render to.

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

Eric Johnson
Member #14,841
January 2013
avatar

Plus, I didn't say you had to move everything by whole-pixel increments, just round down the coordinates used for drawing when you draw them. You can still store and move positions at the floating-point level.

With my own game objects, because I use a frame interpolation system that can run a game at a fixed framerate yet render at whatever framerate the end user can achieve, all of my game objects track the coordinates they are currently in, the coordinates they were in on the last fixed frame, and the coordinates they should render to.

Hmm, what an interesting concept. Would you mind posting pseudo code? What immediately came to my mind was to change the camera values to ints, and then to pass the player's floats as arguments (thus accumulating whole-values after 0.5+ goes for two cycles). This corrected the gaps, but, to no surprise, resulted in jittery player movement.

jmasterx
Member #11,410
October 2009

#SelectExpand
1 void Camera::transformView() 2 { 3 updateCamera(); 4 al_identity_transform(&m_transform); 5 al_translate_transform(&m_transform,floor(-getLeft()),floor(-getTop())); 6 al_use_transform(&m_transform); 7 } 8 9 void Camera::focus( Entity* target ) 10 { 11 m_target = target; 12 updateCamera(); 13 } 14 15 void Camera::onResize(int w, int h) 16 { 17 float x = getX(); 18 float y = getY(); 19 getBox().setBox(0,0,w,h); 20 setPosition(x,y); 21 } 22 23 void Camera::updateCamera() 24 { 25 if(m_target) 26 { 27 setPosition( 28 m_target->getRenderLeft() + (m_target->getWidth() / 2), 29 m_target->getRenderTop() + (m_target->getHeight() / 2)); 30 } 31 } 32} 33 34..... 35 36 int Entity::getRenderLeft() const 37 { 38 return floor(getLeft() + 0.5f); 39 } 40 41 int Entity::getRenderTop() const 42 { 43 return floor(getTop() + 0.5f); 44 }

Kris Asick
Member #1,424
July 2001

Sheegoth said:

Hmm, what an interesting concept. Would you mind posting pseudo code?

Not sure of a good way to write this in pseudo-code, but if you're experiencing jitter it's typically related to not rounding things in the right order, rounding values together that should be rounded separately, or figuring out positions based on rounded values instead of actual values or vice versa. The best way to scan this is to cut all your movement rates down by a factor of 100 or so, write the values on-screen for each step of the calculations and watch them as they slowly change. This is what I do when one of my math equations does funny stuff like this and watching the numbers change is a good way to gauge what's causing things like jitter to happen.

Also, since the Allegro drawing functions take floats, not ints, you probably shouldn't convert to them at any point and just stick with using floor().

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

Go to: