[PATCH 0/5] MR5593: gdiplus: Start bracketing Graphics HDC uses.
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. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
From: Esme Povirk <esme(a)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); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
From: Esme Povirk <esme(a)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) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
From: Esme Povirk <esme(a)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; } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
From: Esme Povirk <esme(a)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; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
From: Esme Povirk <esme(a)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. -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5593
participants (2)
-
Esme Povirk -
Esme Povirk (@madewokherd)