using System; using Avalonia.Platform; using Avalonia.Win32.Interop; using PixelFormat = Avalonia.Platform.PixelFormat; namespace Avalonia.Win32 { public class WindowFramebuffer : ILockedFramebuffer { private readonly IntPtr _handle; private IUnmanagedBlob _bitmapBlob; private UnmanagedMethods.BITMAPINFOHEADER _bmpInfo; public WindowFramebuffer(IntPtr handle, PixelSize size) { if (size.Width <= 0) throw new ArgumentException("Width is less than zero"); if (size.Height <= 0) throw new ArgumentException("Height is less than zero"); _handle = handle; _bmpInfo.Init(); _bmpInfo.biPlanes = 1; _bmpInfo.biBitCount = 32; _bmpInfo.Init(); _bmpInfo.biWidth = size.Width; _bmpInfo.biHeight = -size.Height; _bitmapBlob = AvaloniaLocator.Current.GetService().AllocBlob(size.Width * size.Height * 4); } ~WindowFramebuffer() { Deallocate(); } public IntPtr Address => _bitmapBlob.Address; public int RowBytes => Size.Width * 4; public PixelFormat Format => PixelFormat.Bgra8888; public Vector Dpi { get { if (UnmanagedMethods.ShCoreAvailable) { uint dpix, dpiy; var monitor = UnmanagedMethods.MonitorFromWindow(_handle, UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST); if (UnmanagedMethods.GetDpiForMonitor( monitor, UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out dpix, out dpiy) == 0) { return new Vector(dpix, dpiy); } } return new Vector(96, 96); } } public PixelSize Size => new PixelSize(_bmpInfo.biWidth, -_bmpInfo.biHeight); public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, int height = -1) { if (width == -1) width = Size.Width; if (height == -1) height = Size.Height; UnmanagedMethods.SetDIBitsToDevice(hDC, destX, destY, (uint) width, (uint) height, srcX, srcY, 0, (uint)Size.Height, _bitmapBlob.Address, ref _bmpInfo, 0); } public bool DrawToWindow(IntPtr hWnd, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, int height = -1) { if (_bitmapBlob.IsDisposed) throw new ObjectDisposedException("Framebuffer"); if (hWnd == IntPtr.Zero) return false; IntPtr hDC = UnmanagedMethods.GetDC(hWnd); if (hDC == IntPtr.Zero) return false; DrawToDevice(hDC, destX, destY, srcX, srcY, width, height); UnmanagedMethods.ReleaseDC(hWnd, hDC); return true; } public void Dispose() { //It's not an *actual* dispose. This call means "We are done drawing" DrawToWindow(_handle); } public void Deallocate() => _bitmapBlob.Dispose(); } }