Skip to content

Instantly share code, notes, and snippets.

@tkouba
Created February 7, 2016 21:23
Show Gist options
  • Save tkouba/0b7f8496f1aadcfee2db to your computer and use it in GitHub Desktop.
Save tkouba/0b7f8496f1aadcfee2db to your computer and use it in GitHub Desktop.
Work with bitmap faster in C#, solution for positive and negative stride.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ImageHelper
{
public class LockBitmap : IDisposable
{
Bitmap source = null;
IntPtr Iptr = IntPtr.Zero;
BitmapData bitmapData = null;
public byte[] Pixels { get; set; }
public int Depth { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public int RowSize { get; private set; }
public bool IsDisposed { get; private set; }
public LockBitmap(Bitmap source)
{
this.source = source;
IsDisposed = false;
}
/// <summary>
/// Lock bitmap data
/// </summary>
public void LockBits()
{
try
{
if (IsDisposed)
throw new ObjectDisposedException(typeof(LockBitmap).Name);
// Get width and height of bitmap
Width = source.Width;
Height = source.Height;
// Create rectangle to lock
Rectangle rect = new Rectangle(0, 0, Width, Height);
// get source bitmap pixel format size
Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
// Check if bpp (Bits Per Pixel) is 8, 24, or 32
if (Depth != 8 && Depth != 24 && Depth != 32)
{
throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
}
// Lock bitmap and return bitmap data
bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
source.PixelFormat);
// create byte array to copy pixel values
RowSize = bitmapData.Stride < 0 ? -bitmapData.Stride : bitmapData.Stride;
Pixels = new byte[Height * RowSize];
Iptr = bitmapData.Scan0;
// Copy data from pointer to array
// Not working for negative Stride see. http://stackoverflow.com/a/10360753/1498252
//Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
// Solution for positive and negative Stride:
for (int y = 0; y < Height; y++)
{
Marshal.Copy(IntPtr.Add(Iptr, y * bitmapData.Stride),
Pixels, y * RowSize,
RowSize);
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Unlock bitmap data
/// </summary>
public void UnlockBits()
{
try
{
if (IsDisposed)
throw new ObjectDisposedException(typeof(LockBitmap).Name);
if (bitmapData == null)
throw new InvalidOperationException("Image is not locked.");
// Copy data from byte array to pointer
//Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);
for (int y = 0; y < Height; y++)
{
Marshal.Copy(Pixels, y * RowSize,
IntPtr.Add(Iptr, y * bitmapData.Stride),
RowSize);
}
// Unlock bitmap data
source.UnlockBits(bitmapData);
bitmapData = null;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Get the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Color GetPixel(int x, int y)
{
if (IsDisposed)
throw new ObjectDisposedException(typeof(LockBitmap).Name);
Color clr = Color.Empty;
// Get color components count
int cCount = Depth / 8;
// Get start index of the specified pixel
int i = (y * RowSize) + (x * cCount);
if (i > Pixels.Length - cCount)
throw new IndexOutOfRangeException();
if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
byte a = Pixels[i + 3]; // a
clr = Color.FromArgb(a, r, g, b);
}
if (Depth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (Depth == 8)
// For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = Pixels[i];
clr = Color.FromArgb(c, c, c);
}
return clr;
}
/// <summary>
/// Set the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void SetPixel(int x, int y, Color color)
{
if (IsDisposed)
throw new ObjectDisposedException(typeof(LockBitmap).Name);
// Get color components count
int cCount = Depth / 8;
// Get start index of the specified pixel
int i = (y * RowSize) + (x * cCount);
if (i > Pixels.Length - cCount)
throw new IndexOutOfRangeException();
if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
Pixels[i + 3] = color.A;
}
if (Depth == 24) // For 24 bpp set Red, Green and Blue
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
}
if (Depth == 8)
// For 8 bpp set color value (Red, Green and Blue values are the same)
{
Pixels[i] = color.B;
}
}
#region IDisposable Members
~LockBitmap()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (bitmapData != null)
{
source.UnlockBits(bitmapData);
bitmapData = null;
}
source = null;
IsDisposed = true;
}
#endregion
}
}
@jaydeep6720
Copy link

will it work with 1bpp Indexed file ??

@tkouba
Copy link
Author

tkouba commented Jun 10, 2021

Only 8, 24 and 32 bpp images are supported. See line 54

@jaydeep6720
Copy link

what if i want to make it worked for 1Bpp ?
too much needed

@tkouba
Copy link
Author

tkouba commented Jun 10, 2021

You must change condition on line 52 and add proper condition to GetPixel and PutPixel functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment