From 509ba80bcbd502fa98ad189d5ae26627f9606757 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Wed, 2 Nov 2016 21:24:21 +0300 Subject: [PATCH] gdiplus: Implement GdipBitmapGetHistogram(). Signed-off-by: Nikolay Sivov Signed-off-by: Vincent Povirk Signed-off-by: Alexandre Julliard --- dlls/gdiplus/gdiplus.spec | 2 +- dlls/gdiplus/image.c | 123 +++++++++++++++++++++++++++++++++++++ dlls/gdiplus/tests/image.c | 94 +++++++++++++++++++++++++++- include/gdiplusflat.h | 1 + 4 files changed, 216 insertions(+), 4 deletions(-) diff --git a/dlls/gdiplus/gdiplus.spec b/dlls/gdiplus/gdiplus.spec index 92251a74df0..40c19e6a3bc 100644 --- a/dlls/gdiplus/gdiplus.spec +++ b/dlls/gdiplus/gdiplus.spec @@ -618,7 +618,7 @@ 618 stub GdipInitializePalette 619 stdcall GdipBitmapCreateApplyEffect(ptr long ptr ptr ptr ptr long ptr ptr) 620 stdcall GdipBitmapApplyEffect(ptr ptr ptr long ptr ptr) -621 stub GdipBitmapGetHistogram +621 stdcall GdipBitmapGetHistogram(ptr long long ptr ptr ptr ptr) 622 stdcall GdipBitmapGetHistogramSize(long ptr) 623 stdcall GdipBitmapConvertFormat(ptr long long long ptr float) 624 stdcall GdipImageSetAbort(ptr ptr) diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c index 672b2e5ce81..e4eba2bb6ea 100644 --- a/dlls/gdiplus/image.c +++ b/dlls/gdiplus/image.c @@ -5387,6 +5387,129 @@ GpStatus WINGDIPAPI GdipBitmapConvertFormat(GpBitmap *bitmap, PixelFormat format return NotImplemented; } +static void set_histogram_point_argb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[ color >> 24 ]++; + ch1[(color >> 16) & 0xff]++; + ch2[(color >> 8) & 0xff]++; + ch3[ color & 0xff]++; +} + +static void set_histogram_point_pargb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + BYTE alpha = color >> 24; + + ch0[alpha]++; + ch1[(((color >> 16) & 0xff) * alpha) / 0xff]++; + ch2[(((color >> 8) & 0xff) * alpha) / 0xff]++; + ch3[(( color & 0xff) * alpha) / 0xff]++; +} + +static void set_histogram_point_rgb(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[(color >> 16) & 0xff]++; + ch1[(color >> 8) & 0xff]++; + ch2[ color & 0xff]++; +} + +static void set_histogram_point_gray(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[(76 * ((color >> 16) & 0xff) + 150 * ((color >> 8) & 0xff) + 29 * (color & 0xff)) / 0xff]++; +} + +static void set_histogram_point_b(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[color & 0xff]++; +} + +static void set_histogram_point_g(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[(color >> 8) & 0xff]++; +} + +static void set_histogram_point_r(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[(color >> 16) & 0xff]++; +} + +static void set_histogram_point_a(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + ch0[(color >> 24) & 0xff]++; +} + +/***************************************************************************** + * GdipBitmapGetHistogram [GDIPLUS.@] + */ +GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap *bitmap, HistogramFormat format, UINT num_of_entries, + UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) +{ + static void (* const set_histogram_point[])(ARGB color, UINT *ch0, UINT *ch1, UINT *ch2, UINT *ch3) = + { + set_histogram_point_argb, + set_histogram_point_pargb, + set_histogram_point_rgb, + set_histogram_point_gray, + set_histogram_point_b, + set_histogram_point_g, + set_histogram_point_r, + set_histogram_point_a, + }; + UINT width, height, x, y; + + TRACE("(%p, %d, %u, %p, %p, %p, %p)\n", bitmap, format, num_of_entries, + ch0, ch1, ch2, ch3); + + if (!bitmap || num_of_entries != 256) + return InvalidParameter; + + /* Make sure passed channel pointers match requested format */ + switch (format) + { + case HistogramFormatARGB: + case HistogramFormatPARGB: + if (!ch0 || !ch1 || !ch2 || !ch3) + return InvalidParameter; + memset(ch0, 0, num_of_entries * sizeof(UINT)); + memset(ch1, 0, num_of_entries * sizeof(UINT)); + memset(ch2, 0, num_of_entries * sizeof(UINT)); + memset(ch3, 0, num_of_entries * sizeof(UINT)); + break; + case HistogramFormatRGB: + if (!ch0 || !ch1 || !ch2 || ch3) + return InvalidParameter; + memset(ch0, 0, num_of_entries * sizeof(UINT)); + memset(ch1, 0, num_of_entries * sizeof(UINT)); + memset(ch2, 0, num_of_entries * sizeof(UINT)); + break; + case HistogramFormatGray: + case HistogramFormatB: + case HistogramFormatG: + case HistogramFormatR: + case HistogramFormatA: + if (!ch0 || ch1 || ch2 || ch3) + return InvalidParameter; + memset(ch0, 0, num_of_entries * sizeof(UINT)); + break; + default: + WARN("Invalid histogram format requested, %d\n", format); + return InvalidParameter; + } + + GdipGetImageWidth(&bitmap->image, &width); + GdipGetImageHeight(&bitmap->image, &height); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + ARGB color; + + GdipBitmapGetPixel(bitmap, x, y, &color); + set_histogram_point[format](color, ch0, ch1, ch2, ch3); + } + + return Ok; +} + /***************************************************************************** * GdipBitmapGetHistogramSize [GDIPLUS.@] */ diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c index baf5c3bb2af..ad0cc6f1bd0 100644 --- a/dlls/gdiplus/tests/image.c +++ b/dlls/gdiplus/tests/image.c @@ -31,6 +31,7 @@ #include "wine/test.h" static GpStatus (WINAPI *pGdipBitmapGetHistogramSize)(HistogramFormat,UINT*); +static GpStatus (WINAPI *pGdipBitmapGetHistogram)(GpBitmap*,HistogramFormat,UINT,UINT*,UINT*,UINT*,UINT*); #define expect(expected, got) ok((got) == (expected), "Expected %d, got %d\n", (UINT)(expected), (UINT)(got)) #define expectf(expected, got) ok(fabs((expected) - (got)) < 0.0001, "Expected %f, got %f\n", (expected), (got)) @@ -4787,8 +4788,9 @@ static void test_getadjustedpalette(void) GdipDisposeImageAttributes(imageattributes); } -static void test_histogramsize(void) +static void test_histogram(void) { + UINT ch0[256], ch1[256], ch2[256], ch3[256]; HistogramFormat test_formats[] = { HistogramFormatARGB, @@ -4800,8 +4802,10 @@ static void test_histogramsize(void) HistogramFormatR, HistogramFormatA, }; + const UINT WIDTH = 8, HEIGHT = 16; + UINT num, i, x; GpStatus stat; - UINT num, i; + GpBitmap *bm; if (!pGdipBitmapGetHistogramSize) { @@ -4827,6 +4831,89 @@ static void test_histogramsize(void) expect(Ok, stat); expect(256, num); } + + bm = NULL; + stat = GdipCreateBitmapFromScan0(WIDTH, HEIGHT, 0, PixelFormat24bppRGB, NULL, &bm); + expect(Ok, stat); + + /* Three solid rgb rows, next three rows are rgb shades. */ + for (x = 0; x < WIDTH; x++) + { + GdipBitmapSetPixel(bm, x, 0, 0xffff0000); + GdipBitmapSetPixel(bm, x, 1, 0xff00ff00); + GdipBitmapSetPixel(bm, x, 2, 0xff0000ff); + + GdipBitmapSetPixel(bm, x, 3, 0xff010000); + GdipBitmapSetPixel(bm, x, 4, 0xff003f00); + GdipBitmapSetPixel(bm, x, 5, 0xff000020); + } + + stat = pGdipBitmapGetHistogram(NULL, HistogramFormatRGB, 256, ch0, ch1, ch2, ch3); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, ch2, ch3); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, ch2, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, ch1, NULL, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, 123, 256, ch0, NULL, NULL, NULL); + expect(InvalidParameter, stat); + + /* Requested format matches bitmap format */ + stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 256, ch0, ch1, ch2, ch3); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 100, ch0, ch1, ch2, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 257, ch0, ch1, ch2, NULL); + expect(InvalidParameter, stat); + + /* Channel 3 is not used, must be NULL */ + stat = pGdipBitmapGetHistogram(bm, HistogramFormatRGB, 256, ch0, ch1, ch2, NULL); + expect(Ok, stat); + + ok(ch0[0xff] == WIDTH, "Got red (0xff) %u\n", ch0[0xff]); + ok(ch1[0xff] == WIDTH, "Got green (0xff) %u\n", ch1[0xff]); + ok(ch2[0xff] == WIDTH, "Got blue (0xff) %u\n", ch1[0xff]); + ok(ch0[0x01] == WIDTH, "Got red (0x01) %u\n", ch0[0x01]); + ok(ch1[0x3f] == WIDTH, "Got green (0x3f) %u\n", ch1[0x3f]); + ok(ch2[0x20] == WIDTH, "Got blue (0x20) %u\n", ch1[0x20]); + + /* ARGB histogram from RGB data. */ + stat = pGdipBitmapGetHistogram(bm, HistogramFormatARGB, 256, ch0, ch1, ch2, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatARGB, 256, ch0, ch1, ch2, ch3); + expect(Ok, stat); + + ok(ch1[0xff] == WIDTH, "Got red (0xff) %u\n", ch1[0xff]); + ok(ch2[0xff] == WIDTH, "Got green (0xff) %u\n", ch2[0xff]); + ok(ch3[0xff] == WIDTH, "Got blue (0xff) %u\n", ch3[0xff]); + ok(ch1[0x01] == WIDTH, "Got red (0x01) %u\n", ch1[0x01]); + ok(ch2[0x3f] == WIDTH, "Got green (0x3f) %u\n", ch2[0x3f]); + ok(ch3[0x20] == WIDTH, "Got blue (0x20) %u\n", ch3[0x20]); + + ok(ch0[0xff] == WIDTH * HEIGHT, "Got alpha (0xff) %u\n", ch0[0xff]); + + /* Request grayscale histogram from RGB bitmap. */ + stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, ch2, ch3); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, ch2, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, ch1, NULL, NULL); + expect(InvalidParameter, stat); + + stat = pGdipBitmapGetHistogram(bm, HistogramFormatGray, 256, ch0, NULL, NULL, NULL); + expect(Ok, stat); + + GdipDisposeImage((GpImage*)bm); } START_TEST(image) @@ -4843,6 +4930,7 @@ START_TEST(image) GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); pGdipBitmapGetHistogramSize = (void*)GetProcAddress(mod, "GdipBitmapGetHistogramSize"); + pGdipBitmapGetHistogram = (void*)GetProcAddress(mod, "GdipBitmapGetHistogram"); test_supported_encoders(); test_CloneBitmapArea(); @@ -4890,7 +4978,7 @@ START_TEST(image) test_dispose(); test_createeffect(); test_getadjustedpalette(); - test_histogramsize(); + test_histogram(); GdiplusShutdown(gdiplusToken); } diff --git a/include/gdiplusflat.h b/include/gdiplusflat.h index 824b4601d18..0c40223f754 100644 --- a/include/gdiplusflat.h +++ b/include/gdiplusflat.h @@ -41,6 +41,7 @@ GpStatus WINGDIPAPI GdipSetAdjustableArrowCapWidth(GpAdjustableArrowCap*,REAL); /* Bitmap */ GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap*,CGpEffect*,RECT*,BOOL,VOID**,INT*); GpStatus WINGDIPAPI GdipBitmapCreateApplyEffect(GpBitmap**,INT,CGpEffect*,RECT*,RECT*,GpBitmap**,BOOL,VOID**,INT*); +GpStatus WINGDIPAPI GdipBitmapGetHistogram(GpBitmap*,HistogramFormat,UINT,UINT*,UINT*,UINT*,UINT*); GpStatus WINGDIPAPI GdipBitmapGetHistogramSize(HistogramFormat,UINT*); GpStatus WINGDIPAPI GdipBitmapGetPixel(GpBitmap*,INT,INT,ARGB*); GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap*,GDIPCONST GpRect*,UINT,