using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using BizHawk.Common; namespace BizHawk.Client.EmuHawk { /// /// 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 /// 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; } /// /// returns a Graphics object used to render to this surface. be sure to dispose it! /// 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(); } } }