mono / libgdiplus

C-based implementation of the GDI+ API
http://www.mono-project.com/
MIT License
329 stars 171 forks source link

The color of CMYK pixel format pictures is incorrect #706

Open e8-ShiWei opened 3 years ago

e8-ShiWei commented 3 years ago

https://github.com/mono/libgdiplus/blob/0477843237ea7168d345bfe7f9ee2f13cd315130/src/jpegcodec.c#L452

if (cinfo.saw_Adobe_marker) {
    b = (k * c) / 255;
    g = (k * m) / 255;
    r = (k * y) / 255;
}  else {
    b = (255 - k) * (255 - c) / 255;
    g = (255 - k) * (255 - m) / 255;
    r = (255 - k) * (255 - y) / 255;
}

set_pixel_bgra(lineptr, 0, b, g, r, 0xff);

It seems that the values of b and r are incorrect. Red is assigned to b, and blue is assigned to r.

This leads to the color of CMYK pixel format pictures is incorrect.

I think the correct code should be:

if (cinfo.saw_Adobe_marker) {
    r = (k * c) / 255;
    g = (k * m) / 255;
    b = (k * y) / 255;
}  else {
    r = (255 - k) * (255 - c) / 255;
    g = (255 - k) * (255 - m) / 255;
    b = (255 - k) * (255 - y) / 255;
}

set_pixel_bgra(lineptr, 0, b, g, r, 0xff);
filipnavara commented 3 years ago

The calculations look correct to me based on the color theory. However, it is quite possible that the channels are swapped elsewhere in the code. What version of the library are you using? It would help a lot to have some code/data to replicate the bug.

e8-ShiWei commented 3 years ago

The calculations look correct to me based on the color theory. However, it is quite possible that the channels are swapped elsewhere in the code. What version of the library are you using? It would help a lot to have some code/data to replicate the bug.

Thank you for your reply.

The version of libgdiplus we are using is 6.0.4. OS: ubuntu 20.04 Framework: .NET 5

I don't think the current color conversion is correct. For example, red: C = 0% M = 100% Y = 100% K = 0%

According to the current formulas:

b = (255 - k) * (255 - c) / 255;
g = (255 - k) * (255 - m) / 255;
r = (255 - k) * (255 - y) / 255;

R = 255 0 / 255 = 0 G = 255 0 / 255 = 0 B = 255 * 255 / 255 = 255

This will get the RGB value of blue instead of red.

This is also the same as the problem encountered in our production environment. The red in the picture changed to blue after repainting. We tried to swap the positions of b and r and recompile, and this problem was solved.

filipnavara commented 3 years ago

I have to admit that you are probably right :-) Would you mind submitting a pull request with the change?