using System;
using System.Drawing;
using System.Drawing.Imaging;
using Pfz.Drawing.Extensions.BitmapExtensions;
using Pfz.Threading;
namespace Pfz.Drawing
{
/// <summary>
/// Represents a "map" of values as a two-dimensional array. Ideal to convert to and
/// from unmanaged bitmaps.
/// </summary>
/// <typeparam name="T">The type of the data this map supports.</typeparam>
[Serializable]
public class ManagedBitmap<T>:
IBitmap
{
#region Constructors
/// <summary>
/// Creates a new ManagedBitmap with the given width (first dimension) and
/// height (second dimension).
/// </summary>
/// <param name="width">The size of the first dimension.</param>
/// <param name="height">The size of the second dimension.</param>
public ManagedBitmap(int width, int height)
{
if (width < 1)
throw new ArgumentOutOfRangeException("width");
if (height < 1)
throw new ArgumentOutOfRangeException("height");
fPixelArray = new T[width * height];
fWidth = width;
}
/// <summary>
/// Creates a new ManagedBitmap using the given single-dimension array and
/// the given width. The array must have a length that is a multiplier of width.
/// </summary>
/// <param name="baseArray">The base array. Must be different than null.</param>
/// <param name="width">The width of the array.</param>
public ManagedBitmap(T[] baseArray, int width)
{
if (baseArray == null)
throw new ArgumentNullException("array");
if (width < 1)
throw new ArgumentOutOfRangeException("width");
if (baseArray.Length % width != 0)
throw new ArgumentException("The array length must be a multiple of width.");
fPixelArray = baseArray;
fWidth = width;
}
/// <summary>
/// Creates a new ManagedBitmap that is a copy of a given 32-bit argb
/// System.Drawing.Bitmap.
/// </summary>
public ManagedBitmap(Bitmap bitmap, Func<int, T> convertFromArgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromArgbDelegate == null)
throw new ArgumentNullException("convertFromArgbDelegate");
int width = bitmap.Width;
int height = bitmap.Height;
T[] array = new T[width * height];
int arrayIndex = -1;
Bitmap bitmapToUse = null;
BitmapData data = null;
AbortSafe.Run
(
() =>
{
if (bitmap.PixelFormat == PixelFormat.Format32bppArgb)
bitmapToUse = bitmap;
else
bitmapToUse = bitmap.CloneAsFormat32bppArgb();
data = bitmapToUse.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
},
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<width; x++)
{
arrayIndex++;
int argb = scanLine[x];
array[arrayIndex] = convertFromArgbDelegate(argb);
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmapToUse.UnlockBits(data);
if (bitmapToUse != bitmap && bitmapToUse != null)
bitmapToUse.Dispose();
}
);
fPixelArray = array;
fWidth = width;
}
/// <summary>
/// Creates a new ManagedBitmap that is a copy of a given 24-bit rgb
/// System.Drawing.Bitmap.
/// </summary>
public ManagedBitmap(Bitmap bitmap, Func<Rgb24, T> convertFromRgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromRgbDelegate == null)
throw new ArgumentNullException("convertFromRgbDelegate");
int width = bitmap.Width;
int height = bitmap.Height;
T[] array = new T[width * height];
int arrayIndex = -1;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<width; x++)
{
arrayIndex++;
Rgb24 rgb = scanLine[x];
array[arrayIndex] = convertFromRgbDelegate(rgb);
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
fPixelArray = array;
fWidth = width;
}
/// <summary>
/// Creates a new ManagedBitmap that is a copy of a given indexed 8-bit
/// System.Drawing.Bitmap.
/// </summary>
public ManagedBitmap(Bitmap bitmap, Func<byte, T> convertFromIndexDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap.PixelFormat must be Format8bppIndexed.");
if (convertFromIndexDelegate == null)
throw new ArgumentNullException("convertFromIndexDelegate");
int width = bitmap.Width;
int height = bitmap.Height;
T[] array = new T[width * height];
int arrayIndex = -1;
BitmapData data = null;
AbortSafe.Run
(
() =>
{
data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
},
() =>
{
unsafe
{
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
arrayIndex++;
byte index = scanLine[x];
array[arrayIndex] = convertFromIndexDelegate(index);
}
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
fPixelArray = array;
fWidth = width;
}
#endregion
#region Properties
private T[] fPixelArray;
/// <summary>
/// Gets the base array used by this bitmap.
/// </summary>
public T[] PixelArray
{
get
{
return fPixelArray;
}
}
private int fWidth;
/// <summary>
/// Gets the width of this bitmap.
/// </summary>
public int Width
{
get
{
return fWidth;
}
}
/// <summary>
/// Gets the height of this bitmap.
/// </summary>
public int Height
{
get
{
return fPixelArray.Length / fWidth;
}
}
/// <summary>
/// Gets or sets a value into the bitmap.
/// </summary>
/// <param name="x">The first-dimension index in the array.</param>
/// <param name="y">The second-dimension index in the array.</param>
/// <returns>The value at the given index.</returns>
public T this[int x, int y]
{
get
{
return PixelArray[y * Width + x];
}
set
{
PixelArray[y * Width + x] = value;
}
}
#endregion
#region Methods
#region CopyTo - full managed
#region Same type
/// <summary>
/// Copies a part of this bitmap to another bitmap of the same type.
/// </summary>
public void CopyTo(ManagedBitmap<T> other, Point sourceLocation, Point destLocation, Size size)
{
if (PixelArray == null)
return;
if (other.PixelArray == null)
throw new ArgumentException("other array must not be empty.", "other");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > other.fWidth || destLocation.Y + size.Height > other.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int sourceIndex = sourceLocation.Y * Width + sourceLocation.X;
int destIndex = destLocation.Y * other.Width + destLocation.X;
T[] sourceArray = PixelArray;
T[] destArray = other.PixelArray;
for (int y=0; y<size.Height; y++)
{
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = sourceArray[sourceIndex+x];
sourceIndex += Width;
destIndex += other.Width;
}
}
#endregion
#region Other type
/// <summary>
/// Copies a part of this bitmap to a bitmap of a different type.
/// </summary>
public void CopyTo<T2>(ManagedBitmap<T2> other, Point sourceLocation, Point destLocation, Size size, Func<T, T2> convertToDelegate)
{
if (PixelArray == null)
return;
if (other.PixelArray == null)
throw new ArgumentException("other array must not be empty.", "other");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertToDelegate == null)
throw new ArgumentNullException("convertToDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > other.fWidth || destLocation.Y + size.Height > other.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int sourceIndex = sourceLocation.Y * Width + sourceLocation.X;
int destIndex = destLocation.Y * other.Width + destLocation.X;
T[] sourceArray = PixelArray;
T2[] destArray = other.PixelArray;
for (int y=0; y<size.Height; y++)
{
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertToDelegate(sourceArray[sourceIndex+x]);
sourceIndex += Width;
destIndex += other.Width;
}
}
#endregion
#endregion
#region Create - CopyTo/From - System.Drawing.Bitmap - Argb32
#region CopyTo
private void p_CopyToBitmapDirectArgb32(Bitmap bitmap, Func<T, int> convertToArgbDelegate)
{
int width = Width;
int height = Height;
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<width; x++)
{
arrayIndex++;
T value = baseArray[arrayIndex];
int argb = convertToArgbDelegate(value);
scanLine[x] = argb;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a part of this bitmap to a System.Drawing.Bitmap.
/// </summary>
public void CopyToArgb32(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<T, int> convertToArgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap.PixelFormat must be Format32bppArgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertToArgbDelegate == null)
throw new ArgumentNullException("convertToArgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int sourceIndex = sourceLocation.Y * Width + sourceLocation.X;
T[] sourceArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(destLocation, size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<size.Width; x++)
scanLine[x] = convertToArgbDelegate(sourceArray[sourceIndex+x]);
sourceIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies this bitmap to the given System.Drawing.Bitmap.
/// It must be a 32-bit argb bitmap for this to work.
/// </summary>
public void CopyToArgb32(Bitmap bitmap, Func<T, int> convertToArgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertToArgbDelegate == null)
throw new ArgumentNullException("convertToArgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap must be in 32-bit argb format.");
int width = bitmap.Width;
int height = bitmap.Height;
if (width == Width && height == Height)
{
p_CopyToBitmapDirectArgb32(bitmap, convertToArgbDelegate);
return;
}
CopyToArgb32(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(Width, width), Math.Min(Height, height)), convertToArgbDelegate);
}
#endregion
#region CopyFrom
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 32-bit argb bitmap for this to work.
/// </summary>
public void CopyFromArgb32(Bitmap bitmap, Func<int, T> convertFromArgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromArgbDelegate == null)
throw new ArgumentNullException("convertFromArgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap must be in 32-bit argb format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromArgb32(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromArgbDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<width; x++)
{
int argb = scanLine[x];
T value = convertFromArgbDelegate(argb);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 32-bit argb bitmap for this to work.
/// </summary>
public void CopyFromArgb32(Bitmap bitmap, Func<Point, int, T> convertFromArgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromArgbDelegate == null)
throw new ArgumentNullException("convertFromArgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap must be in 32-bit argb format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromArgb32(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromArgbDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<width; x++)
{
int argb = scanLine[x];
T value = convertFromArgbDelegate(new Point(x, y), argb);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this ManagedBitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromArgb32(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<int, T> convertFromArgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap.PixelFormat must be Format32bppArgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromArgbDelegate == null)
throw new ArgumentNullException("convertFromArgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromArgbDelegate(scanLine[x]);
destIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this bitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromArgb32(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<Point, int, T> convertFromArgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
throw new ArgumentException("bitmap.PixelFormat must be Format32bppArgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromArgbDelegate == null)
throw new ArgumentNullException("convertFromArgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
int *scanLine = (int *)scanLineBytes;
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromArgbDelegate(new Point(x, y), scanLine[x]);
destIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
#endregion
#region CreateBitmapArgb32
/// <summary>
/// Creates a new 32-bit argb System.Drawing.Bitmap using the given convert method.
/// </summary>
public Bitmap CreateBitmapArgb32(Func<T, int> convertToArgbDelegate)
{
if (PixelArray == null)
throw new ArgumentException("This array must not be empty.", "array");
if (convertToArgbDelegate == null)
throw new ArgumentNullException("convertToArgbDelegate");
Bitmap result = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
p_CopyToBitmapDirectArgb32(result, convertToArgbDelegate);
return result;
}
#endregion
#endregion
#region Create - CopyTo/From - System.Drawing.Bitmap - Rgb24
#region CopyTo
private void p_CopyToBitmapDirectRgb24(Bitmap bitmap, Func<T, Rgb24> convertToRgbDelegate)
{
int width = Width;
int height = Height;
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<width; x++)
{
arrayIndex++;
T value = baseArray[arrayIndex];
Rgb24 rgb = convertToRgbDelegate(value);
scanLine[x] = rgb;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a part of this bitmap to a System.Drawing.Bitmap.
/// </summary>
public void CopyToRgb24(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<T, Rgb24> convertToRgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap.PixelFormat must be Format24bppRgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertToRgbDelegate == null)
throw new ArgumentNullException("convertToArgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int sourceIndex = sourceLocation.Y * Width + sourceLocation.X;
T[] sourceArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(destLocation, size), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<size.Width; x++)
scanLine[x] = convertToRgbDelegate(sourceArray[sourceIndex+x]);
sourceIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies this bitmap to the given System.Drawing.Bitmap.
/// It must be a 24-bit rgb bitmap for this to work.
/// </summary>
public void CopyToRgb24(Bitmap bitmap, Func<T, Rgb24> convertToRgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertToRgbDelegate == null)
throw new ArgumentNullException("convertToRgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap must be Format24bppRgb.");
int width = bitmap.Width;
int height = bitmap.Height;
if (width == Width && height == Height)
{
p_CopyToBitmapDirectRgb24(bitmap, convertToRgbDelegate);
return;
}
CopyToRgb24(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(Width, width), Math.Min(Height, height)), convertToRgbDelegate);
}
#endregion
#region CopyFrom
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 24-bit rgb bitmap for this to work.
/// </summary>
public void CopyFromRgb24(Bitmap bitmap, Func<Rgb24, T> convertFromRgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromRgbDelegate == null)
throw new ArgumentNullException("convertFromRgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap must be in Format24bppRgb format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromRgb24(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromRgbDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<width; x++)
{
Rgb24 rgb = scanLine[x];
T value = convertFromRgbDelegate(rgb);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 24-bit rgb bitmap for this to work.
/// </summary>
public void CopyFromRgb24(Bitmap bitmap, Func<Point, Rgb24, T> convertFromRgbDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromRgbDelegate == null)
throw new ArgumentNullException("convertFromRgbDelegate");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap must be in Format24bppRgb format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromRgb24(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromRgbDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<width; x++)
{
Rgb24 rgb = scanLine[x];
T value = convertFromRgbDelegate(new Point(x, y), rgb);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this ManagedBitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromRgb24(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<Rgb24, T> convertFromRgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap.PixelFormat must be Format24bppRgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromRgbDelegate == null)
throw new ArgumentNullException("convertFromRgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromRgbDelegate(scanLine[x]);
destIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this bitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromRgb24(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<Point, Rgb24, T> convertFromRgbDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format24bppRgb)
throw new ArgumentException("bitmap.PixelFormat must be Format24bppRgb.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromRgbDelegate == null)
throw new ArgumentNullException("convertFromRgbDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb),
() =>
{
unsafe
{
byte *scanLineBytes = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
Rgb24 *scanLine = (Rgb24 *)scanLineBytes;
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromRgbDelegate(new Point(x, y), scanLine[x]);
destIndex += Width;
scanLineBytes += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
#endregion
#region CreateBitmapRgb24
/// <summary>
/// Creates a new 24-bit rgb System.Drawing.Bitmap using the given convert method.
/// </summary>
public Bitmap CreateBitmapRgb24(Func<T, Rgb24> convertToRgbDelegate)
{
if (PixelArray == null)
throw new ArgumentException("This array must not be empty.", "array");
if (convertToRgbDelegate == null)
throw new ArgumentNullException("convertToRgbDelegate");
Bitmap result = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
p_CopyToBitmapDirectRgb24(result, convertToRgbDelegate);
return result;
}
#endregion
#endregion
#region Create - CopyTo/From - System.Drawing.Bitmap - Indexed8/Grayscale
#region CopyTo
private void p_CopyToBitmapDirectIndexed8(Bitmap bitmap, Func<T, byte> convertToIndexDelegate)
{
int width = Width;
int height = Height;
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
arrayIndex++;
T value = baseArray[arrayIndex];
byte index = convertToIndexDelegate(value);
scanLine[x] = index;
}
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a part of this bitmap to a System.Drawing.Bitmap.
/// </summary>
public void CopyToIndexed8(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<T, byte> convertToIndexDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap.PixelFormat must be Format8bppIndexed.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertToIndexDelegate == null)
throw new ArgumentNullException("convertToIndexDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int sourceIndex = sourceLocation.Y * Width + sourceLocation.X;
T[] sourceArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(destLocation, size), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
for (int x=0; x<size.Width; x++)
scanLine[x] = convertToIndexDelegate(sourceArray[sourceIndex+x]);
sourceIndex += Width;
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies this bitmap to the given System.Drawing.Bitmap.
/// It must be a 8-bit indexed bitmap for this to work.
/// </summary>
public void CopyToIndexed8(Bitmap bitmap, Func<T, byte> convertToIndexDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertToIndexDelegate == null)
throw new ArgumentNullException("convertToIndexDelegate");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap must be in 8-bit indexed format.");
int width = bitmap.Width;
int height = bitmap.Height;
if (width == Width && height == Height)
{
p_CopyToBitmapDirectIndexed8(bitmap, convertToIndexDelegate);
return;
}
CopyToIndexed8(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(Width, width), Math.Min(Height, height)), convertToIndexDelegate);
}
#endregion
#region CopyFrom
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 8-bit indexed bitmap for this to work.
/// </summary>
public void CopyFromIndexed8(Bitmap bitmap, Func<byte, T> convertFromIndexDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromIndexDelegate == null)
throw new ArgumentNullException("convertFromIndexDelegate");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap must be in 8-bit indexed format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromIndexed8(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromIndexDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
byte index = scanLine[x];
T value = convertFromIndexDelegate(index);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies the given System.Drawing.Bitmap to this bitmap.
/// It must be a 8-bit indexed bitmap for this to work.
/// </summary>
public void CopyFromIndexed8(Bitmap bitmap, Func<Point, byte, T> convertFromIndexDelegate)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (convertFromIndexDelegate == null)
throw new ArgumentNullException("convertFromIndexDelegate");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap must be in 8-bit indexed format.");
int width = Width;
int height = Height;
if (bitmap.Width != width || bitmap.Height != height)
{
CopyFromIndexed8(bitmap, new Point(0, 0), new Point(0, 0), new Size(Math.Min(width, bitmap.Width), Math.Min(height, bitmap.Height)), convertFromIndexDelegate);
return;
}
T[] baseArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
int arrayIndex = -1;
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
byte index = scanLine[x];
T value = convertFromIndexDelegate(new Point(x, y), index);
arrayIndex++;
baseArray[arrayIndex] = value;
}
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this ManagedBitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromIndexed8(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<byte, T> convertFromIndexDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap.PixelFormat must be Format8bppIndexed.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromIndexDelegate == null)
throw new ArgumentNullException("convertFromIndexDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromIndexDelegate(scanLine[x]);
destIndex += Width;
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
/// <summary>
/// Copies a block from a System.Drawing.Bitmap to this bitmap using the given conversion
/// delegate.
/// </summary>
public void CopyFromIndexed8(Bitmap bitmap, Point sourceLocation, Point destLocation, Size size, Func<Point, byte, T> convertFromIndexDelegate)
{
if (PixelArray == null)
return;
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
throw new ArgumentException("bitmap.PixelFormat must be Format8bppIndexed.", "bitmap");
if (sourceLocation.X < 0 || sourceLocation.Y < 0)
throw new ArgumentOutOfRangeException("sourceLocation");
if (destLocation.X < 0 || destLocation.Y < 0)
throw new ArgumentOutOfRangeException("destLocation");
if (convertFromIndexDelegate == null)
throw new ArgumentNullException("convertFromIndexDelegate");
if
(
sourceLocation.X + size.Width > fWidth || sourceLocation.Y + size.Height > Height ||
destLocation.X + size.Width > bitmap.Width || destLocation.Y + size.Height > bitmap.Height
)
{
throw new ArgumentOutOfRangeException("size");
}
int destIndex = destLocation.Y * Width + destLocation.X;
T[] destArray = PixelArray;
BitmapData data = null;
AbortSafe.Run
(
() => data = bitmap.LockBits(new Rectangle(sourceLocation, size), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed),
() =>
{
unsafe
{
byte *scanLine = (byte *)data.Scan0.ToPointer();
for (int y=0; y<size.Height; y++)
{
for (int x=0; x<size.Width; x++)
destArray[destIndex+x] = convertFromIndexDelegate(new Point(x, y), scanLine[x]);
destIndex += Width;
scanLine += data.Stride;
}
}
},
() =>
{
if (data != null)
bitmap.UnlockBits(data);
}
);
}
#endregion
#region CreateBitmapIndexed8
/// <summary>
/// Creates a new 8-bit indexed System.Drawing.Bitmap using the given convert method.
/// </summary>
public Bitmap CreateBitmapIndexed8(Func<T, byte> convertToIndexDelegate)
{
if (PixelArray == null)
throw new ArgumentException("This array must not be empty.", "array");
if (convertToIndexDelegate == null)
throw new ArgumentNullException("convertToIndexDelegate");
Bitmap result = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
p_CopyToBitmapDirectIndexed8(result, convertToIndexDelegate);
result.Palette = GrayscalePalette.Value;
return result;
}
#endregion
#endregion
#endregion
#region IBitmap Members
Array IBitmap.PixelArray
{
get
{
return PixelArray;
}
}
object IBitmap.this[int x, int y]
{
get
{
return this[x, y];
}
set
{
this[x, y] = (T)value;
}
}
#endregion
}
}