Skip to main content
GameDev.net gamedev.net
🔒 Locked

Pixel rounding problem... (c++)

Started by suliman Jul 12, 2018 at 12:07 PM 8 replies 1.6k views
Original Post
suliman
suliman

Hi!
When drawing a part of a sprite to screen (the sprite fills up from the bottom up when 'progress' goes from 0 to 1) I can see it jump up and down slightly (i think there is some problem with rounding the pixel position).

Params in order:
posX
posY
pic (the sprite)
xStart
yStart
xEnd
yEnd


gfx.picPartRel(x,y+(1.0-progress)*pic->GetHeight()*scale,pic,0,1-progress,1,progress);

Can you see how to do this so that the image doesnt jump up and down one pixel? (it's the posY that messes it up i think due to pixel rounding).

Below is and example of drawing with progress = 0.7 (the black image is drawn from bottom up but jumps around slightly in y-position when changing progress in realtime)

image.png.279b6de135264ab5420c35855fe92f01.png

Thanks!

a light breeze
a light breeze

The problem is either in the function you're calling or in the parameters you are passing into the function.

In the former case, find another function to use instead (if it's from a library) or fix the function (if it's yours).

In the latter cases, you can try rounding your progress variable to an integer number of pixels:


rounded_progress = float(int(progress*pic->GetHeight())) / pic->GetHeight();
gfx.picPartRel(x,y+(1.0-rounded_progress)*pic->GetHeight()*scale,pic,0,1-rounded_progress,1,rounded_progress);

It helps if pic->GetHeight() is a power of 2, because then the division in the rounding operation is lossless.


suliman
suliman

GetHeight() doesnt return a height with a power of 2 (it can be any pixel height, in this case it's 150). However if i try it on a 128x128 sprite i get the same problem.

The code suggested doesnt solve the problem. Is there a way to evaluate which input leads to "pixel shift" and just adjust those cases with plus/minus 1 pixel?

LorenzoGatti
LorenzoGatti

The problem should be related to rounding different floating point values (progress and 1-progress multiplied by some constant distance or size in pixels) to integer values, and expecting them to be matched (e.g. constant sprite size); for some values of progress they can be off by one. In other words, the unavoidable jitter becomes more troublesome because it's out of sync between parts of your scene.

A correct solution would round only one large floating point value, obtain a large integer, and add or subtract constant integers to that.

18 hours ago, a light breeze said:

In the latter cases, you can try rounding your progress variable to an integer number of pixels:



rounded_progress = float(int(progress*pic->GetHeight())) / pic->GetHeight();
gfx.picPartRel(x,y+(1.0-rounded_progress)*pic->GetHeight()*scale,pic,0,1-rounded_progress,1,rounded_progress)

This code simply replaces progress with another floating point variable rounded_progress in the same range and with the same problems. Despite the name rounded_progressI doesn't have an integer value and it cannot serve as a reference.

Unfortunately, since gfx.picPartRel() takes floating point parameters in the [0,1] range, the rounding is likely to take place there rather than in your calling code.





Omae Wa Mou Shindeiru
a light breeze
a light breeze
22 minutes ago, LorenzoGatti said:

This code simply replaces progress with another floating point variable rounded_progress in the same range and with the same problems. Despite the name rounded_progressI doesn't have an integer value and it cannot serve as a reference.

That's not quite the case. There are certain numbers that can be exactly represented in a floating variable. These include all sufficiently small integers, but also all sufficiently small fractions so long as the fraction has a power of two in its denominator. In other words, it is possible for the called function to losslessly convert its floating point arguments back into integer coordinates. To see this, try the following:

  1. Make sure that you have an image with power-of-two dimensions.
  2. Make sure that you use rounded_progress as defined above.
  3. Replace the picPartRel function with one that prints out the following values as floating point:
  • posX
  • posY
  • xStart * pic->GetWidth()
  • yStart * pic->GetHeight()
  • xEnd * pic->GetWidth()
  • yEnd * pic->GetHeight()

If all of the printed results are perfect integers, then you have passed in the correct parameters, which means that the problem is in the picPartRel function. If the results are not perfect integers, then either you didn't follow all of the instructions or I made a mistake, which has been known to happen from time to time.

suliman
suliman

@a light breeze

I tried that. The posY is not a perfect integer (it has decimals). The others are ok though.

a light breeze
a light breeze
13 minutes ago, suliman said:

@a light breeze

I tried that. The posY is not a perfect integer (it has decimals). The others are ok though.

Then the problem is probably with the scale variable. If it's an integer, then my code should work. If it's not, then I'm out of ideas.

Oh, and the x and y variables need to be integers too, but it's easy to correct them if they're not. Just cast them to int.

suliman
suliman

Yeah the "scale" is a float (0 means empty, 1 means full)

LorenzoGatti
LorenzoGatti

Can you explain why are you using as counterproductive a drawing call as that gfx.picPartRel?

A sane API should have parameters in pixel units: either 2 (screen position, possibly floating point) to render a whole sprite, or 6 (screen position and 4 integers) to cut an arbitrary rectangle out of a texture atlas.

If the 4 gfx.picPartRel parameters between 0 and 1 are texture coordinates, can you use texture coordinates in pixel units instead? For example, OpenGL has the ARB_texture_rectangle extension for this purpose.

Omae Wa Mou Shindeiru

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.