| File: System\Windows\Forms\Rendering\DCMapping.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; namespace System.Windows.Forms; /// <summary> /// DCMapping is used to change the mapping and clip region of the specified device context to the given /// bounds. When the DCMapping is disposed, the original mapping and clip rectangle are restored. /// /// Example: /// /// using(DCMapping mapping = new DCMapping(hDC, new Rectangle(10,10, 50, 50) /// { /// // inside here the hDC's mapping of (0,0) is inset by (10,10) and /// // all painting is clipped at (0,0) - (50,50) /// } /// /// PERF: DCMapping is a structure so that it will allocate on the stack rather than in GC managed memory. This /// way disposing the object does not force a GC. Since DCMapping objects aren't likely to be passed between /// functions rather used and disposed in the same one, this reduces overhead. /// </summary> internal readonly struct DCMapping : IDisposable { private readonly HDC _hdc; private readonly int _savedState; public unsafe DCMapping(HDC hdc, Rectangle bounds) { if (hdc.IsNull) { throw new ArgumentNullException(nameof(hdc)); } _hdc = hdc; _savedState = PInvokeCore.SaveDC(hdc); // Retrieve the x-coordinates and y-coordinates of the viewport origin for the specified device context. Point viewportOrg = default; bool success = PInvokeCore.GetViewportOrgEx(hdc, &viewportOrg); Debug.Assert(success, "GetViewportOrgEx() failed."); // Create a new rectangular clipping region based off of the bounds specified, // shifted over by the x & y specified in the viewport origin. RegionScope clippingRegion = new( viewportOrg.X + bounds.Left, viewportOrg.Y + bounds.Top, viewportOrg.X + bounds.Right, viewportOrg.Y + bounds.Bottom); Debug.Assert(!clippingRegion.IsNull, "CreateRectRgn() failed."); try { RegionScope originalRegion = new(hdc); // Shift the viewpoint origin by coordinates specified in "bounds". success = PInvoke.SetViewportOrgEx( hdc, viewportOrg.X + bounds.Left, viewportOrg.Y + bounds.Top, lppt: null); Debug.Assert(success, "SetViewportOrgEx() failed."); GDI_REGION_TYPE originalRegionType; if (!originalRegion.IsNull) { // Get the original clipping region so we can determine its type (we'll check later if we've restored the region back properly.) RECT originalClipRect = default; originalRegionType = PInvoke.GetRgnBox(originalRegion, &originalClipRect); Debug.Assert( originalRegionType != GDI_REGION_TYPE.RGN_ERROR, "ERROR returned from SelectClipRgn while selecting the original clipping region.."); if (originalRegionType == GDI_REGION_TYPE.SIMPLEREGION) { // Find the intersection of our clipping region and the current clipping region (our parent's) GDI_REGION_TYPE combineResult = PInvokeCore.CombineRgn( clippingRegion, clippingRegion, originalRegion, RGN_COMBINE_MODE.RGN_AND); Debug.Assert( combineResult is GDI_REGION_TYPE.SIMPLEREGION or GDI_REGION_TYPE.NULLREGION, "SIMPLEREGION or NULLREGION expected."); } } else { // If there was no clipping region, then the result is a simple region. originalRegionType = GDI_REGION_TYPE.SIMPLEREGION; } // Select the new clipping region; make sure it's a SIMPLEREGION or NULLREGION GDI_REGION_TYPE selectResult = PInvokeCore.SelectClipRgn(hdc, clippingRegion); Debug.Assert( selectResult is GDI_REGION_TYPE.SIMPLEREGION or GDI_REGION_TYPE.NULLREGION, "SIMPLEREGION or NULLLREGION expected."); } catch (Exception ex) when (!ex.IsCriticalException()) { } } public void Dispose() { if (!_hdc.IsNull) { PInvokeCore.RestoreDC(_hdc, _savedState); } } }