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

P to PA conversion loses color #6496

Closed
Yay295 opened this issue Aug 12, 2022 · 5 comments · Fixed by #6497
Closed

P to PA conversion loses color #6496

Yay295 opened this issue Aug 12, 2022 · 5 comments · Fixed by #6497

Comments

@Yay295
Copy link
Contributor

Yay295 commented Aug 12, 2022

What did you do?

using https://github.com/python-pillow/Pillow/blob/main/Tests/images/hopper.jpg

h = Image.open('hopper.jpg')
h.convert('P').show()
h.convert('PA').show()
h.convert('P').convert('PA').show()
h.convert('PA').convert('P').show()

What did you expect to happen?

I expected all of the images to look essentially the same.

What actually happened?

The image converted to P then to PA has no color.

What are your OS, Python, and Pillow versions?

  • OS: Windows 10
  • Python: 3.9.6
  • Pillow: 9.2.0
@Yay295
Copy link
Contributor Author

Yay295 commented Aug 12, 2022

I think the issue is here:

static void
p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
int rgb = strcmp(palette->mode, "RGB");
for (x = 0; x < xsize; x++, in++) {
const UINT8 *rgba = &palette->palette[in[0] * 4];
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
*out++ = rgb == 0 ? 255 : rgba[3];
}
}

This function was previously changed two times:

  1. 4f3b449#diff-504185fb56f577513c6b401b51e9d36abfabf172655309861f5f4f34892abf2f
  2. https://github.com/python-pillow/Pillow/pull/6337/files#diff-504185fb56f577513c6b401b51e9d36abfabf172655309861f5f4f34892abf2f

I think the correct function would be:

static void
p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
    int x;
    int rgb = strcmp(palette->mode, "RGB");
    for (x = 0; x < xsize; x++, in++) {
        const UINT8 *rgba = &palette->palette[in[0]*4];
        *out++ = in[0];
        *out++ = in[1]; // <- change 0 to 1
        *out++ = in[2]; // <- change 0 to 2
        *out++ = rgb == 0 ? 255 : rgba[3];
    }
}

@radarhere
Copy link
Member

The problem isn't in the pixels themselves. The PA mode is similar to the P mode - each pixel is just points to an index in the palette - except that PA also has an alpha channel. The problem is that the palette isn't correctly transferred.

from PIL import Image
h = Image.open('hopper.jpg')

im_p = h.convert('P')
print(im_p.getpalette()[:40])  # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 102, 0, 0, 153]

im_pa = h.convert('P').convert('PA')
print(im_pa.getpalette()[:40])  # [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13]

I've created PR #6497 to resolve this, by copying the palette into the new image.

@Yay295
Copy link
Contributor Author

Yay295 commented Aug 13, 2022

Oh yeah, that makes sense. Shouldn't PA only need two bytes for each pixel though? It looks like LA and La also use more space than they need.

} else if (strcmp(mode, "PA") == 0) {
/* 8-bit palette with alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
im->palette = ImagingPaletteNew("RGB");
} else if (strcmp(mode, "L") == 0) {
/* 8-bit greyscale (luminance) images */
im->bands = im->pixelsize = 1;
im->linesize = xsize;
} else if (strcmp(mode, "LA") == 0) {
/* 8-bit greyscale (luminance) with alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;
} else if (strcmp(mode, "La") == 0) {
/* 8-bit greyscale (luminance) with premultiplied alpha */
im->bands = 2;
im->pixelsize = 4; /* store in image32 memory */
im->linesize = xsize * 4;

@radarhere
Copy link
Member

I can't think of any reason why that should be, I'll only say that PA and LA have been stored in 4 bytes since PIL.

If you'd like to make that change, you could create a PR. Or if you'd like to just request it, you could create a new issue.

@radarhere
Copy link
Member

The request was created as #6503

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

Successfully merging a pull request may close this issue.

2 participants