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

gdImageScaleTwoPass() looses top row and left column #93

Closed
imazen-bot opened this issue Feb 5, 2015 · 12 comments
Closed

gdImageScaleTwoPass() looses top row and left column #93

imazen-bot opened this issue Feb 5, 2015 · 12 comments
Milestone

Comments

@imazen-bot
Copy link

The resulting image is shifted left and up, with the right and bottom rows filled with apparently correct data.

I'll look into it sometime next week and try to have a reproduction and fix available then.


@imazen-bot
Copy link
Author

great, thanks!

Btw, like access to the repo? Your patches are very good and I would love to see you on board :)


Originally posted by pierrejoye (Pierre Joye) on Oct 12 2013 via Bitbucket

@imazen-bot
Copy link
Author

Sure, that's be great.

Also, can you email me? I'm <chris(at)blit.ca>. I'd like to explain my goals and make sure they're in sync with yours.


Originally posted by suetanvil (Chris Reuter) on Oct 13 2013 via Bitbucket

@imazen-bot
Copy link
Author

This bug seems to be a quirk in the algorithm. Basically, it's resampling from the left edge of the pixel rather than the center (pixels have a width and height when you're using continuous functions) which causes the shift. Fixing it is non-trivial, so I'm going to put it off for now.


Originally posted by suetanvil (Chris Reuter) on Oct 18 2013 via Bitbucket

@cmb69
Copy link
Contributor

cmb69 commented Oct 11, 2016

I assume that this ticket is about the following behavior:

Test Program

#include <gd.h>


int main()
{
    gdImagePtr src, dst;
    FILE *fp;

    src = gdImageCreateTrueColor(11, 11);
    gdImageFilledRectangle(src, 0,0, 10,10, gdTrueColorAlpha(255, 255, 255, gdAlphaOpaque));
    gdImageRectangle(src, 0,0, 10,10, gdTrueColorAlpha(0, 0, 255, gdAlphaOpaque));
    gdImageEllipse(src, 5,5, 8, 8, gdTrueColorAlpha(0, 0, 0, gdAlphaOpaque));

    gdImageSetInterpolationMethod(src, GD_TRIANGLE);
    dst = gdImageScale(src, 132, 132);

    fp = fopen("scale.png", "wb");
    gdImagePng(dst, fp);
    fclose(fp);

    gdImageDestroy(src);
    gdImageDestroy(dst);

    return 0;
}

Expected Result

scale_exp

Actual Result

scale_act

@cmb69
Copy link
Contributor

cmb69 commented Oct 11, 2016

The problem is happening during the calculation of the contributing pixels, namely in the calculation of dCenter. Consider upscaling from 2 to 6 pixels (factor 3). In this case dCenter has the consecutive values of 0/3, 1/3, 2/3, 3/3, 4/3 and 5/3. That does not center nicely around the actual existing pixels (0 and 1). We can easily adjust by -1/3, and as such have values from -1/3 up to 4/3.

@cmb69 cmb69 changed the title gdImageScale() with floating-point interpolation method looses top row and left column gdImageScaleTwoPass() looses top row and left column Oct 11, 2016
cmb69 added a commit to cmb69/libgd that referenced this issue Oct 11, 2016
Because we're dealing with pixels, opposed to mathematical points, we
have to adjust `dCenter` appropriately. For upscaling we have to shift
the center to the left, for downscaling we have to shift to the right.
@cmb69
Copy link
Contributor

cmb69 commented Oct 11, 2016

The resulting image is shifted left and up, with the right and bottom rows filled with apparently correct data.

Firstly, it has to be noted that the shift to zero (i.e. the mentioned behavior) occurs when upscaling. When downscaling there is a shift away from zero (i.e. the image is shifted right and down).

Anyhow, the right colum and bottom row has not necessarily the correct data. Especially GD_BOX may create a black border at the right and bottom when upscaling. This is because the most weighted contributing pixel may be outside the valid range, and as such would simply be skipped. The remaining contributor has a weight of 0, so the source pixel will be blacked.

In my opinion, particularly the behavior of GD_BOX has to be regarded as a bug, and as such PR #333 should be merged into master and GD-2.2, even though that might slightly break backwards compatibility.

Thoughts?

@pierrejoye
Copy link
Contributor

I cleanup/refactor a bit the new scaling functions and added an example here.

It fixes an issue about the support (radius) used by each filter and ensure correct filters being used.

I would GD_LANCZOS3 for downscale and mitchell for upscale, to begin width.

@cmb69 could you give a try?

@pierrejoye
Copy link
Contributor

I applied a fix for the support/radius as well as cleaned up/refactored it.

I made some tests using a few images and compared with other references libs for most filters.

for the 12 I tried, it looks accurate and correct now.

f.e. using Lanczos3 ("Lanczos in GM or IM)
GM:
image

GD Master:
image

@pierrejoye
Copy link
Contributor

pierrejoye commented Aug 16, 2021

Full size. Generated using fillandstroke results and imagescale example, scaled 300% using Lanczos3.

GM
im_lanczso

GD
scale_23

@pierrejoye
Copy link
Contributor

the gdImagePtr interpolation_method was never public.

I was pondering to remove the method assignment here:

im->interpolation = filter_bell;

And only relies on the new static array (all in one place). Keeping the gdImagePtr element for ABI (same struct size) but won't be used, will be remove in next x.y+1.0. Thoughts?

@pierrejoye
Copy link
Contributor

#661 has some tests image as well.

@pierrejoye
Copy link
Contributor

The original bug is fixed by cmb69@d27c467

Interpolation methods now always use the right radius and callbacks.

thanks for the detailed reports, cases and fixes!

@pierrejoye pierrejoye added this to the GD 2.3.3 milestone Aug 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants