BizHawk/BizHawk.Client.EmuHawk/DisplayManager/DisplaySurface.cs

154 lines
3.1 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk
{
/// <summary>
/// This is a wrapper for a Bitmap, basically, which can also be a int[].
/// It should be phased out, in favor of BitmapBuffer and Texture2d's
/// </summary>
public unsafe class DisplaySurface : IDisposable
{
private Bitmap _bmp;
private BitmapData _bmpData;
private readonly int[] _pixels;
public unsafe void Clear()
{
FromBitmap(false);
Util.Memset(PixelPtr, 0, Stride * Height);
}
public Bitmap PeekBitmap()
{
ToBitmap();
return _bmp;
}
/// <summary>
/// returns a Graphics object used to render to this surface. be sure to dispose it!
/// </summary>
public Graphics GetGraphics()
{
ToBitmap();
return Graphics.FromImage(_bmp);
}
public unsafe void ToBitmap(bool copy=true)
{
if (_isBitmap) return;
_isBitmap = true;
if (_bmp == null)
{
_bmp = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
}
if (copy)
{
_bmpData = _bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int w = Width;
int h = Height;
int stride = _bmpData.Stride / 4;
int* bmpBuf = (int*)_bmpData.Scan0.ToPointer();
for (int y = 0, i = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
bmpBuf[y * stride + x] = _pixels[i++];
}
}
_bmp.UnlockBits(_bmpData);
}
}
private bool _isBitmap;
public unsafe void FromBitmap(bool copy = true)
{
if (!_isBitmap)
{
return;
}
_isBitmap = false;
if (copy)
{
_bmpData = _bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int w = Width;
int h = Height;
int stride = _bmpData.Stride / 4;
int* bmpBuf = (int*)_bmpData.Scan0.ToPointer();
for (int y = 0, i = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
_pixels[i++] = bmpBuf[y * stride + x];
}
}
_bmp.UnlockBits(_bmpData);
}
}
public DisplaySurface(int width, int height)
{
// can't create a bitmap with zero dimensions, so for now, just bump it up to one
if (width == 0)
{
width = 1;
}
if (height == 0)
{
height = 1;
}
Width = width;
Height = height;
_pixels = new int[width * height];
LockPixels();
}
public int* PixelPtr => (int*)_ptr;
public int Stride => Width * 4;
private void* _ptr;
private GCHandle _handle;
private void LockPixels()
{
UnlockPixels();
_handle = GCHandle.Alloc(_pixels, GCHandleType.Pinned);
_ptr = _handle.AddrOfPinnedObject().ToPointer();
}
private void UnlockPixels()
{
if (_handle.IsAllocated)
{
_handle.Free();
}
}
public int Width { get; }
public int Height { get; }
public void Dispose()
{
_bmp?.Dispose();
_bmp = null;
UnlockPixels();
}
}
}