This is the first part of a longer series of commits that can be found here: https://gitlab.winehq.org/madewokherd/wine/-/tree/getdc-wip?ref_type=heads
The eventual goal is to no longer store gdi32 handles, which are a limited resource, on gdiplus objects, except when absolutely necessary. In particular, Graphics objects created from an HWND do not need to store an HDC (GetDC/ReleaseDC theoretically caches those and works just fine), and Bitmap objects with a gdi32-compatible format shouldn't store an HBITMAP or HDC. These changes revealed some other bugs in bitmap and linecap code, which I've also fixed in order to keep the tests working.
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/gdiplus_private.h | 8 ++++++ dlls/gdiplus/graphics.c | 48 +++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h index 0b302a405ea..c3b516a9a2b 100644 --- a/dlls/gdiplus/gdiplus_private.h +++ b/dlls/gdiplus/gdiplus_private.h @@ -62,6 +62,8 @@ extern REAL units_scale(GpUnit from, GpUnit to, REAL dpi, BOOL printer_display);
#define WineCoordinateSpaceGdiDevice ((GpCoordinateSpace)4)
+extern GpStatus gdi_dc_acquire(GpGraphics *graphics, HDC *hdc); +extern void gdi_dc_release(GpGraphics *graphics, HDC hdc); extern GpStatus gdi_transform_acquire(GpGraphics *graphics); extern GpStatus gdi_transform_release(GpGraphics *graphics); extern GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space, @@ -248,6 +250,7 @@ struct GpPen{ struct GpGraphics{ HDC hdc; HWND hwnd; + INT hdc_refs; BOOL owndc; BOOL alpha_hdc; BOOL printer_display; @@ -658,6 +661,11 @@ static inline void image_unlock(GpImage *image) ReleaseSRWLockExclusive(&image->lock); }
+static inline BOOL has_gdi_dc(GpGraphics *graphics) +{ + return graphics->hdc != NULL; +} + static inline void set_rect(GpRectF *rect, REAL x, REAL y, REAL width, REAL height) { rect->X = x; diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 0727b63ed4f..cf54334ae2e 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -19,6 +19,7 @@ #include <stdarg.h> #include <math.h> #include <limits.h> +#include <assert.h>
#include "windef.h" #include "winbase.h" @@ -46,6 +47,25 @@ WINE_DEFAULT_DEBUG_CHANNEL(gdiplus); #define ANCHOR_WIDTH (2.0) #define MAX_ITERS (50)
+GpStatus gdi_dc_acquire(GpGraphics *graphics, HDC *hdc) +{ + if (graphics->hdc != NULL) + { + *hdc = graphics->hdc; + graphics->hdc_refs++; + return Ok; + } + + *hdc = NULL; + return InvalidParameter; +} + +void gdi_dc_release(GpGraphics *graphics, HDC hdc) +{ + assert(graphics->hdc_refs > 0); + graphics->hdc_refs--; +} + static GpStatus draw_driver_string(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length, GDIPCONST GpFont *font, GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush, GDIPCONST PointF *positions, @@ -252,7 +272,7 @@ static HBRUSH create_gdi_brush(const GpBrush *brush, INT origin_x, INT origin_y) return gdibrush; }
-static INT prepare_dc(GpGraphics *graphics, GpPen *pen) +static INT prepare_dc(GpGraphics *graphics, HDC hdc, GpPen *pen) { LOGBRUSH lb; HPEN gdipen; @@ -261,9 +281,9 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen) GpPointF pt[2]; DWORD dash_array[MAX_DASHLEN];
- save_state = SaveDC(graphics->hdc); + save_state = SaveDC(hdc);
- EndPath(graphics->hdc); + EndPath(hdc);
if(pen->unit == UnitPixel){ width = pen->width; @@ -311,15 +331,15 @@ static INT prepare_dc(GpGraphics *graphics, GpPen *pen) free_gdi_logbrush(&lb); }
- SelectObject(graphics->hdc, gdipen); + SelectObject(hdc, gdipen);
return save_state; }
-static void restore_dc(GpGraphics *graphics, INT state) +static void restore_dc(GpGraphics *graphics, HDC hdc, INT state) { - DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN))); - RestoreDC(graphics->hdc, state); + DeleteObject(SelectObject(hdc, GetStockObject(NULL_PEN))); + RestoreDC(hdc, state); }
static void round_points(POINT *pti, GpPointF *ptf, INT count) @@ -2533,6 +2553,8 @@ GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics) if(!graphics) return InvalidParameter; if(graphics->busy) return ObjectBusy;
+ assert(graphics->hdc_refs == 0); + if (is_metafile_graphics(graphics)) { stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image); @@ -3633,11 +3655,16 @@ GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path) { + HDC hdc; INT save_state; GpStatus retval; HRGN hrgn=NULL;
- save_state = prepare_dc(graphics, pen); + retval = gdi_dc_acquire(graphics, &hdc); + if (retval != Ok) + return retval; + + save_state = prepare_dc(graphics, hdc, pen);
retval = get_clip_hrgn(graphics, &hrgn);
@@ -3654,8 +3681,9 @@ static GpStatus GDI32_GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *pat gdi_transform_release(graphics);
end: - restore_dc(graphics, save_state); + restore_dc(graphics, hdc, save_state); DeleteObject(hrgn); + gdi_dc_release(graphics, hdc);
return retval; } @@ -4069,7 +4097,7 @@ GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
if (is_metafile_graphics(graphics)) retval = METAFILE_DrawPath((GpMetafile*)graphics->image, pen, path); - else if (!graphics->hdc || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE)) + else if (!has_gdi_dc(graphics) || graphics->alpha_hdc || !brush_can_fill_path(pen->brush, FALSE)) retval = SOFTWARE_GdipDrawPath(graphics, pen, path); else retval = GDI32_GdipDrawPath(graphics, pen, path);
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/graphics.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index cf54334ae2e..3c00eddddf4 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -362,9 +362,14 @@ static void round_points(POINT *pti, GpPointF *ptf, INT count) static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_width, INT dst_height, HDC hdc, INT src_x, INT src_y, INT src_width, INT src_height) { + HDC dst_hdc; CompositingMode comp_mode; - INT technology = GetDeviceCaps(graphics->hdc, TECHNOLOGY); - INT shadeblendcaps = GetDeviceCaps(graphics->hdc, SHADEBLENDCAPS); + INT technology, shadeblendcaps; + + gdi_dc_acquire(graphics, &dst_hdc); + + technology = GetDeviceCaps(dst_hdc, TECHNOLOGY); + shadeblendcaps = GetDeviceCaps(dst_hdc, SHADEBLENDCAPS);
GdipGetCompositingMode(graphics, &comp_mode);
@@ -373,7 +378,7 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_ { TRACE("alpha blending not supported by device, fallback to StretchBlt\n");
- StretchBlt(graphics->hdc, dst_x, dst_y, dst_width, dst_height, + StretchBlt(dst_hdc, dst_x, dst_y, dst_width, dst_height, hdc, src_x, src_y, src_width, src_height, SRCCOPY); } else @@ -385,9 +390,11 @@ static void gdi_alpha_blend(GpGraphics *graphics, INT dst_x, INT dst_y, INT dst_ bf.SourceConstantAlpha = 255; bf.AlphaFormat = AC_SRC_ALPHA;
- GdiAlphaBlend(graphics->hdc, dst_x, dst_y, dst_width, dst_height, + GdiAlphaBlend(dst_hdc, dst_x, dst_y, dst_width, dst_height, hdc, src_x, src_y, src_width, src_height, bf); } + + gdi_dc_release(graphics, dst_hdc); }
static GpStatus get_clip_hrgn(GpGraphics *graphics, HRGN *hrgn)
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/graphics.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 3c00eddddf4..78e3b07222d 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -591,28 +591,39 @@ static GpStatus alpha_blend_pixels_hrgn(GpGraphics *graphics, INT dst_x, INT dst } else { + HDC hdc; HRGN hrgn; int save;
+ stat = gdi_dc_acquire(graphics, &hdc); + + if (stat != Ok) + return stat; + stat = get_clip_hrgn(graphics, &hrgn);
if (stat != Ok) + { + gdi_dc_release(graphics, hdc); return stat; + }
- save = SaveDC(graphics->hdc); + save = SaveDC(hdc);
- ExtSelectClipRgn(graphics->hdc, hrgn, RGN_COPY); + ExtSelectClipRgn(hdc, hrgn, RGN_COPY);
if (hregion) - ExtSelectClipRgn(graphics->hdc, hregion, RGN_AND); + ExtSelectClipRgn(hdc, hregion, RGN_AND);
stat = alpha_blend_hdc_pixels(graphics, dst_x, dst_y, src, src_width, src_height, src_stride, fmt);
- RestoreDC(graphics->hdc, save); + RestoreDC(hdc, save);
DeleteObject(hrgn);
+ gdi_dc_release(graphics, hdc); + return stat; } }
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/graphics.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 78e3b07222d..48d425341f8 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -1150,7 +1150,13 @@ static BOOL brush_can_fill_path(GpBrush *brush, BOOL is_fill)
static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush) { - GpStatus status = Ok; + HDC hdc; + GpStatus status; + + status = gdi_dc_acquire(graphics, &hdc); + if (status != Ok) + return status; + switch (brush->bt) { case BrushTypeSolidColor: @@ -1163,27 +1169,27 @@ static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush) RECT rc; /* partially transparent fill */
- if (!SelectClipPath(graphics->hdc, RGN_AND)) + if (!SelectClipPath(hdc, RGN_AND)) { status = GenericError; DeleteObject(bmp); break; } - if (GetClipBox(graphics->hdc, &rc) != NULLREGION) + if (GetClipBox(hdc, &rc) != NULLREGION) { - HDC hdc = CreateCompatibleDC(NULL); + HDC src_hdc = CreateCompatibleDC(NULL);
- if (!hdc) + if (!src_hdc) { status = OutOfMemory; DeleteObject(bmp); break; }
- SelectObject(hdc, bmp); + SelectObject(src_hdc, bmp); gdi_alpha_blend(graphics, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, - hdc, 0, 0, 1, 1); - DeleteDC(hdc); + src_hdc, 0, 0, 1, 1); + DeleteDC(src_hdc); }
DeleteObject(bmp); @@ -1202,14 +1208,16 @@ static GpStatus brush_fill_path(GpGraphics *graphics, GpBrush *brush) break; }
- old_brush = SelectObject(graphics->hdc, gdibrush); - FillPath(graphics->hdc); - SelectObject(graphics->hdc, old_brush); + old_brush = SelectObject(hdc, gdibrush); + FillPath(hdc); + SelectObject(hdc, old_brush); DeleteObject(gdibrush); break; } }
+ gdi_dc_release(graphics, hdc); + return status; }
From: Esme Povirk esme@codeweavers.com
--- dlls/gdiplus/graphics.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c index 48d425341f8..bd77758eb51 100644 --- a/dlls/gdiplus/graphics.c +++ b/dlls/gdiplus/graphics.c @@ -1667,6 +1667,7 @@ static GpStatus brush_fill_pixels(GpGraphics *graphics, GpBrush *brush, static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size, const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2) { + HDC hdc; HGDIOBJ oldbrush = NULL, oldpen = NULL; GpMatrix matrix; HBRUSH brush = NULL; @@ -1682,6 +1683,8 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s if((x1 == x2) && (y1 == y2)) return;
+ gdi_dc_acquire(graphics, &hdc); + theta = gdiplus_atan2(y2 - y1, x2 - x1);
customstroke = (cap == LineCapCustom) && custom && (!custom->fill); @@ -1693,8 +1696,8 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT | PS_JOIN_MITER, 1, &lb, 0, NULL); - oldbrush = SelectObject(graphics->hdc, brush); - oldpen = SelectObject(graphics->hdc, pen); + oldbrush = SelectObject(hdc, brush); + oldpen = SelectObject(hdc, pen); }
switch(cap){ @@ -1729,7 +1732,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
round_points(pt, ptf, 4);
- Polygon(graphics->hdc, pt, 4); + Polygon(hdc, pt, 4);
break; case LineCapArrowAnchor: @@ -1754,7 +1757,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
round_points(pt, ptf, 3);
- Polygon(graphics->hdc, pt, 3); + Polygon(hdc, pt, 3);
break; case LineCapRoundAnchor: @@ -1769,7 +1772,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
round_points(pt, ptf, 2);
- Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y); + Ellipse(hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
break; case LineCapTriangle: @@ -1792,7 +1795,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
round_points(pt, ptf, 3);
- Polygon(graphics->hdc, pt, 3); + Polygon(hdc, pt, 3);
break; case LineCapRound: @@ -1815,7 +1818,7 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s
round_points(pt, ptf, 4);
- Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x, + Pie(hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
break; @@ -1855,13 +1858,13 @@ static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL s tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
if(custom->fill){ - BeginPath(graphics->hdc); - PolyDraw(graphics->hdc, custpt, tp, count); - EndPath(graphics->hdc); - StrokeAndFillPath(graphics->hdc); + BeginPath(hdc); + PolyDraw(hdc, custpt, tp, count); + EndPath(hdc); + StrokeAndFillPath(hdc); } else - PolyDraw(graphics->hdc, custpt, tp, count); + PolyDraw(hdc, custpt, tp, count);
custend: free(custptf); @@ -1873,11 +1876,13 @@ custend: }
if(!customstroke){ - SelectObject(graphics->hdc, oldbrush); - SelectObject(graphics->hdc, oldpen); + SelectObject(hdc, oldbrush); + SelectObject(hdc, oldpen); DeleteObject(brush); DeleteObject(pen); } + + gdi_dc_release(graphics, hdc); }
/* Shortens the line by the given percent by changing x2, y2.