|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices;
namespace System.Drawing;
/// <summary>
/// The BufferedGraphics class can be thought of as a "Token" or "Reference" to the buffer that a
/// BufferedGraphicsContext creates. While a BufferedGraphics is outstanding, the memory associated with the
/// buffer is locked. The general design is such that under normal conditions a single BufferedGraphics will be in
/// use at one time for a given BufferedGraphicsContext.
/// </summary>
public sealed class BufferedGraphics : IDisposable
{
private readonly Graphics? _targetGraphics;
private readonly IntPtr _targetDC;
private Graphics _bufferedGraphicsSurface;
private BufferedGraphicsContext _context;
private readonly Point _targetLoc;
private readonly Size _virtualSize;
/// <summary>
/// Internal constructor, this class is created by BufferedGraphicsContext.
/// </summary>
internal BufferedGraphics(Graphics bufferedGraphicsSurface, BufferedGraphicsContext context, Graphics? targetGraphics,
IntPtr targetDC, Point targetLoc, Size virtualSize)
{
_context = context;
_bufferedGraphicsSurface = bufferedGraphicsSurface;
_targetDC = targetDC;
_targetGraphics = targetGraphics;
_targetLoc = targetLoc;
_virtualSize = virtualSize;
}
public void Dispose()
{
if (_context is not null)
{
_context.ReleaseBuffer();
if (DisposeContext)
{
_context.Dispose();
_context = null!;
}
}
if (_bufferedGraphicsSurface is not null)
{
_bufferedGraphicsSurface.Dispose();
_bufferedGraphicsSurface = null!;
}
}
/// <summary>
/// Allows access to the Graphics wrapper for the buffer.
/// </summary>
public Graphics Graphics => _bufferedGraphicsSurface;
/// <summary>
/// Renders the buffer to the specified target graphics.
/// </summary>
public void Render(Graphics? target)
{
if (target is not null)
{
IntPtr targetDC = target.GetHdc();
try
{
RenderInternal(new HandleRef(target, targetDC));
}
finally
{
target.ReleaseHdcInternal(targetDC);
}
}
}
/// <summary>
/// Internal method that renders the specified buffer into the target.
/// </summary>
private void RenderInternal(HandleRef refTargetDC)
{
IntPtr sourceDC = Graphics.GetHdc();
try
{
PInvokeCore.BitBlt(
(HDC)refTargetDC.Handle,
_targetLoc.X,
_targetLoc.Y,
_virtualSize.Width,
_virtualSize.Height,
(HDC)sourceDC,
0,
0,
ROP_CODE.SRCCOPY);
GC.KeepAlive(refTargetDC.Wrapper);
}
finally
{
Graphics.ReleaseHdcInternal(sourceDC);
}
}
/// <summary>
/// Determines if we need to dispose of the Context when this is disposed.
/// </summary>
internal bool DisposeContext { get; set; }
/// <summary>
/// Renders the buffer to the original graphics used to allocate the buffer.
/// </summary>
public void Render()
{
if (_targetGraphics is not null)
{
Render(_targetGraphics);
}
else
{
RenderInternal(new HandleRef(Graphics, _targetDC));
}
}
/// <summary>
/// Renders the buffer to the specified target HDC.
/// </summary>
public void Render(IntPtr targetDC) => RenderInternal(new HandleRef(null, targetDC));
}
|