Advertisement

Pixel rounding problem... (c++)

Started by July 12, 2018 12:07 PM
8 comments, last by LorenzoGatti 6 years, 1 month ago

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!

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.

 

Advertisement

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?

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

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.

@a light breeze

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

Advertisement
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.

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

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

This topic is closed to new replies.

Advertisement