Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GR32_LowLevel.Mirror PUREPASCAL does not produce expected values #291

Open
andersmelander opened this issue Mar 23, 2024 · 11 comments
Open
Labels

Comments

@andersmelander
Copy link
Member

andersmelander commented Mar 23, 2024

The GR32_LowLevel.Mirror* functions are used in various places to clamp values to a given range. Notably, when sampling a bitmap, they are used to avoid out of bounds pixel coordinates.

For example, Mirror(n, 5) where n=[0..15] should return the values 012345432101234.
In other words: Mirror(n, Max) should be a triangle function with a wavelength of Max.

The current implementations has multiple issues:

  • The algorithm of the Mirror(Value, Max) PUREPASCAL implementation does not match the algorithm of the assembler implementation. The PUREPASCAL implementation has incorrect handling of the edge conditions, so in the example above, instead of returning
    012345432101234
    it returns
    012345543210012
  • The MirrorPow2 implementation does not special case negative values and appears to suffer from the same problem as the Mirror implementation.
andersmelander pushed a commit that referenced this issue Mar 23, 2024
…iangle "wave".

Mirror() assembler implementation used wrong (and thus uninitialized) register.
MirrorPow2 still broken.
Updated Mirror and MirrorPow2 unit tests.
Refs #291
@CuriousKit
Copy link
Contributor

How goes the progress in fixing this?

@andersmelander
Copy link
Member Author

I'm busy with other stuff so I'm not working on it (which is why I haven't assigned it to myself)

@CuriousKit
Copy link
Contributor

That's fair. I saw that you made a commit related to the bug and thought initially that the bug had been fixed, but a closer look at the commit messages indicates that it's not all fixed yet. Still, these messages will indicate that the issue is still open.

@andersmelander
Copy link
Member Author

For reference, what we need is a replacement for the current (broken) algorithm:

function MirrorPow2(Value, Max: Integer): Integer; overload;
begin
  if Value and (Max + 1) = 0 then
    Result := Value and Max
  else
    Result := Max - Value and Max;
end;

MirrorPow([0..13], 3)
Expected: 0123210123210
Actual: 0123210301232

@CuriousKit
Copy link
Contributor

I'll take a study of the file myself at some point. I'm curious to know what the function is supposed to do exactly.

@andersmelander
Copy link
Member Author

The Clamp, Wrap, and Mirror functions are primarily used when sampling bitmaps (e.g. resizing, rotating, transforming, etc). They specify how out of bounds pixel coordinates should be handled.
https://graphics32.github.io/Docs/Units/GR32/Types/TWrapMode.htm

@CuriousKit
Copy link
Contributor

Ah I see, thank you. Having played with textures in OpenGL, I can definitely see the importance of getting this function correct then!

@andersmelander
Copy link
Member Author

@andersmelander
Copy link
Member Author

I can definitely see the importance of getting this function correct then!

I would think so but the bug has been there since the functions introduction in 2007...

@lamdalili
Copy link
Contributor

lamdalili commented Apr 12, 2024

Sorry I didn't notice that it was resolved in new branch

function Mirror(Value, Max: Integer): Integer;
var
Quotient: Integer;
begin
if Value < 0 then
Value := -Value;
Quotient := DivMod(Value, Max, Result);
if ((Quotient and 1) = 1) then
Result := Max - Result;
end;

Test on GradFills example :
Original mirror :

mirror 1

After correction

Mirror 2

@andersmelander
Copy link
Member Author

@lamdalili Thanks but the Mirror function has already been fixed in the LowLevel.Mirror branch:

function Mirror(Value, Max: Integer): Integer;
var
Quotient: Integer;
begin
if Value < 0 then
Value := -Value;
Quotient := DivMod(Value, Max, Result);
if ((Quotient and 1) = 1) then
Result := Max - Result;
end;

What remains to be fixed is the MirrorPow2 function:

function MirrorPow2(Value, Max: Integer): Integer; overload;
begin
if Value < 0 then
Value := -Value;
if ((Value and (Max + 1)) = 0) then
Result := (Value ) and Max
else
Result := (Max - Value + Max) and Max;
end;

See: #291 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants