[PATCH v2 0/2] MR10835: gdiplus: Add support for CMYK to ARGB pixel format conversion.
Add conversion from PixelFormat32bppCMYK to PixelFormat32bppARGB in convert_pixels(), using the standard subtractive color model formula where RGB = (255 - CMY) * (255 - K) / 255. -- v2: gdiplus: Add support for CMYK to ARGB pixel format conversion. gdiplus/tests: Add test for CMYK to ARGB conversion. https://gitlab.winehq.org/wine/wine/-/merge_requests/10835
From: yaoyongjie <yaoyongjie@uniontech.com> Change-Id: I9a1a2499e6998c428ecdcf399de19a7e55d2ace6 --- dlls/gdiplus/tests/image.c | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c index a1b81c8eda7..510bf0f5c63 100644 --- a/dlls/gdiplus/tests/image.c +++ b/dlls/gdiplus/tests/image.c @@ -5481,6 +5481,81 @@ static void test_PARGB_conversion(void) GdipDisposeImage((GpImage *)bitmap); } +static void test_CMYK_conversion(void) +{ + static const BYTE cmyk_bits[16] = + { + /* pixel 0: C=0,M=0,Y=0,K=0 -> white */ + 0x00,0x00,0x00,0x00, + /* pixel 1: C=0,M=0,Y=0,K=255 -> black */ + 0x00,0x00,0x00,0xff, + /* pixel 2: C=128,M=64,Y=32,K=16 -> R=119,G=179,B=209 */ + 0x80,0x40,0x20,0x10, + /* pixel 3: C=255,M=255,Y=255,K=0 -> black */ + 0xff,0xff,0xff,0x00 + }; + /* Expected ARGB output in BGRA memory layout: + * pixel 0: R=255,G=255,B=255,A=255 -> ff,ff,ff,ff + * pixel 1: R=0,G=0,B=0,A=255 -> 00,00,00,ff + * pixel 2: R=119,G=179,B=209,A=255 -> d1,b3,77,ff + * pixel 3: R=0,G=0,B=0,A=255 -> 00,00,00,ff + */ + static const BYTE expected_argb[16] = + { + 0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0xff, + 0xd1,0xb3,0x77,0xff, + 0x00,0x00,0x00,0xff + }; + GpBitmap *bitmap; + BitmapData data; + GpStatus status; + BYTE *bits; + int match = 0; + + status = GdipCreateBitmapFromScan0(4, 1, 16, PixelFormat32bppCMYK, + (BYTE*)cmyk_bits, &bitmap); + ok(status == Ok, "GdipCreateBitmapFromScan0(CMYK) failed, status=%d\n", status); + if (status != Ok) return; + + memset(&data, 0, sizeof(data)); + status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, + PixelFormat32bppARGB, &data); + todo_wine ok(status == Ok, "LockBits CMYK->ARGB failed, status=%d\n", status); + todo_wine_if (status == Ok) + { + todo_wine ok(data.Width == 4, "expected width 4, got %d\n", data.Width); + todo_wine ok(data.Height == 1, "expected height 1, got %d\n", data.Height); + todo_wine ok(data.PixelFormat == PixelFormat32bppARGB, + "expected PixelFormat32bppARGB, got %#x\n", data.PixelFormat); + + bits = data.Scan0; + if (bits) + { + match = !memcmp(bits, expected_argb, sizeof(expected_argb)); + todo_wine ok(match, "CMYK to ARGB conversion mismatch\n"); + } + if (!match && bits) + { + trace("expected: %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x\n", + expected_argb[0], expected_argb[1], expected_argb[2], expected_argb[3], + expected_argb[4], expected_argb[5], expected_argb[6], expected_argb[7], + expected_argb[8], expected_argb[9], expected_argb[10], expected_argb[11], + expected_argb[12], expected_argb[13], expected_argb[14], expected_argb[15]); + trace("got: %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x %02x,%02x,%02x,%02x\n", + bits[0], bits[1], bits[2], bits[3], + bits[4], bits[5], bits[6], bits[7], + bits[8], bits[9], bits[10], bits[11], + bits[12], bits[13], bits[14], bits[15]); + } + + status = GdipBitmapUnlockBits(bitmap, &data); + todo_wine expect(Ok, status); + + GdipDisposeImage((GpImage *)bitmap); + } +} + static void test_CloneBitmapArea(void) { @@ -6863,6 +6938,7 @@ START_TEST(image) test_CloneBitmapArea(); test_ARGB_conversion(); test_PARGB_conversion(); + test_CMYK_conversion(); test_DrawImage_scale(); test_image_format(); test_DrawImage(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10835
From: yaoyongjie <yaoyongjie@uniontech.com> Add conversion from PixelFormat32bppCMYK to PixelFormat32bppARGB in convert_pixels(), using the standard subtractive color model formula where RGB = (255 - CMY) * (255 - K) / 255. Signed-off-by: yaoyongjie <yaoyongjie@uniontech.com> Change-Id: I4b0991ccd50b648a5bafc6feca723755ca691301 --- dlls/gdiplus/image.c | 33 +++++++++++++++++++++++++++++++++ dlls/gdiplus/tests/image.c | 14 +++++++------- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 17e1437eb5f..b1a3c4a7bd8 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -312,6 +312,15 @@ static inline void getpixel_64bppPARGB(BYTE *r, BYTE *g, BYTE *b, BYTE *a, } } +static inline void getpixel_32bppCMYK(BYTE *c, BYTE *m, BYTE *y, BYTE *k, + const BYTE *row, UINT x) +{ + *c = row[x*4]; + *m = row[x*4+1]; + *y = row[x*4+2]; + *k = row[x*4+3]; +} + GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap* bitmap, INT x, INT y, ARGB *color) { @@ -654,6 +663,21 @@ GpStatus convert_pixels(INT width, INT height, return Ok; \ } while (0); +#define convert_cmyk_to_rgb(getpixel_function, setpixel_function) do { \ + for (y=0; y<height; y++) \ + for (x=0; x<width; x++) { \ + BYTE r, g, b, a; \ + BYTE cc, cm, cy, ck; \ + getpixel_function(&cc, &cm, &cy, &ck, src_bits+src_stride*y, x); \ + r = (255-cc) * (255-ck)/255; \ + g = (255-cm) * (255-ck)/255; \ + b = (255-cy) * (255-ck)/255; \ + a = 255; \ + setpixel_function(r, g, b, a, dst_bits+dst_stride*y, x); \ + } \ + return Ok; \ +} while (0); + switch (src_format) { case PixelFormat1bppIndexed: @@ -1091,6 +1115,15 @@ GpStatus convert_pixels(INT width, INT height, break; } break; + case PixelFormat32bppCMYK: + switch (dst_format) + { + case PixelFormat32bppARGB: + convert_cmyk_to_rgb(getpixel_32bppCMYK, setpixel_32bppARGB); + default: + break; + } + break; default: break; } diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c index 510bf0f5c63..1febd509c3d 100644 --- a/dlls/gdiplus/tests/image.c +++ b/dlls/gdiplus/tests/image.c @@ -5521,19 +5521,19 @@ static void test_CMYK_conversion(void) memset(&data, 0, sizeof(data)); status = GdipBitmapLockBits(bitmap, NULL, ImageLockModeRead, PixelFormat32bppARGB, &data); - todo_wine ok(status == Ok, "LockBits CMYK->ARGB failed, status=%d\n", status); - todo_wine_if (status == Ok) + ok(status == Ok, "LockBits CMYK->ARGB failed, status=%d\n", status); + if (status == Ok) { - todo_wine ok(data.Width == 4, "expected width 4, got %d\n", data.Width); - todo_wine ok(data.Height == 1, "expected height 1, got %d\n", data.Height); - todo_wine ok(data.PixelFormat == PixelFormat32bppARGB, + ok(data.Width == 4, "expected width 4, got %d\n", data.Width); + ok(data.Height == 1, "expected height 1, got %d\n", data.Height); + ok(data.PixelFormat == PixelFormat32bppARGB, "expected PixelFormat32bppARGB, got %#x\n", data.PixelFormat); bits = data.Scan0; if (bits) { match = !memcmp(bits, expected_argb, sizeof(expected_argb)); - todo_wine ok(match, "CMYK to ARGB conversion mismatch\n"); + ok(match, "CMYK to ARGB conversion mismatch\n"); } if (!match && bits) { @@ -5550,7 +5550,7 @@ static void test_CMYK_conversion(void) } status = GdipBitmapUnlockBits(bitmap, &data); - todo_wine expect(Ok, status); + expect(Ok, status); GdipDisposeImage((GpImage *)bitmap); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10835
@gang65 Hello,can you help me review this patch? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10835#note_139782
On Thu May 14 20:21:28 2026 +0000, Yongjie Yao wrote:
@gang65 Hello,can you help me review this patch? It seems that test on Windows is failing. Do you need help with fixing that?
``` image.c:5524:0.031 Test failed: LockBits CMYK->ARGB failed, status=2 ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10835#note_139876
On Thu May 14 20:21:28 2026 +0000, Bartosz Kosiorek wrote:
It seems that test on Windows is failing. Do you need help with fixing that? ``` image.c:5524:0.031 Test failed: LockBits CMYK->ARGB failed, status=2 ``` Ok, I will fix it. Maybe converting CMYK to ARGB is not support in gdiplus, it should only be in WIC. There is no PixelFormat32bppCMYK in Gdipluspixelformats.h.
https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-im... -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10835#note_139922
On Fri May 15 06:16:54 2026 +0000, Yongjie Yao wrote:
Ok, I will fix it. Maybe converting CMYK to ARGB is not support in gdiplus, it should only be in WIC. There is no PixelFormat32bppCMYK in Gdipluspixelformats.h. https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-constant-im... Yes, exactly. It seems GdipBitmapLockBits is rejecting CMYK format conversion. I think CMYK to RGB conversion, should be made during image decoding (WIC decode path).
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10835#note_140064
participants (3)
-
Bartosz Kosiorek (@gang65) -
yaoyongjie -
Yongjie Yao (@yaoyongjie)