Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/tests/brush.c | 222 +++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+)
diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index b8067c735c..35f99524b0 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1634,6 +1634,226 @@ static void test_getHatchStyle(void) GdipDeleteBrush((GpBrush *)brush); }
+static void test_hatchBrushStyles(void) +{ + static const struct + { + short pattern[8]; + GpHatchStyle hs; + BOOL todo; + } + styles[] = + { + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, + { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, + { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, + { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, + { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, + { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal, TRUE }, + { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal, TRUE }, + { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical, TRUE }, + { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti, TRUE }, + { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti, TRUE }, + { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag, TRUE }, + { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave, TRUE }, + { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick, TRUE }, + { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick, TRUE }, + { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave, TRUE }, + { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid, TRUE }, + { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot, TRUE }, + { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid, TRUE }, + { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond, TRUE }, + { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle, TRUE }, + { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis, TRUE }, + { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere, TRUE }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid, TRUE }, + { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard, TRUE }, + { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard, TRUE }, + { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond, TRUE }, + { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond, TRUE }, + }; + static const ARGB colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; + static const int width = 16, height = 16; + GpStatus status; + GpGraphics *graphics; + GpBitmap *bitmap; + GpHatch *brush = NULL; + BOOL match; + int x, y; + int i; + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics); + expect(Ok, status); + ok(graphics != NULL, "Expected the graphics context to be initialized.\n"); + status = GdipSetCompositingMode(graphics, CompositingModeSourceCopy); + expect(Ok, status); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + status = GdipCreateHatchBrush(styles[i].hs, colors[3], colors[0], &brush); + expect(Ok, status); + ok(brush != NULL, "Expected the brush to be initialized.\n"); + status = GdipFillRectangleI(graphics, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipDeleteBrush((GpBrush *)brush); + expect(Ok, status); + brush = NULL; + + match = TRUE; + for(y = 0; y < width && match; y++) + { + for(x = 0; x < height && match; x++) + { + ARGB color; + int cindex = (styles[i].pattern[7-(y%8)] >> (2*(7-(x%8)))) & 3; + + GdipBitmapGetPixel(bitmap, x, y, &color); + if (color != colors[cindex]) + match = FALSE; + } + } + todo_wine_if(styles[i].todo) ok(match, "Unexpected pattern for hatch style %#x.\n", styles[i].hs); + } + + status = GdipDeleteGraphics(graphics); + expect(Ok, status); + status = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, status); +} + +static void test_hatchBrushColors(void) +{ + /* Foreground color, background color, + * resulting 4 colors with non-alpha surface, + * resulting 4 colors with alpha surface + */ + static const ARGB colors[][10] = { + { 0xff000000, 0xffffffff, + 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000, + 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }, + { 0xffffffff, 0xff000000, + 0xff000000, 0xff3f3f3f, 0xffe9e9e9, 0xffffffff, + 0xff000000, 0xff3f3f3f, 0xffe9e9e9, 0xffffffff }, + { 0xffd2d2d2, 0xff000000, + 0xff000000, 0xff343434, 0xffbfbfbf, 0xffd2d2d2, + 0xff000000, 0xff343434, 0xffbfbfbf, 0xffd2d2d2 }, + { 0xfefbfbfb, 0x00070707, + 0xff000000, 0xff3e3e3e, 0xffe4e4e4, 0xfffafafa, + 0x00000000, 0x3ffafafa, 0xe8fafafa, 0xfefafafa }, + }; + static const int width = 16, height = 8; + static const int pixel_coord[][2] = { + {12, 1}, {1, 0}, {0, 0}, {8, 0} + }; + HDC hdc; + GpStatus status; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpGraphics *graphics_image_a; + GpBitmap *bitmap; + GpBitmap *bitmap_a; + GpHatch *brush_diag_cross; + GpHatch *brush_cross; + int i, j; + + hdc = GetDC(hwnd); + status = GdipCreateFromHDC(hdc, &graphics_hdc); + expect(Ok, status); + status = GdipSetCompositingMode(graphics_hdc, CompositingModeSourceCopy); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + expect(Ok, status); + status = GdipSetCompositingMode(graphics_image, CompositingModeSourceCopy); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppARGB, NULL, &bitmap_a); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap_a, &graphics_image_a); + expect(Ok, status); + status = GdipSetCompositingMode(graphics_image_a, CompositingModeSourceCopy); + expect(Ok, status); + + for (i = 0; i < ARRAY_SIZE(colors); i++) + { + status = GdipCreateHatchBrush(HatchStyleDiagonalCross, colors[i][0], colors[i][1], + &brush_diag_cross); + expect(Ok, status); + status = GdipCreateHatchBrush(HatchStyleCross, colors[i][0], colors[i][1], + &brush_cross); + expect(Ok, status); + + GdipFillRectangleI(graphics_hdc, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_image, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_image_a, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_hdc, (GpBrush *)brush_cross, width/2, 0, width/2, height); + GdipFillRectangleI(graphics_image, (GpBrush *)brush_cross, width/2, 0, width/2, height); + GdipFillRectangleI(graphics_image_a, (GpBrush *)brush_cross, width/2, 0, width/2, height); + + GdipDeleteBrush((GpBrush *)brush_diag_cross); + GdipDeleteBrush((GpBrush *)brush_cross); + + for (j = 0; j < 4; j++) + { + ARGB color; + const ARGB exp_color = colors[i][j+2]; + const ARGB exp_color_a = colors[i][j+6]; + const int x = pixel_coord[j][0]; + const int y = pixel_coord[j][1]; + + color = 0xff000000 | GetPixel(hdc, x, y); + todo_wine ok(color == exp_color, "For hwnd (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color, color); + + GdipBitmapGetPixel(bitmap, x, y, &color); + todo_wine ok(color == exp_color, "For rgb image (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color, color); + + GdipBitmapGetPixel(bitmap_a, x, y, &color); + todo_wine ok(color == exp_color_a, "For argb image (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color_a, color); + } + } + + GdipDeleteGraphics(graphics_image); + GdipDeleteGraphics(graphics_image_a); + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)bitmap_a); + + ReleaseDC(hwnd, hdc); + GdipDeleteGraphics(graphics_hdc); +} + START_TEST(brush) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1686,6 +1906,8 @@ START_TEST(brush) test_pathgradientpresetblend(); test_pathgradientblend(); test_getHatchStyle(); + test_hatchBrushStyles(); + test_hatchBrushColors();
GdiplusShutdown(gdiplusToken); DestroyWindow(hwnd);
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 87 +++++++++++++++++++----------- dlls/gdiplus/gdiplus_private.h | 2 +- dlls/gdiplus/graphics.c | 91 +++++++++++++++++++++++++++---- dlls/gdiplus/tests/brush.c | 97 +++++++++++++++++----------------- 4 files changed, 184 insertions(+), 93 deletions(-)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index ff0b009559..17d9beae9c 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -197,40 +197,63 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) return Ok; }
-static const char HatchBrushes[][8] = { - { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */ - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */ - { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */ - { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */ - { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */ - { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */ - { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */ - { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */ - { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */ - { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */ - { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */ - { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */ - { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */ - { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */ - { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */ - { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */ - { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */ - { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */ - { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */ - { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */ - { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */ - { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */ - { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */ - { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */ - { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */ - { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */ - { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */ - { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */ - { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */ - { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ +static const short HatchBrushes[][8] = { + { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff }, /* HatchStyleHorizontal */ + { 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000 }, /* HatchStyleVertical */ + { 0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001 }, /* HatchStyleForwardDiagonal */ + { 0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006 }, /* HatchStyleBackwardDiagonal */ + { 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff }, /* HatchStyleCross */ + { 0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006 }, /* HatchStyleDiagonalCross */ + { 0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000 }, /* HatchStyle05Percent */ + { 0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000 }, /* HatchStyle10Percent */ + { 0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0 }, /* HatchStyle20Percent */ + { 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0 }, /* HatchStyle25Percent */ + { 0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc }, /* HatchStyle30Percent */ + { 0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc }, /* HatchStyle40Percent */ + { 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc }, /* HatchStyle50Percent */ + { 0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc }, /* HatchStyle60Percent */ + { 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f }, /* HatchStyle70Percent */ + { 0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f }, /* HatchStyle75Percent */ + { 0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff }, /* HatchStyle80Percent */ + { 0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff }, /* HatchStyle90Percent */ + { 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0 }, /* HatchStyleLightDownwardDiagonal */ + { 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303 }, /* HatchStyleLightUpwardDiagonal */ + { 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0 }, /* HatchStyleDarkDownwardDiagonal */ + { 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f }, /* HatchStyleDarkUpwardDiagonal */ + { 0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003 }, /* HatchStyleWideDownwardDiagonal */ + { 0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f }, /* HatchStyleWideUpwardDiagonal */ + { 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0 }, /* HatchStyleLightVertical */ + { 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff }, /* HatchStyleLightHorizontal */ + { 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333 }, /* HatchStyleNarrowVertical */ + { 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff }, /* HatchStyleNarrowHorizontal */ + { 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 }, /* HatchStyleDarkVertical */ + { 0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff }, /* HatchStyleDarkHorizontal */ + { 0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000 }, /* HatchStyleDashedDownwardDiagonal */ + { 0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000 }, /* HatchStyleDashedUpwardDiagonal */ + { 0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00 }, /* HatchStyleDashedHorizontal */ + { 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000 }, /* HatchStyleDashedVertical */ + { 0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000 }, /* HatchStyleSmallConfetti */ + { 0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03 }, /* HatchStyleLargeConfetti */ + { 0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003 }, /* HatchStyleZigZag */ + { 0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000 }, /* HatchStyleWave */ + { 0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003 }, /* HatchStyleDiagonalBrick */ + { 0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff }, /* HatchStyleHorizontalBrick */ + { 0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0 }, /* HatchStyleWeave */ + { 0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc }, /* HatchStylePlaid */ + { 0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000 }, /* HatchStyleDivot */ + { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc }, /* HatchStyleDottedGrid */ + { 0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000 }, /* HatchStyleDottedDiamond */ + { 0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f }, /* HatchStyleShingle */ + { 0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff }, /* HatchStyleTrellis */ + { 0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f }, /* HatchStyleSphere */ + { 0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff }, /* HatchStyleSmallGrid */ + { 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3 }, /* HatchStyleSmallCheckerBoard */ + { 0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00 }, /* HatchStyleLargeCheckerBoard */ + { 0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c }, /* HatchStyleOutlinedDiamond */ + { 0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300 }, /* HatchStyleSolidDiamond */ };
-GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) +GpStatus get_hatch_data(GpHatchStyle hatchstyle, const short **result) { if (hatchstyle < ARRAY_SIZE(HatchBrushes)) { diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 9e90a5d28c..d9f1e3673a 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -123,7 +123,7 @@ extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN; typedef struct region_element region_element; extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
-extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN; +extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const short **result) DECLSPEC_HIDDEN;
static inline INT gdip_round(REAL x) { diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 72075e02fb..cf588ec302 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -114,6 +114,24 @@ static COLORREF get_gdi_brush_color(const GpBrush *brush) return ARGB2COLORREF(argb); }
+static ARGB blend_colors_pre(ARGB start, ARGB end, REAL position) +{ + const BYTE a = ((start >> 24) & 0xff) * (1 - position) + ((end >> 24) & 0xff) * position; + const BYTE r = ((start >> 16) & 0xff) * (1 - position) + ((end >> 16) & 0xff) * position; + const BYTE g = ((start >> 8) & 0xff) * (1 - position) + ((end >> 8) & 0xff) * position; + const BYTE b = (start & 0xff) * (1 - position) + (end & 0xff) * position; + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +static void init_hatch_palette(ARGB *hatch_palette, ARGB back_color, ARGB fore_color) +{ + hatch_palette[0] = back_color; + hatch_palette[1] = blend_colors_pre(back_color, fore_color, 0.25); + hatch_palette[2] = blend_colors_pre(back_color, fore_color, sqrt(2.0) - 0.5); + hatch_palette[3] = fore_color; +} + static HBITMAP create_hatch_bitmap(const GpHatch *hatch) { HBITMAP hbmp; @@ -132,18 +150,19 @@ static HBITMAP create_hatch_bitmap(const GpHatch *hatch) hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0); if (hbmp) { - const char *hatch_data; + const short *hatch_data;
if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok) { + ARGB hatch_palette[4]; + init_hatch_palette(hatch_palette, hatch->backcol, hatch->forecol); + for (y = 0; y < 8; y++) { for (x = 0; x < 8; x++) { - if (hatch_data[y] & (0x80 >> x)) - bits[y * 8 + x] = hatch->forecol; - else - bits[y * 8 + x] = hatch->backcol; + int index = (hatch_data[y] >> (2 * (7-x))) & 3; + bits[y * 8 + x] = hatch_palette[index]; } } } @@ -1142,6 +1161,38 @@ static BOOL brush_can_fill_pixels(GpBrush *brush) } }
+/* Convert an ARGB value to PARGB */ +static ARGB premult_color(ARGB c) +{ + const BYTE a = (c >> 24) & 0xff; + const BYTE r = (((c >> 16) & 0xff) * a + 127) / 255; + const BYTE g = (((c >> 8) & 0xff) * a + 127) / 255; + const BYTE b = ((c & 0xff) * a + 127) / 255; + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +/* Convert a PARGB color value to ARGB + * + * Since this is not perfectly symetric with premult_color, + * rev_premult_color(premult_color(orig_color)) will often not return orig_color. + */ +static ARGB rev_premult_color(ARGB c) +{ + const BYTE a = (c >> 24) & 0xff; + if (a == 0) + return 0; + else + { + const DWORD scaled_q = (255 << 15) / a; + const BYTE r = (((c >> 16) & 0xff) * scaled_q) >> 15; + const BYTE g = (((c >> 8) & 0xff) * scaled_q) >> 15; + const BYTE b = ((c & 0xff) * scaled_q) >> 15; + + return (a << 24) | (r << 16) | (g << 8) | b; + } +} + static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, DWORD *argb_pixels, GpRect *fill_area, UINT cdwStride) { @@ -1160,24 +1211,42 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, { int x, y; GpHatch *fill = (GpHatch*)brush; - const char *hatch_data; + const short *hatch_data; + ARGB hatch_palette[4];
if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok) return NotImplemented;
+ if (((fill->backcol & 0xff000000) == 0xff000000) && + ((fill->forecol & 0xff000000) == 0xff000000)) + init_hatch_palette(hatch_palette, fill->backcol, fill->forecol); + else if (graphics->image && graphics->image->type == ImageTypeBitmap && + IsAlphaPixelFormat(((GpBitmap *)graphics->image)->format)) + { + init_hatch_palette(hatch_palette, + premult_color(fill->backcol), + premult_color(fill->forecol)); + hatch_palette[0] = rev_premult_color(hatch_palette[0]); + hatch_palette[1] = rev_premult_color(hatch_palette[1]); + hatch_palette[2] = rev_premult_color(hatch_palette[2]); + hatch_palette[3] = rev_premult_color(hatch_palette[3]); + } + else + init_hatch_palette(hatch_palette, + premult_color(fill->backcol) | 0xff000000, + premult_color(fill->forecol) | 0xff000000); + for (x=0; x<fill_area->Width; x++) for (y=0; y<fill_area->Height; y++) { - int hx, hy; + int hx, hy, index;
/* FIXME: Account for the rendering origin */ hx = (x + fill_area->X) % 8; hy = (y + fill_area->Y) % 8;
- if ((hatch_data[7-hy] & (0x80 >> hx)) != 0) - argb_pixels[x + y*cdwStride] = fill->forecol; - else - argb_pixels[x + y*cdwStride] = fill->backcol; + index = (hatch_data[7-hy] >> (2 * (7-hx))) & 3; + argb_pixels[x + y*cdwStride] = hatch_palette[index]; }
return Ok; diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 35f99524b0..fbd89f7631 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1640,63 +1640,62 @@ static void test_hatchBrushStyles(void) { short pattern[8]; GpHatchStyle hs; - BOOL todo; } styles[] = { - { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, - { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, - { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, - { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross }, { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, - { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, - { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, - { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, - { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, - { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, - { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, - { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, - { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, - { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, - { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, - { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent }, { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, - { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, - { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, - { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal }, { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, - { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical }, { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, - { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal, TRUE }, - { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal, TRUE }, - { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal, TRUE }, - { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical, TRUE }, - { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti, TRUE }, - { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti, TRUE }, - { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag, TRUE }, - { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave, TRUE }, - { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick, TRUE }, - { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick, TRUE }, - { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave, TRUE }, - { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid, TRUE }, - { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot, TRUE }, - { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid, TRUE }, - { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond, TRUE }, - { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle, TRUE }, - { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis, TRUE }, - { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere, TRUE }, - { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid, TRUE }, - { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard, TRUE }, - { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard, TRUE }, - { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond, TRUE }, - { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond, TRUE }, + { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal }, + { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal }, + { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal }, + { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical }, + { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti }, + { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti }, + { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag }, + { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave }, + { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick }, + { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick }, + { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave }, + { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid }, + { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot }, + { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid }, + { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond }, + { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle }, + { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis }, + { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid }, + { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard }, + { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard }, + { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond }, + { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond }, }; static const ARGB colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; static const int width = 16, height = 16; @@ -1740,7 +1739,7 @@ static void test_hatchBrushStyles(void) match = FALSE; } } - todo_wine_if(styles[i].todo) ok(match, "Unexpected pattern for hatch style %#x.\n", styles[i].hs); + ok(match, "Unexpected pattern for hatch style %#x.\n", styles[i].hs); }
status = GdipDeleteGraphics(graphics); @@ -1832,15 +1831,15 @@ static void test_hatchBrushColors(void) const int y = pixel_coord[j][1];
color = 0xff000000 | GetPixel(hdc, x, y); - todo_wine ok(color == exp_color, "For hwnd (%d, %d) colorset %d expected %08x, got %08x.\n", + ok(color == exp_color, "For hwnd (%d, %d) colorset %d expected %08x, got %08x.\n", x, y, i, exp_color, color);
GdipBitmapGetPixel(bitmap, x, y, &color); - todo_wine ok(color == exp_color, "For rgb image (%d, %d) colorset %d expected %08x, got %08x.\n", + ok(color == exp_color, "For rgb image (%d, %d) colorset %d expected %08x, got %08x.\n", x, y, i, exp_color, color);
GdipBitmapGetPixel(bitmap_a, x, y, &color); - todo_wine ok(color == exp_color_a, "For argb image (%d, %d) colorset %d expected %08x, got %08x.\n", + ok(color == exp_color_a, "For argb image (%d, %d) colorset %d expected %08x, got %08x.\n", x, y, i, exp_color_a, color); } }
+ hatch_palette[2] = blend_colors_pre(back_color, fore_color, sqrt(2.0) - 0.5);
What is this magic number?
I'm not sure how to describe it succinctly, but if you pass a diagonal line with a width of one unit through the middle of a unit square, this is the portion of the square that is covered. Also note that an adjacent square, which just has the corner nicked, will have 1/4 of the surface covered.
On Thu, Jun 11, 2020 at 9:08 PM Esme Povirk (they/them) vincent@codeweavers.com wrote:
- hatch_palette[2] = blend_colors_pre(back_color, fore_color,
sqrt(2.0) - 0.5);
What is this magic number?
Or visually: https://www.desmos.com/calculator/f7czote8qs
On Thu, Jun 11, 2020 at 9:34 PM Jeff Smith whydoubt@gmail.com wrote:
I'm not sure how to describe it succinctly, but if you pass a diagonal line with a width of one unit through the middle of a unit square, this is the portion of the square that is covered. Also note that an adjacent square, which just has the corner nicked, will have 1/4 of the surface covered.
On Thu, Jun 11, 2020 at 9:08 PM Esme Povirk (they/them) vincent@codeweavers.com wrote:
- hatch_palette[2] = blend_colors_pre(back_color, fore_color,
sqrt(2.0) - 0.5);
What is this magic number?
I'm not sure if it's OK from a copyright perspective to copy these exactly.
On Wed, Jun 10, 2020 at 10:36 AM Esme Povirk (they/them) vincent@codeweavers.com wrote:
I'm not sure if it's OK from a copyright perspective to copy these exactly.
That thought had occurred to me as well.
Basic geometric patterns should not be a problem. However, I could maybe see where something like the "shingle" or "sphere" patterns exhibit some creativity.
What do you think about if I just fix the existing patterns (all of which are either dithering patterns, or consist of vertical/horizontal/diagonal lines)?
Well, really I don't think copyright questions should be my call. I guess I could review your patches without that and if I sign off include a comment about the copyright question
It seems like you're putting a lot of effort into testing how colors are blended rather than the actual hatch brushes.
Agreed. I've actually been working on an alternate patch set that includes breaking that out.
On Thu, Jun 11, 2020 at 9:03 PM Esme Povirk (they/them) vincent@codeweavers.com wrote:
It seems like you're putting a lot of effort into testing how colors are blended rather than the actual hatch brushes.
Signed-off-by: Jeff Smith whydoubt@gmail.com --- v2: - Split pattern fixes from anti-aliasing fixes - Removed changes for blending transparent colors (needs more work) - Added some comments relating to anti-aliasing - Fixed conversion of COLORREF to ARGB
dlls/gdiplus/tests/brush.c | 125 +++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+)
diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index b8067c735c..ddc336c94d 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1634,6 +1634,130 @@ static void test_getHatchStyle(void) GdipDeleteBrush((GpBrush *)brush); }
+static ARGB get_pixel_as_argb(HDC hdc, int x, int y) +{ + COLORREF color = GetPixel(hdc, x, y); + return 0xff000000 | + (color & 0xff) << 16 | + (color & 0xff00) | + (color & 0xff0000) >> 16; +} + +static void test_hatchBrushStyles(void) +{ + static const struct + { + short pattern[8]; + GpHatchStyle hs; + BOOL todo; + } + styles[] = + { + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, + { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, + { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, + { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, + { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, + { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + }; + static const ARGB exp_colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; + static const ARGB fore_color = 0xff000000; + static const ARGB back_color = 0xffffffff; + static const int width = 16, height = 16; + GpStatus status; + HDC hdc; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpBitmap *bitmap; + GpHatch *brush = NULL; + BOOL match_hdc; + BOOL match_image; + int x, y; + int i; + + hdc = GetDC(hwnd); + status = GdipCreateFromHDC(hdc, &graphics_hdc); + expect(Ok, status); + ok(graphics_hdc != NULL, "Expected the graphics context to be initialized.\n"); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + expect(Ok, status); + ok(graphics_image != NULL, "Expected the graphics context to be initialized.\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + status = GdipCreateHatchBrush(styles[i].hs, fore_color, back_color, &brush); + expect(Ok, status); + ok(brush != NULL, "Expected the brush to be initialized.\n"); + status = GdipFillRectangleI(graphics_hdc, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipFillRectangleI(graphics_image, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipDeleteBrush((GpBrush *)brush); + expect(Ok, status); + brush = NULL; + + match_hdc = TRUE; + match_image = TRUE; + for(y = 0; y < width && (match_hdc || match_image); y++) + { + for(x = 0; x < height && (match_hdc || match_image); x++) + { + ARGB color; + int cindex = (styles[i].pattern[7-(y%8)] >> (2*(7-(x%8)))) & 3; + + color = get_pixel_as_argb(hdc, x, y); + if (color != exp_colors[cindex]) + match_hdc = FALSE; + + GdipBitmapGetPixel(bitmap, x, y, &color); + if (color != exp_colors[cindex]) + match_image = FALSE; + } + } + todo_wine_if(styles[i].todo) + { + ok(match_hdc, "Unexpected pattern for hatch style %#x with hdc.\n", styles[i].hs); + ok(match_image, "Unexpected pattern for hatch style %#x with image.\n", styles[i].hs); + } + } + + status = GdipDeleteGraphics(graphics_image); + expect(Ok, status); + status = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, status); + + status = GdipDeleteGraphics(graphics_hdc); + expect(Ok, status); + ReleaseDC(hwnd, hdc); +} + START_TEST(brush) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1686,6 +1810,7 @@ START_TEST(brush) test_pathgradientpresetblend(); test_pathgradientblend(); test_getHatchStyle(); + test_hatchBrushStyles();
GdiplusShutdown(gdiplusToken); DestroyWindow(hwnd);
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 36 ++++++++++++++++++------------------ dlls/gdiplus/tests/brush.c | 36 ++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index ff0b009559..aa1cda1990 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -198,33 +198,33 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) }
static const char HatchBrushes[][8] = { - { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */ - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleHorizontal */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleVertical */ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */ { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */ - { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleCross */ { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */ { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */ - { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */ - { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */ - { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */ - { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */ - { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */ - { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */ - { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */ - { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */ - { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */ - { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */ - { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */ + { 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00, 0x80 }, /* HatchStyle10Percent */ + { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }, /* HatchStyle20Percent */ + { 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88 }, /* HatchStyle25Percent */ + { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa }, /* HatchStyle30Percent */ + { 0x15, 0xaa, 0x55, 0xaa, 0x51, 0xaa, 0x55, 0xaa }, /* HatchStyle40Percent */ + { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }, /* HatchStyle50Percent */ + { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }, /* HatchStyle60Percent */ + { 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77 }, /* HatchStyle70Percent */ + { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 }, /* HatchStyle75Percent */ + { 0xff, 0xfe, 0xff, 0xef, 0xff, 0xfe, 0xff, 0xef }, /* HatchStyle80Percent */ + { 0x7f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff }, /* HatchStyle90Percent */ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */ { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */ - { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */ - { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */ - { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */ + { 0x99, 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33 }, /* HatchStyleDarkUpwardDiagonal */ + { 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xc1 }, /* HatchStyleWideDownwardDiagonal */ + { 0xc1, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83 }, /* HatchStyleWideUpwardDiagonal */ { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */ { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */ - { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */ + { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, /* HatchStyleNarrowVertical */ { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */ { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index ddc336c94d..445b0cd126 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1653,33 +1653,33 @@ static void test_hatchBrushStyles(void) } styles[] = { - { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, - { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, - { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, - { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, - { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, - { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, - { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, - { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, - { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, - { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, - { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, - { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent }, { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, - { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, - { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, - { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal }, { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, - { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical }, { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal },
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/tests/brush.c | 94 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+)
diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 445b0cd126..8e50318268 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1758,6 +1758,99 @@ static void test_hatchBrushStyles(void) ReleaseDC(hwnd, hdc); }
+static void test_hatchBrushColors(void) +{ + /* Foreground color, background color, + * resulting 4 colors */ + static const ARGB colors[][6] = { + { 0xff000000, 0xffffffff, + 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }, + { 0xffffffff, 0xff000000, + 0xff000000, 0xff3f3f3f, 0xffe9e9e9, 0xffffffff }, + { 0xffd2d2d2, 0xff000000, + 0xff000000, 0xff343434, 0xffbfbfbf, 0xffd2d2d2 }, + }; + static const int width = 16, height = 8; + static const int pixel_coord[][2] = { + {12, 1}, {1, 0}, {0, 0}, {8, 0} + }; + HDC hdc; + GpStatus status; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpGraphics *graphics_image_a; + GpBitmap *bitmap; + GpBitmap *bitmap_a; + GpHatch *brush_diag_cross; + GpHatch *brush_cross; + int i, j; + + hdc = GetDC(hwnd); + status = GdipCreateFromHDC(hdc, &graphics_hdc); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + expect(Ok, status); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppARGB, NULL, &bitmap_a); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap_a, &graphics_image_a); + expect(Ok, status); + + for (i = 0; i < ARRAY_SIZE(colors); i++) + { + status = GdipCreateHatchBrush(HatchStyleDiagonalCross, colors[i][0], colors[i][1], + &brush_diag_cross); + expect(Ok, status); + status = GdipCreateHatchBrush(HatchStyleCross, colors[i][0], colors[i][1], + &brush_cross); + expect(Ok, status); + + GdipFillRectangleI(graphics_hdc, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_image, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_image_a, (GpBrush *)brush_diag_cross, 0, 0, width/2, height); + GdipFillRectangleI(graphics_hdc, (GpBrush *)brush_cross, width/2, 0, width/2, height); + GdipFillRectangleI(graphics_image, (GpBrush *)brush_cross, width/2, 0, width/2, height); + GdipFillRectangleI(graphics_image_a, (GpBrush *)brush_cross, width/2, 0, width/2, height); + + GdipDeleteBrush((GpBrush *)brush_diag_cross); + GdipDeleteBrush((GpBrush *)brush_cross); + + for (j = 0; j < 4; j++) + { + ARGB color; + const ARGB exp_color = colors[i][j+2]; + const int x = pixel_coord[j][0]; + const int y = pixel_coord[j][1]; + + todo_wine_if(j == 1 || j == 2) + { + color = get_pixel_as_argb(hdc, x, y); + ok(color == exp_color, "For hwnd (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color, color); + + GdipBitmapGetPixel(bitmap, x, y, &color); + ok(color == exp_color, "For rgb image (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color, color); + + GdipBitmapGetPixel(bitmap_a, x, y, &color); + ok(color == exp_color, "For argb image (%d, %d) colorset %d expected %08x, got %08x.\n", + x, y, i, exp_color, color); + } + } + } + + GdipDeleteGraphics(graphics_hdc); + GdipDeleteGraphics(graphics_image); + GdipDeleteGraphics(graphics_image_a); + + GdipDisposeImage((GpImage*)bitmap); + GdipDisposeImage((GpImage*)bitmap_a); + ReleaseDC(hwnd, hdc); +} + START_TEST(brush) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1811,6 +1904,7 @@ START_TEST(brush) test_pathgradientblend(); test_getHatchStyle(); test_hatchBrushStyles(); + test_hatchBrushColors();
GdiplusShutdown(gdiplusToken); DestroyWindow(hwnd);
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 12 +++--- dlls/gdiplus/gdiplus_private.h | 2 +- dlls/gdiplus/graphics.c | 73 ++++++++++++++++++++++++++-------- dlls/gdiplus/tests/brush.c | 13 ++---- 4 files changed, 68 insertions(+), 32 deletions(-)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index aa1cda1990..4b6ccca12a 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -197,13 +197,15 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) return Ok; }
-static const char HatchBrushes[][8] = { +/* The first 8 items per entry are bitmaps for each row of the hatch style. + * The 9th item of the entry is a flag indicating anti-aliasing. */ +static const unsigned char HatchBrushes[][9] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleHorizontal */ { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleVertical */ - { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */ - { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */ + { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, TRUE }, /* HatchStyleForwardDiagonal */ + { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, TRUE }, /* HatchStyleBackwardDiagonal */ { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleCross */ - { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */ + { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, TRUE }, /* HatchStyleDiagonalCross */ { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */ { 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00, 0x80 }, /* HatchStyle10Percent */ { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }, /* HatchStyle20Percent */ @@ -230,7 +232,7 @@ static const char HatchBrushes[][8] = { { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ };
-GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) +GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) { if (hatchstyle < ARRAY_SIZE(HatchBrushes)) { diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 9e90a5d28c..d62860360a 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -123,7 +123,7 @@ extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN; typedef struct region_element region_element; extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
-extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN; +extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) DECLSPEC_HIDDEN;
static inline INT gdip_round(REAL x) { diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 72075e02fb..c2421339e5 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -114,6 +114,27 @@ static COLORREF get_gdi_brush_color(const GpBrush *brush) return ARGB2COLORREF(argb); }
+static ARGB blend_colors_pre(ARGB start, ARGB end, REAL position) +{ + const BYTE a = ((start >> 24) & 0xff) * (1 - position) + ((end >> 24) & 0xff) * position; + const BYTE r = ((start >> 16) & 0xff) * (1 - position) + ((end >> 16) & 0xff) * position; + const BYTE g = ((start >> 8) & 0xff) * (1 - position) + ((end >> 8) & 0xff) * position; + const BYTE b = (start & 0xff) * (1 - position) + (end & 0xff) * position; + + return (a << 24) | (r << 16) | (g << 8) | b; +} + +static void init_hatch_palette(ARGB *hatch_palette, ARGB fore_color, ARGB back_color) +{ + /* Pass the center of a 45-degree diagonal line with width of one unit through the + * center of a unit square, and the portion of the square that will be covered will + * equal sqrt(2) - 1/2. The covered portion for adjacent squares will be 1/4. */ + hatch_palette[0] = back_color; + hatch_palette[1] = blend_colors_pre(back_color, fore_color, 0.25); + hatch_palette[2] = blend_colors_pre(back_color, fore_color, sqrt(2.0) - 0.5); + hatch_palette[3] = fore_color; +} + static HBITMAP create_hatch_bitmap(const GpHatch *hatch) { HBITMAP hbmp; @@ -132,18 +153,30 @@ static HBITMAP create_hatch_bitmap(const GpHatch *hatch) hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0); if (hbmp) { - const char *hatch_data; + const unsigned char *hatch_data;
if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok) { + ARGB hatch_palette[4]; + init_hatch_palette(hatch_palette, hatch->forecol, hatch->backcol); + + /* Anti-aliasing is only specified for diagonal hatch patterns. + * This implementation repeats the pattern, shifts as needed, + * then uses bitmask 1 to check the pixel value, and the 0x82 + * bitmask to check the adjacent pixel values, to determine the + * degree of shading needed. */ for (y = 0; y < 8; y++) { - for (x = 0; x < 8; x++) + unsigned int row = 0x101 * hatch_data[y]; + + for (x = 0; x < 8; x++, row >>= 1) { - if (hatch_data[y] & (0x80 >> x)) - bits[y * 8 + x] = hatch->forecol; + int index; + if (hatch_data[8]) + index = (row & 1) ? 2 : (row & 0x82) ? 1 : 0; else - bits[y * 8 + x] = hatch->backcol; + index = (row & 1) ? 3 : 0; + bits[y * 8 + 7 - x] = hatch_palette[index]; } } } @@ -1160,25 +1193,33 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, { int x, y; GpHatch *fill = (GpHatch*)brush; - const char *hatch_data; + const unsigned char *hatch_data; + ARGB hatch_palette[4];
if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok) return NotImplemented;
- for (x=0; x<fill_area->Width; x++) - for (y=0; y<fill_area->Height; y++) - { - int hx, hy; + init_hatch_palette(hatch_palette, fill->forecol, fill->backcol);
- /* FIXME: Account for the rendering origin */ - hx = (x + fill_area->X) % 8; - hy = (y + fill_area->Y) % 8; + /* See create_hatch_bitmap for an explanation of how index is derived. */ + for (y = 0; y < fill_area->Height; y++, argb_pixels += cdwStride) + { + /* FIXME: Account for the rendering origin */ + const int hy = 7 - ((y + fill_area->Y) % 8); + const unsigned int row = 0x101 * hatch_data[hy];
- if ((hatch_data[7-hy] & (0x80 >> hx)) != 0) - argb_pixels[x + y*cdwStride] = fill->forecol; + for (x = 0; x < fill_area->Width; x++) + { + const unsigned int srow = row >> (7 - ((x + fill_area->X) % 8)); + int index; + if (hatch_data[8]) + index = (srow & 1) ? 2 : (srow & 0x82) ? 1 : 0; else - argb_pixels[x + y*cdwStride] = fill->backcol; + index = (srow & 1) ? 3 : 0; + + argb_pixels[x] = hatch_palette[index]; } + }
return Ok; } diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 8e50318268..4afc6fcb76 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1649,16 +1649,15 @@ static void test_hatchBrushStyles(void) { short pattern[8]; GpHatchStyle hs; - BOOL todo; } styles[] = { { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, - { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, - { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal }, { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, - { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross }, { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, @@ -1741,11 +1740,8 @@ static void test_hatchBrushStyles(void) match_image = FALSE; } } - todo_wine_if(styles[i].todo) - { ok(match_hdc, "Unexpected pattern for hatch style %#x with hdc.\n", styles[i].hs); ok(match_image, "Unexpected pattern for hatch style %#x with image.\n", styles[i].hs); - } }
status = GdipDeleteGraphics(graphics_image); @@ -1825,8 +1821,6 @@ static void test_hatchBrushColors(void) const int x = pixel_coord[j][0]; const int y = pixel_coord[j][1];
- todo_wine_if(j == 1 || j == 2) - { color = get_pixel_as_argb(hdc, x, y); ok(color == exp_color, "For hwnd (%d, %d) colorset %d expected %08x, got %08x.\n", x, y, i, exp_color, color); @@ -1838,7 +1832,6 @@ static void test_hatchBrushColors(void) GdipBitmapGetPixel(bitmap_a, x, y, &color); ok(color == exp_color, "For argb image (%d, %d) colorset %d expected %08x, got %08x.\n", x, y, i, exp_color, color); - } } }
+static ARGB blend_colors_pre(ARGB start, ARGB end, REAL position) +{ + const BYTE a = ((start >> 24) & 0xff) * (1 - position) + ((end >> 24) & 0xff) * position; + const BYTE r = ((start >> 16) & 0xff) * (1 - position) + ((end >> 16) & 0xff) * position; + const BYTE g = ((start >> 8) & 0xff) * (1 - position) + ((end >> 8) & 0xff) * position; + const BYTE b = (start & 0xff) * (1 - position) + (end & 0xff) * position; + + return (a << 24) | (r << 16) | (g << 8) | b; +}
I'm confused by the introduction of this function. It seems to be for blending PARGB colors, but the hatch brush colors are ARGB.
On Sat, Jun 13, 2020 at 9:29 PM Esme Povirk (they/them) vincent@codeweavers.com wrote:
+static ARGB blend_colors_pre(ARGB start, ARGB end, REAL position) +{
- const BYTE a = ((start >> 24) & 0xff) * (1 - position) + ((end >>
- & 0xff) * position;
- const BYTE r = ((start >> 16) & 0xff) * (1 - position) + ((end >>
- & 0xff) * position;
- const BYTE g = ((start >> 8) & 0xff) * (1 - position) + ((end >>
- & 0xff) * position;
- const BYTE b = (start & 0xff) * (1 - position) + (end & 0xff) * position;
- return (a << 24) | (r << 16) | (g << 8) | b;
+}
I'm confused by the introduction of this function. It seems to be for blending PARGB colors, but the hatch brush colors are ARGB.
While hatch brush colors are specified as ARGB, for the purpose of anti-aliasing, they are blended as PARGB. This is effectively the same for opaque hatch colors. For transparent hatch colors, alpha will be correct, but rgb components will be off to some degree.
Fixing the transparent colors takes a bit of extra work, and I didn't want to make this patchset any larger. I'm not quite satisfied with the state of that work either, to be honest.
On Sat, Jun 13, 2020 at 11:11 PM Jeff Smith whydoubt@gmail.com wrote:
While hatch brush colors are specified as ARGB, for the purpose of anti-aliasing, they are blended as PARGB. This is effectively the same for opaque hatch colors. For transparent hatch colors, alpha will be correct, but rgb components will be off to some degree.
Why not blend them as ARGB?
On Sat, Jun 13, 2020 at 11:46 PM Esme Povirk (they/them) vincent@codeweavers.com wrote:
On Sat, Jun 13, 2020 at 11:11 PM Jeff Smith whydoubt@gmail.com wrote:
While hatch brush colors are specified as ARGB, for the purpose of anti-aliasing, they are blended as PARGB. This is effectively the same for opaque hatch colors. For transparent hatch colors, alpha will be correct, but rgb components will be off to some degree.
Let me break this out further so maybe we can get on the same page. Hopefully this doesn't get completely butchered...
The best-case-scenario for compositing ARGB is this:
[forecolor ARGB] ---> {premult} -----> [forecolor PARGB] | [result ARGB] <-- {premult^-1} <-- [result PARGB] <-- {blend} | [backcolor ARGB] ---> {premult} -----> [backcolor PARGB]
The {blend} function interpolates each channel independently. The {premult^-1} function is the inverse of {premult}.
For this patch I am focused on getting the RGB subset (i.e. A = 255) right, which makes {premult} a no-op and simplifies to
[forecolor RGB] | [result RGB] <-- {blend} | [backcolor RGB]
The {blend} function is the same though (except that strictly speaking it also simplifies the result A to a constant 255).
Except for not hardcoding A = 255, this is what this patch does. For this RGB subset, I have tested extensively and it matches results from native gdiplus perfectly AFAICT.
Why not blend them as ARGB?
What are you suggesting that is not being done now?
On Sun, Jun 14, 2020 at 2:16 AM Jeff Smith whydoubt@gmail.com wrote:
Why not blend them as ARGB?
What are you suggesting that is not being done now?
Use blend_colors. It's intended to blend two ARGB colors. If it doesn't work correctly, fix it.
If the color values are slightly off from what native produces, but there's no clear reason ours are worse, I don't think we should care. Matching the output perfectly is not necessary for application compatibility.
I don't see a reason why blending should work differently in this case from other cases that use blend_colors.
On Sun, Jun 14, 2020 at 9:15 AM Esme Povirk (they/them) vincent@codeweavers.com wrote:
On Sun, Jun 14, 2020 at 2:16 AM Jeff Smith whydoubt@gmail.com wrote:
Why not blend them as ARGB?
What are you suggesting that is not being done now?
Use blend_colors. It's intended to blend two ARGB colors. If it doesn't work correctly, fix it.
Thank you for clarifying. I just want to make sure that I am addressing the right concerns. As you suggest, I will simply use blend_colors for now, and leave further blending improvements out of this patchset. I guess I'll also make the test accept inexact matches for now.
If the color values are slightly off from what native produces, but there's no clear reason ours are worse, I don't think we should care.
If we have an alternative that matches what native produces, and there's no clear reason it is worse, perhaps we should.
Matching the output perfectly is not necessary for application compatibility.
True, but I think this is an area where small errors can compound quickly, so I do think fidelity with native is warranted here if practical.
I don't see a reason why blending should work differently in this case from other cases that use blend_colors.
I agree that it shouldn't in principle, and I hope that holds true in practice.
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 23 +++++++++++++++++++++++ dlls/gdiplus/tests/brush.c | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index 4b6ccca12a..d0da60df3a 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -230,6 +230,29 @@ static const unsigned char HatchBrushes[][9] = { { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */ { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ + { 0x00, 0x00, 0x11, 0x22, 0x44, 0x88, 0x00, 0x00 }, /* HatchStyleDashedDownwardDiagonal */ + { 0x00, 0x00, 0x88, 0x44, 0x22, 0x11, 0x00, 0x00 }, /* HatchStyleDashedUpwardDiagonal */ + { 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xf0 }, /* HatchStyleDashedHorizontal */ + { 0x08, 0x08, 0x08, 0x08, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleDashedVertical */ + { 0x04, 0x20, 0x01, 0x10, 0x02, 0x40, 0x08, 0x80 }, /* HatchStyleSmallConfetti */ + { 0x8d, 0x0c, 0xc0, 0xd8, 0x1b, 0x03, 0x30, 0xb1 }, /* HatchStyleLargeConfetti */ + { 0x18, 0x24, 0x42, 0x81, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleZigZag */ + { 0xc0, 0x25, 0x18, 0x00, 0xc0, 0x25, 0x18, 0x00 }, /* HatchStyleWave */ + { 0x81, 0x42, 0x24, 0x18, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleDiagonalBrick */ + { 0x08, 0x08, 0x08, 0xff, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleHorizontalBrick */ + { 0x51, 0x22, 0x14, 0x88, 0x45, 0x22, 0x54, 0x88 }, /* HatchStyleWeave */ + { 0xf0, 0xf0, 0xf0, 0xf0, 0x55, 0xaa, 0x55, 0xaa }, /* HatchStylePlaid */ + { 0x80, 0x01, 0x80, 0x00, 0x10, 0x08, 0x10, 0x00 }, /* HatchStyleDivot */ + { 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xaa }, /* HatchStyleDottedGrid */ + { 0x00, 0x22, 0x00, 0x08, 0x00, 0x22, 0x00, 0x80 }, /* HatchStyleDottedDiamond */ + { 0x01, 0x01, 0x02, 0x0c, 0x30, 0x48, 0x84, 0x03 }, /* HatchStyleShingle */ + { 0x99, 0xff, 0x66, 0xff, 0x99, 0xff, 0x66, 0xff }, /* HatchStyleTrellis */ + { 0xf8, 0xf8, 0x98, 0x77, 0x8f, 0x8f, 0x89, 0x77 }, /* HatchStyleSphere */ + { 0x88, 0x88, 0x88, 0xff, 0x88, 0x88, 0x88, 0xff }, /* HatchStyleSmallGrid */ + { 0x99, 0x66, 0x66, 0x99, 0x99, 0x66, 0x66, 0x99 }, /* HatchStyleSmallCheckerBoard */ + { 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0 }, /* HatchStyleLargeCheckerBoard */ + { 0x01, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82 }, /* HatchStyleOutlinedDiamond */ + { 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10 }, /* HatchStyleSolidDiamond */ };
GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 4afc6fcb76..beec3c5af6 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1682,6 +1682,29 @@ static void test_hatchBrushStyles(void) { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal }, + { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal }, + { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal }, + { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical }, + { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti }, + { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti }, + { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag }, + { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave }, + { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick }, + { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick }, + { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave }, + { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid }, + { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot }, + { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid }, + { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond }, + { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle }, + { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis }, + { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid }, + { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard }, + { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard }, + { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond }, + { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond }, }; static const ARGB exp_colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; static const ARGB fore_color = 0xff000000;
Signed-off-by: Jeff Smith whydoubt@gmail.com --- v3: - use existing blend_colors function - accept inexact color matches in test_hatchBrushStyles - leave out test_hatchBrushColors - switch to COLORREF2ARGB for converting GetPixel results
dlls/gdiplus/tests/brush.c | 126 +++++++++++++++++++++++++++++++++++++ dlls/gdiplus/tests/image.c | 2 +- 2 files changed, 127 insertions(+), 1 deletion(-)
diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index b8067c735c..3bf9277377 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1634,6 +1634,131 @@ static void test_getHatchStyle(void) GdipDeleteBrush((GpBrush *)brush); }
+static ARGB COLORREF2ARGB(COLORREF color) +{ + return 0xff000000 | + (color & 0xff) << 16 | + (color & 0xff00) | + (color & 0xff0000) >> 16; +} + +extern BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff); + +static void test_hatchBrushStyles(void) +{ + static const struct + { + short pattern[8]; + GpHatchStyle hs; + BOOL todo; + } + styles[] = + { + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, + { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, + { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, + { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, + { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, + { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + }; + static const ARGB exp_colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; + static const ARGB fore_color = 0xff000000; + static const ARGB back_color = 0xffffffff; + static const int width = 16, height = 16; + GpStatus status; + HDC hdc; + GpGraphics *graphics_hdc; + GpGraphics *graphics_image; + GpBitmap *bitmap; + GpHatch *brush = NULL; + BOOL match_hdc; + BOOL match_image; + int x, y; + int i; + + hdc = GetDC(hwnd); + status = GdipCreateFromHDC(hdc, &graphics_hdc); + expect(Ok, status); + ok(graphics_hdc != NULL, "Expected the graphics context to be initialized.\n"); + + status = GdipCreateBitmapFromScan0(width, height, 0, PixelFormat32bppRGB, NULL, &bitmap); + expect(Ok, status); + status = GdipGetImageGraphicsContext((GpImage *)bitmap, &graphics_image); + expect(Ok, status); + ok(graphics_image != NULL, "Expected the graphics context to be initialized.\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + status = GdipCreateHatchBrush(styles[i].hs, fore_color, back_color, &brush); + expect(Ok, status); + ok(brush != NULL, "Expected the brush to be initialized.\n"); + status = GdipFillRectangleI(graphics_hdc, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipFillRectangleI(graphics_image, (GpBrush *)brush, 0, 0, width, height); + expect(Ok, status); + status = GdipDeleteBrush((GpBrush *)brush); + expect(Ok, status); + brush = NULL; + + match_hdc = TRUE; + match_image = TRUE; + for(y = 0; y < width && (match_hdc || match_image); y++) + { + for(x = 0; x < height && (match_hdc || match_image); x++) + { + ARGB color; + int cindex = (styles[i].pattern[7-(y%8)] >> (2*(7-(x%8)))) & 3; + + color = COLORREF2ARGB(GetPixel(hdc, x, y)); + if (!color_match(color, exp_colors[cindex], 1)) + match_hdc = FALSE; + + GdipBitmapGetPixel(bitmap, x, y, &color); + if (!color_match(color, exp_colors[cindex], 1)) + match_image = FALSE; + } + } + todo_wine_if(styles[i].todo) + { + ok(match_hdc, "Unexpected pattern for hatch style %#x with hdc.\n", styles[i].hs); + ok(match_image, "Unexpected pattern for hatch style %#x with image.\n", styles[i].hs); + } + } + + status = GdipDeleteGraphics(graphics_image); + expect(Ok, status); + status = GdipDisposeImage((GpImage*)bitmap); + expect(Ok, status); + + status = GdipDeleteGraphics(graphics_hdc); + expect(Ok, status); + ReleaseDC(hwnd, hdc); +} + START_TEST(brush) { struct GdiplusStartupInput gdiplusStartupInput; @@ -1686,6 +1811,7 @@ START_TEST(brush) test_pathgradientpresetblend(); test_pathgradientblend(); test_getHatchStyle(); + test_hatchBrushStyles();
GdiplusShutdown(gdiplusToken); DestroyWindow(hwnd); diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c index b17323cb75..d32453fa38 100644 --- a/dlls/gdiplus/tests/image.c +++ b/dlls/gdiplus/tests/image.c @@ -45,7 +45,7 @@ static BOOL compare_uint(unsigned int x, unsigned int y, unsigned int max_diff) return diff <= max_diff; }
-static BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff) +BOOL color_match(ARGB c1, ARGB c2, BYTE max_diff) { if (!compare_uint(c1 & 0xff, c2 & 0xff, max_diff)) return FALSE; c1 >>= 8; c2 >>= 8;
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 36 ++++++++++++++++++------------------ dlls/gdiplus/tests/brush.c | 36 ++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index ff0b009559..aa1cda1990 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -198,33 +198,33 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) }
static const char HatchBrushes[][8] = { - { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HatchStyleHorizontal */ - { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleVertical */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleHorizontal */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleVertical */ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */ { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */ - { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HatchStyleCross */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleCross */ { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */ { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */ - { 0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88 }, /* HatchStyle10Percent */ - { 0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc }, /* HatchStyle20Percent */ - { 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc }, /* HatchStyle25Percent */ - { 0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc }, /* HatchStyle30Percent */ - { 0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc }, /* HatchStyle40Percent */ - { 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc }, /* HatchStyle50Percent */ - { 0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee }, /* HatchStyle60Percent */ - { 0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff }, /* HatchStyle70Percent */ - { 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff }, /* HatchStyle75Percent */ - { 0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff }, /* HatchStyle80Percent */ - { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff }, /* HatchStyle90Percent */ + { 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00, 0x80 }, /* HatchStyle10Percent */ + { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }, /* HatchStyle20Percent */ + { 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88 }, /* HatchStyle25Percent */ + { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa }, /* HatchStyle30Percent */ + { 0x15, 0xaa, 0x55, 0xaa, 0x51, 0xaa, 0x55, 0xaa }, /* HatchStyle40Percent */ + { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }, /* HatchStyle50Percent */ + { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }, /* HatchStyle60Percent */ + { 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77 }, /* HatchStyle70Percent */ + { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 }, /* HatchStyle75Percent */ + { 0xff, 0xfe, 0xff, 0xef, 0xff, 0xfe, 0xff, 0xef }, /* HatchStyle80Percent */ + { 0x7f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff }, /* HatchStyle90Percent */ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, /* HatchStyleLightDownwardDiagonal */ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, /* HatchStyleLightUpwardDiagonal */ { 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc }, /* HatchStyleDarkDownwardDiagonal */ - { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 }, /* HatchStyleDarkUpwardDiagonal */ - { 0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0 }, /* HatchStyleWideDownwardDiagonal */ - { 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1 }, /* HatchStyleWideUpwardDiagonal */ + { 0x99, 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33 }, /* HatchStyleDarkUpwardDiagonal */ + { 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xc1 }, /* HatchStyleWideDownwardDiagonal */ + { 0xc1, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83 }, /* HatchStyleWideUpwardDiagonal */ { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, /* HatchStyleLightVertical */ { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleLightHorizontal */ - { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, /* HatchStyleNarrowVertical */ + { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, /* HatchStyleNarrowVertical */ { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */ { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 3bf9277377..2ab27c9de4 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1654,33 +1654,33 @@ static void test_hatchBrushStyles(void) } styles[] = { - { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical, TRUE }, + { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, - { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross, TRUE }, + { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, - { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent, TRUE }, - { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent, TRUE }, - { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent, TRUE }, - { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent, TRUE }, - { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent, TRUE }, - { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent, TRUE }, - { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent, TRUE }, - { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent, TRUE }, - { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent, TRUE }, - { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent, TRUE }, - { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent, TRUE }, + { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, + { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, + { {0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0, 0x0c0c, 0xc0c0}, HatchStyle25Percent }, + { {0x0303, 0xcccc, 0x3030, 0xcccc, 0x0303, 0xcccc, 0x3030, 0xcccc}, HatchStyle30Percent }, + { {0x0333, 0xcccc, 0x3333, 0xcccc, 0x3303, 0xcccc, 0x3333, 0xcccc}, HatchStyle40Percent }, + { {0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStyle50Percent }, + { {0x3333, 0xcfcf, 0x3333, 0xfcfc, 0x3333, 0xcfcf, 0x3333, 0xfcfc}, HatchStyle60Percent }, + { {0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f, 0xf3f3, 0x3f3f}, HatchStyle70Percent }, + { {0xffff, 0xf3f3, 0xffff, 0x3f3f, 0xffff, 0xf3f3, 0xffff, 0x3f3f}, HatchStyle75Percent }, + { {0xffff, 0xfffc, 0xffff, 0xfcff, 0xffff, 0xfffc, 0xffff, 0xfcff}, HatchStyle80Percent }, + { {0x3fff, 0xffff, 0xffff, 0xffff, 0xff3f, 0xffff, 0xffff, 0xffff}, HatchStyle90Percent }, { {0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0303, 0x0c0c, 0x3030, 0xc0c0}, HatchStyleLightDownwardDiagonal }, { {0xc0c0, 0x3030, 0x0c0c, 0x0303, 0xc0c0, 0x3030, 0x0c0c, 0x0303}, HatchStyleLightUpwardDiagonal }, { {0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0, 0xc3c3, 0x0f0f, 0x3c3c, 0xf0f0}, HatchStyleDarkDownwardDiagonal }, - { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal, TRUE }, - { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal, TRUE }, - { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal, TRUE }, + { {0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f, 0xc3c3, 0xf0f0, 0x3c3c, 0x0f0f}, HatchStyleDarkUpwardDiagonal }, + { {0xc00f, 0x003f, 0x00fc, 0x03f0, 0x0fc0, 0x3f00, 0xfc00, 0xf003}, HatchStyleWideDownwardDiagonal }, + { {0xf003, 0xfc00, 0x3f00, 0x0fc0, 0x03f0, 0x00fc, 0x003f, 0xc00f}, HatchStyleWideUpwardDiagonal }, { {0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0, 0xc0c0}, HatchStyleLightVertical }, { {0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleLightHorizontal }, - { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical, TRUE }, + { {0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333}, HatchStyleNarrowVertical }, { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal },
Signed-off-by: Esme Povirk vincent@codeweavers.com
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 12 ++++--- dlls/gdiplus/gdiplus_private.h | 2 +- dlls/gdiplus/graphics.c | 65 +++++++++++++++++++++++++--------- dlls/gdiplus/tests/brush.c | 10 ++---- 4 files changed, 60 insertions(+), 29 deletions(-)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index aa1cda1990..4b6ccca12a 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -197,13 +197,15 @@ GpStatus WINGDIPAPI GdipCloneBrush(GpBrush *brush, GpBrush **clone) return Ok; }
-static const char HatchBrushes[][8] = { +/* The first 8 items per entry are bitmaps for each row of the hatch style. + * The 9th item of the entry is a flag indicating anti-aliasing. */ +static const unsigned char HatchBrushes[][9] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }, /* HatchStyleHorizontal */ { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleVertical */ - { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HatchStyleForwardDiagonal */ - { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleBackwardDiagonal */ + { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, TRUE }, /* HatchStyleForwardDiagonal */ + { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, TRUE }, /* HatchStyleBackwardDiagonal */ { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleCross */ - { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleDiagonalCross */ + { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, TRUE }, /* HatchStyleDiagonalCross */ { 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80 }, /* HatchStyle05Percent */ { 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00, 0x80 }, /* HatchStyle10Percent */ { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 }, /* HatchStyle20Percent */ @@ -230,7 +232,7 @@ static const char HatchBrushes[][8] = { { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ };
-GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) +GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) { if (hatchstyle < ARRAY_SIZE(HatchBrushes)) { diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 9e90a5d28c..d62860360a 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -123,7 +123,7 @@ extern GpStatus trace_path(GpGraphics *graphics, GpPath *path) DECLSPEC_HIDDEN; typedef struct region_element region_element; extern void delete_element(region_element *element) DECLSPEC_HIDDEN;
-extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const char **result) DECLSPEC_HIDDEN; +extern GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) DECLSPEC_HIDDEN;
static inline INT gdip_round(REAL x) { diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 72075e02fb..1ed29d1094 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -114,6 +114,19 @@ static COLORREF get_gdi_brush_color(const GpBrush *brush) return ARGB2COLORREF(argb); }
+static ARGB blend_colors(ARGB start, ARGB end, REAL position); + +static void init_hatch_palette(ARGB *hatch_palette, ARGB fore_color, ARGB back_color) +{ + /* Pass the center of a 45-degree diagonal line with width of one unit through the + * center of a unit square, and the portion of the square that will be covered will + * equal sqrt(2) - 1/2. The covered portion for adjacent squares will be 1/4. */ + hatch_palette[0] = back_color; + hatch_palette[1] = blend_colors(back_color, fore_color, 0.25); + hatch_palette[2] = blend_colors(back_color, fore_color, sqrt(2.0) - 0.5); + hatch_palette[3] = fore_color; +} + static HBITMAP create_hatch_bitmap(const GpHatch *hatch) { HBITMAP hbmp; @@ -132,18 +145,30 @@ static HBITMAP create_hatch_bitmap(const GpHatch *hatch) hbmp = CreateDIBSection(0, (BITMAPINFO *)&bmih, DIB_RGB_COLORS, (void **)&bits, NULL, 0); if (hbmp) { - const char *hatch_data; + const unsigned char *hatch_data;
if (get_hatch_data(hatch->hatchstyle, &hatch_data) == Ok) { + ARGB hatch_palette[4]; + init_hatch_palette(hatch_palette, hatch->forecol, hatch->backcol); + + /* Anti-aliasing is only specified for diagonal hatch patterns. + * This implementation repeats the pattern, shifts as needed, + * then uses bitmask 1 to check the pixel value, and the 0x82 + * bitmask to check the adjacent pixel values, to determine the + * degree of shading needed. */ for (y = 0; y < 8; y++) { - for (x = 0; x < 8; x++) + unsigned int row = 0x101 * hatch_data[y]; + + for (x = 0; x < 8; x++, row >>= 1) { - if (hatch_data[y] & (0x80 >> x)) - bits[y * 8 + x] = hatch->forecol; + int index; + if (hatch_data[8]) + index = (row & 1) ? 2 : (row & 0x82) ? 1 : 0; else - bits[y * 8 + x] = hatch->backcol; + index = (row & 1) ? 3 : 0; + bits[y * 8 + 7 - x] = hatch_palette[index]; } } } @@ -1160,25 +1185,33 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, { int x, y; GpHatch *fill = (GpHatch*)brush; - const char *hatch_data; + const unsigned char *hatch_data; + ARGB hatch_palette[4];
if (get_hatch_data(fill->hatchstyle, &hatch_data) != Ok) return NotImplemented;
- for (x=0; x<fill_area->Width; x++) - for (y=0; y<fill_area->Height; y++) - { - int hx, hy; + init_hatch_palette(hatch_palette, fill->forecol, fill->backcol);
- /* FIXME: Account for the rendering origin */ - hx = (x + fill_area->X) % 8; - hy = (y + fill_area->Y) % 8; + /* See create_hatch_bitmap for an explanation of how index is derived. */ + for (y = 0; y < fill_area->Height; y++, argb_pixels += cdwStride) + { + /* FIXME: Account for the rendering origin */ + const int hy = 7 - ((y + fill_area->Y) % 8); + const unsigned int row = 0x101 * hatch_data[hy];
- if ((hatch_data[7-hy] & (0x80 >> hx)) != 0) - argb_pixels[x + y*cdwStride] = fill->forecol; + for (x = 0; x < fill_area->Width; x++) + { + const unsigned int srow = row >> (7 - ((x + fill_area->X) % 8)); + int index; + if (hatch_data[8]) + index = (srow & 1) ? 2 : (srow & 0x82) ? 1 : 0; else - argb_pixels[x + y*cdwStride] = fill->backcol; + index = (srow & 1) ? 3 : 0; + + argb_pixels[x] = hatch_palette[index]; } + }
return Ok; } diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 2ab27c9de4..2f34712c71 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1650,16 +1650,15 @@ static void test_hatchBrushStyles(void) { short pattern[8]; GpHatchStyle hs; - BOOL todo; } styles[] = { { {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff}, HatchStyleHorizontal }, { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleVertical }, - { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal, TRUE }, - { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal, TRUE }, + { {0x4006, 0x0019, 0x0064, 0x0190, 0x0640, 0x1900, 0x6400, 0x9001}, HatchStyleForwardDiagonal }, + { {0x9001, 0x6400, 0x1900, 0x0640, 0x0190, 0x0064, 0x0019, 0x4006}, HatchStyleBackwardDiagonal }, { {0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleCross }, - { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross, TRUE }, + { {0x9006, 0x6419, 0x1964, 0x0690, 0x0690, 0x1964, 0x6419, 0x9006}, HatchStyleDiagonalCross }, { {0x0000, 0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x0000, 0xc000}, HatchStyle05Percent }, { {0x0000, 0x00c0, 0x0000, 0xc000, 0x0000, 0x00c0, 0x0000, 0xc000}, HatchStyle10Percent }, { {0x0000, 0x0c0c, 0x0000, 0xc0c0, 0x0000, 0x0c0c, 0x0000, 0xc0c0}, HatchStyle20Percent }, @@ -1742,11 +1741,8 @@ static void test_hatchBrushStyles(void) match_image = FALSE; } } - todo_wine_if(styles[i].todo) - { ok(match_hdc, "Unexpected pattern for hatch style %#x with hdc.\n", styles[i].hs); ok(match_image, "Unexpected pattern for hatch style %#x with image.\n", styles[i].hs); - } }
status = GdipDeleteGraphics(graphics_image);
Signed-off-by: Esme Povirk vincent@codeweavers.com
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/gdiplus/brush.c | 23 +++++++++++++++++++++++ dlls/gdiplus/tests/brush.c | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/dlls/gdiplus/brush.c b/dlls/gdiplus/brush.c index 4b6ccca12a..d0da60df3a 100644 --- a/dlls/gdiplus/brush.c +++ b/dlls/gdiplus/brush.c @@ -230,6 +230,29 @@ static const unsigned char HatchBrushes[][9] = { { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }, /* HatchStyleNarrowHorizontal */ { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc }, /* HatchStyleDarkVertical */ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff }, /* HatchStyleDarkHorizontal */ + { 0x00, 0x00, 0x11, 0x22, 0x44, 0x88, 0x00, 0x00 }, /* HatchStyleDashedDownwardDiagonal */ + { 0x00, 0x00, 0x88, 0x44, 0x22, 0x11, 0x00, 0x00 }, /* HatchStyleDashedUpwardDiagonal */ + { 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xf0 }, /* HatchStyleDashedHorizontal */ + { 0x08, 0x08, 0x08, 0x08, 0x80, 0x80, 0x80, 0x80 }, /* HatchStyleDashedVertical */ + { 0x04, 0x20, 0x01, 0x10, 0x02, 0x40, 0x08, 0x80 }, /* HatchStyleSmallConfetti */ + { 0x8d, 0x0c, 0xc0, 0xd8, 0x1b, 0x03, 0x30, 0xb1 }, /* HatchStyleLargeConfetti */ + { 0x18, 0x24, 0x42, 0x81, 0x18, 0x24, 0x42, 0x81 }, /* HatchStyleZigZag */ + { 0xc0, 0x25, 0x18, 0x00, 0xc0, 0x25, 0x18, 0x00 }, /* HatchStyleWave */ + { 0x81, 0x42, 0x24, 0x18, 0x08, 0x04, 0x02, 0x01 }, /* HatchStyleDiagonalBrick */ + { 0x08, 0x08, 0x08, 0xff, 0x80, 0x80, 0x80, 0xff }, /* HatchStyleHorizontalBrick */ + { 0x51, 0x22, 0x14, 0x88, 0x45, 0x22, 0x54, 0x88 }, /* HatchStyleWeave */ + { 0xf0, 0xf0, 0xf0, 0xf0, 0x55, 0xaa, 0x55, 0xaa }, /* HatchStylePlaid */ + { 0x80, 0x01, 0x80, 0x00, 0x10, 0x08, 0x10, 0x00 }, /* HatchStyleDivot */ + { 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xaa }, /* HatchStyleDottedGrid */ + { 0x00, 0x22, 0x00, 0x08, 0x00, 0x22, 0x00, 0x80 }, /* HatchStyleDottedDiamond */ + { 0x01, 0x01, 0x02, 0x0c, 0x30, 0x48, 0x84, 0x03 }, /* HatchStyleShingle */ + { 0x99, 0xff, 0x66, 0xff, 0x99, 0xff, 0x66, 0xff }, /* HatchStyleTrellis */ + { 0xf8, 0xf8, 0x98, 0x77, 0x8f, 0x8f, 0x89, 0x77 }, /* HatchStyleSphere */ + { 0x88, 0x88, 0x88, 0xff, 0x88, 0x88, 0x88, 0xff }, /* HatchStyleSmallGrid */ + { 0x99, 0x66, 0x66, 0x99, 0x99, 0x66, 0x66, 0x99 }, /* HatchStyleSmallCheckerBoard */ + { 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0 }, /* HatchStyleLargeCheckerBoard */ + { 0x01, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82 }, /* HatchStyleOutlinedDiamond */ + { 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10 }, /* HatchStyleSolidDiamond */ };
GpStatus get_hatch_data(GpHatchStyle hatchstyle, const unsigned char **result) diff --git a/dlls/gdiplus/tests/brush.c b/dlls/gdiplus/tests/brush.c index 2f34712c71..fca1c17057 100644 --- a/dlls/gdiplus/tests/brush.c +++ b/dlls/gdiplus/tests/brush.c @@ -1683,6 +1683,29 @@ static void test_hatchBrushStyles(void) { {0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}, HatchStyleNarrowHorizontal }, { {0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0}, HatchStyleDarkVertical }, { {0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}, HatchStyleDarkHorizontal }, + { {0x0000, 0x0000, 0x0303, 0x0c0c, 0x3030, 0xc0c0, 0x0000, 0x0000}, HatchStyleDashedDownwardDiagonal }, + { {0x0000, 0x0000, 0xc0c0, 0x3030, 0x0c0c, 0x0303, 0x0000, 0x0000}, HatchStyleDashedUpwardDiagonal }, + { {0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, 0x0000, 0x0000, 0xff00}, HatchStyleDashedHorizontal }, + { {0x00c0, 0x00c0, 0x00c0, 0x00c0, 0xc000, 0xc000, 0xc000, 0xc000}, HatchStyleDashedVertical }, + { {0x0030, 0x0c00, 0x0003, 0x0300, 0x000c, 0x3000, 0x00c0, 0xc000}, HatchStyleSmallConfetti }, + { {0xc0f3, 0x00f0, 0xf000, 0xf3c0, 0x03cf, 0x000f, 0x0f00, 0xcf03}, HatchStyleLargeConfetti }, + { {0x03c0, 0x0c30, 0x300c, 0xc003, 0x03c0, 0x0c30, 0x300c, 0xc003}, HatchStyleZigZag }, + { {0xf000, 0x0c33, 0x03c0, 0x0000, 0xf000, 0x0c33, 0x03c0, 0x0000}, HatchStyleWave }, + { {0xc003, 0x300c, 0x0c30, 0x03c0, 0x00c0, 0x0030, 0x000c, 0x0003}, HatchStyleDiagonalBrick }, + { {0x00c0, 0x00c0, 0x00c0, 0xffff, 0xc000, 0xc000, 0xc000, 0xffff}, HatchStyleHorizontalBrick }, + { {0x3303, 0x0c0c, 0x0330, 0xc0c0, 0x3033, 0x0c0c, 0x3330, 0xc0c0}, HatchStyleWeave }, + { {0xff00, 0xff00, 0xff00, 0xff00, 0x3333, 0xcccc, 0x3333, 0xcccc}, HatchStylePlaid }, + { {0xc000, 0x0003, 0xc000, 0x0000, 0x0300, 0x00c0, 0x0300, 0x0000}, HatchStyleDivot }, + { {0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xcccc}, HatchStyleDottedGrid }, + { {0x0000, 0x0c0c, 0x0000, 0x00c0, 0x0000, 0x0c0c, 0x0000, 0xc000}, HatchStyleDottedDiamond }, + { {0x0003, 0x0003, 0x000c, 0x00f0, 0x0f00, 0x30c0, 0xc030, 0x000f}, HatchStyleShingle }, + { {0xc3c3, 0xffff, 0x3c3c, 0xffff, 0xc3c3, 0xffff, 0x3c3c, 0xffff}, HatchStyleTrellis }, + { {0xffc0, 0xffc0, 0xc3c0, 0x3f3f, 0xc0ff, 0xc0ff, 0xc0c3, 0x3f3f}, HatchStyleSphere }, + { {0xc0c0, 0xc0c0, 0xc0c0, 0xffff, 0xc0c0, 0xc0c0, 0xc0c0, 0xffff}, HatchStyleSmallGrid }, + { {0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3, 0xc3c3, 0x3c3c, 0x3c3c, 0xc3c3}, HatchStyleSmallCheckerBoard }, + { {0x00ff, 0x00ff, 0x00ff, 0x00ff, 0xff00, 0xff00, 0xff00, 0xff00}, HatchStyleLargeCheckerBoard }, + { {0x0003, 0xc00c, 0x3030, 0x0cc0, 0x0300, 0x0cc0, 0x3030, 0xc00c}, HatchStyleOutlinedDiamond }, + { {0x0000, 0x0300, 0x0fc0, 0x3ff0, 0xfffc, 0x3ff0, 0x0fc0, 0x0300}, HatchStyleSolidDiamond }, }; static const ARGB exp_colors[] = { 0xffffffff, 0xffbfbfbf, 0xff151515, 0xff000000 }; static const ARGB fore_color = 0xff000000;
Signed-off-by: Esme Povirk vincent@codeweavers.com
This is with the caveat that I'm unsure if duplicating the patterns exactly is acceptable from a copyright perpective.
Signed-off-by: Esme Povirk vincent@codeweavers.com