-
-
Save tkouba/0b7f8496f1aadcfee2db to your computer and use it in GitHub Desktop.
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 | |
} | |
} |
Excellent version. I'm not sure you need to implement an IDisposable interface though as source (Bitmap) is not yours to worry about and is under the caller's ownership. If they call LockBitmap() methods after the source bitmap is disposed, that's on them. Either way, they'd get an exception.
I'd like to read the ideas regarding your approach as I'm always open to learning new ideas.
Dispose only unlock bits, not bitmap itself. You should lock bitmap only for short time, so IDisposable interface helps to remember that.
This code was from my old project, so I have some comments on it, but I have no time for better implementation and testing.
will it work with 1bpp Indexed file ??
Only 8, 24 and 32 bpp images are supported. See line 54
what if i want to make it worked for 1Bpp ?
too much needed
You must change condition on line 52 and add proper condition to GetPixel and PutPixel functions.
Hello,
I've been using this code for small bitmaps. I haven't any experience with similar problem.
I'm sorry, I can't help you.
Tomas