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

gdImageGrayscale results in non-gray pixels for semi-transparent images due to alpha blending onto the original image #386

Closed
thenickdude opened this Issue Mar 31, 2017 · 5 comments

Comments

Projects
None yet
3 participants
@thenickdude

thenickdude commented Mar 31, 2017

If you call gdImageGrayscale() on a semi-transparent image, it converts the pixels to grayscale and writes them back to the image using gdImageSetPixel().

In the default gdEffectNormal blending mode, this results in gdImageSetPixel() blending the new pixel value onto the old one using its alpha channel, so instead of the resulting image being grayscale, it is the grayscaled version of the image alpha-blended on top of the original color one.

For correct results, the blending mode needs to be changed to "gdEffectReplace" (i.e. gdImageAlphaBlending(im, gdEffectReplace);) and restored after grayscaling. It seems like gdImageGrayscale should either do this itself, or suggest it to users in the documentation.

Here's a test image wings.png:

wings

And the program that attempts to convert it to grayscale:

#include "gd.h"

#include <stdio.h>
#include <stdlib.h>

int main() {
  gdImagePtr im;
  FILE *pngin, *pngout;
  int pixel;

  pngin = fopen("wings.png", "rb");

  if (!pngin) {
  	fprintf(stderr, "Failed to open input image");
  	return -1;
  }

  im = gdImageCreateFromPng(pngin);

  if (!im) {
	fprintf(stderr, "Failed to decode input image");
	return -1;
  }

  // It's true-color already, but let's make sure
  gdImagePaletteToTrueColor(im);

  pixel = gdImageGetPixel(im, 5, 17);
  printf("Before grayscaling, pixel at 5,17 is RGBA: (%d, %d, %d, %d)\n", gdImageRed(im, pixel), gdImageGreen(im, pixel), gdImageBlue(im, pixel), gdImageAlpha(im, pixel));

  // gdImageAlphaBlending(im, gdEffectReplace); // Adding this line fixes the output

  if (!gdImageGrayScale(im)) {
  	fprintf(stderr, "Failed to convert to grayscale");
	return -1;
  }

  pixel = gdImageGetPixel(im, 5, 17);
  printf("After grayscaling, pixel at 5,17 is RGBA: (%d, %d, %d, %d)\n", gdImageRed(im, pixel), gdImageGreen(im, pixel), gdImageBlue(im, pixel), gdImageAlpha(im, pixel));

  gdImageSaveAlpha(im, 1);

  pngout = fopen("output.png", "wb");

  gdImagePng(im, pngout);

  fclose(pngout);

  gdImageDestroy(im);
  return 0;
}

The result should be:

output-correct

But instead you get this faintly-purple:

output-wrong

@thenickdude

This comment has been minimized.

thenickdude commented Mar 31, 2017

Actually a better test image is this:

wings

Which looks like this after grayscaling filter is applied:

output

@cmb69

This comment has been minimized.

Contributor

cmb69 commented Apr 14, 2017

Thanks!

Maybe someone is relying on the current behavior, so I'd suggest to stick with it for now (and to change it in 2.3 or maybe better 3.0), and to document the issue.

@pierrejoye

This comment has been minimized.

Contributor

pierrejoye commented May 4, 2017

I am not sure someone expect "colors" after calling the grays scale filter.

Do you see any issue to consider that as a bug? At the latest 2.3.0 yes.

@cmb69

This comment has been minimized.

Contributor

cmb69 commented Aug 7, 2017

Do you see any issue to consider that as a bug?

I'm afraid I've fixed too many apparent bugs, causing not always welcome BC breaks. :) Anyhow, I'm setting the 2.2.5 milestone, to at least document the issue.

@cmb69 cmb69 added this to the GD 2.2.5 milestone Aug 7, 2017

@cmb69

This comment has been minimized.

Contributor

cmb69 commented Aug 26, 2017

After further consideration and investigation, I believe the current behavior is a bug (not a documentation issue). Actually, gdImageGrayScale() is a back-port of PHP's bundled libgd (d8e19af), and has been introduced there in php/php-src@c62cb67, and the function comment ("Convert the image src to a grayscale image") clearly indicates that conversion to grey-scale was intended, but has obviously not been accomplished in all cases.

@cmb69 cmb69 added the bug label Aug 26, 2017

@cmb69 cmb69 closed this in a7a7ece Aug 27, 2017

cmb69 added a commit that referenced this issue Aug 27, 2017

Fix #386: gdImageGrayScale() may produce colors
We have to make sure to avoid alpha-blending issues by explicitly
switching to `gdEffectReplace` and to restore the old value afterwards.

We also document the algorithm used by `gdImageGrayScale()` and note
its limitations regarding palette images.

(cherry picked from commit a7a7ece)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment