MemoryBitmap.cs

Direct Access Pixel Data, GetPixel, SetPixel

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
public unsafe class MemoryBitmap : IDisposable
{
    private readonly BitmapData _bmpData;
    private readonly int        _depth;
    private readonly byte*      _pfp;
    private readonly int        _stride;
    private          Bitmap     _memoryBitmap;
    public MemoryBitmap(string path)
    {
        if (path.IsNullOrEmpty())
            throw new Exception("Path cannot be null or empty.");
        if (!File.Exists(path))
            throw new Exception($"Path {path} does not exist.");
        var bitmap = new Bitmap(path);
        _memoryBitmap = bitmap;
        if (_memoryBitmap.PixelFormat != PixelFormat.Format32bppArgb && _memoryBitmap.PixelFormat != PixelFormat.Format24bppRgb && _memoryBitmap.PixelFormat != PixelFormat.Format8bppIndexed)
        {
            var clone = new Bitmap(_memoryBitmap.Width, _memoryBitmap.Height, PixelFormat.Format32bppArgb);
            using (var gr = Graphics.FromImage(clone))
            {
                gr.DrawImage(_memoryBitmap, new Rectangle(0, 0, clone.Width, clone.Height));
                _memoryBitmap = clone;
            }
        }
        Width  = _memoryBitmap.Width;
        Height = _memoryBitmap.Height;
        var rect = new Rectangle(0, 0, _memoryBitmap.Width, _memoryBitmap.Height);
        try
        {
            _bmpData = _memoryBitmap.LockBits(rect, ImageLockMode.ReadWrite, _memoryBitmap.PixelFormat);
        }
        catch (Exception ex)
        {
            throw new Exception("Could not lock bitmap", ex.InnerException);
        }
        _depth  = Image.GetPixelFormatSize(_bmpData.PixelFormat) / 8;
        _pfp    = (byte*) _bmpData.Scan0.ToPointer();
        _stride = _bmpData.Stride;
    }
    public MemoryBitmap(Bitmap bitmap)
    {
        _memoryBitmap = bitmap ?? throw new Exception("Bitmap cannot be null");
        if (_memoryBitmap.PixelFormat != PixelFormat.Format32bppArgb && _memoryBitmap.PixelFormat != PixelFormat.Format24bppRgb && _memoryBitmap.PixelFormat != PixelFormat.Format8bppIndexed)
        {
            var clone = new Bitmap(_memoryBitmap.Width, _memoryBitmap.Height, PixelFormat.Format32bppArgb);
            using (var gr = Graphics.FromImage(clone))
            {
                gr.DrawImage(_memoryBitmap, new Rectangle(0, 0, clone.Width, clone.Height));
                _memoryBitmap = clone;
            }
        }
        Width  = _memoryBitmap.Width;
        Height = _memoryBitmap.Height;
        var rect = new Rectangle(0, 0, _memoryBitmap.Width, _memoryBitmap.Height);
        try
        {
            _bmpData = _memoryBitmap.LockBits(rect, ImageLockMode.ReadWrite, _memoryBitmap.PixelFormat);
        }
        catch (Exception ex)
        {
            throw new Exception("Could not lock bitmap", ex.InnerException);
        }
        _depth  = Image.GetPixelFormatSize(_bmpData.PixelFormat) / 8;
        _pfp    = (byte*) _bmpData.Scan0.ToPointer();
        _stride = _bmpData.Stride;
    }
    public int Width
    {
        get;
    }
    public int Height
    {
        get;
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    public void ConvertFormat(PixelFormat newPf)
    {
        var clone = new Bitmap(_memoryBitmap.Width, _memoryBitmap.Height, newPf);
        using (var gr = Graphics.FromImage(clone))
        {
            gr.DrawImage(_memoryBitmap, new Rectangle(0, 0, clone.Width, clone.Height));
            _memoryBitmap = clone;
        }
    }
    public void Save(string path)
    {
        _memoryBitmap.Save(path);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!disposing)
            return;
        if (_memoryBitmap != null)
            _memoryBitmap.UnlockBits(_bmpData);
    }
    private byte* PixelPointer(int x, int y)
    {
        return _pfp + y * _stride + x * _depth;
    }
    public Color GetPixel(int x, int y)
    {
        if (x < 0 || y < 0 || x >= Width || y >= Height)
            throw new Exception("Coordinates out of range");
        int a, r, g, b;
        var p = PixelPointer(x, y);
        if (_memoryBitmap.PixelFormat == PixelFormat.Format32bppArgb)
        {
            b = *p++;
            g = *p++;
            r = *p++;
            a = *p;
            return Color.FromArgb(a, r, g, b);
        }
        if (_memoryBitmap.PixelFormat == PixelFormat.Format24bppRgb)
        {
            b = *p++;
            g = *p++;
            r = *p;
            return Color.FromArgb(r, g, b);
        }
        if (_memoryBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
        {
            a = *p;
            return Color.FromArgb(a, a, a);
        }
        return default;
    }
    public void SetPixel(int x, int y, Color col)
    {
        if (x < 0 || y < 0 || x >= Width || y >= Height)
            throw new Exception("Coordinates out of range");
        var p = PixelPointer(x, y);
        if (_memoryBitmap.PixelFormat == PixelFormat.Format32bppArgb)
        {
            *p++ = col.B;
            *p++ = col.G;
            *p++ = col.R;
            *p   = col.A;
            return;
        }
        if (_memoryBitmap.PixelFormat == PixelFormat.Format24bppRgb)
        {
            *p++ = col.B;
            *p++ = col.G;
            *p   = col.R;
            return;
        }
        if (_memoryBitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            *p = col.B;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *