Created
February 7, 2016 21:23
-
-
Save tkouba/0b7f8496f1aadcfee2db to your computer and use it in GitHub Desktop.
Work with bitmap faster in C#, solution for positive and negative stride.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
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
what if i want to make it worked for 1Bpp ?
too much needed