using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Pfz.Drawing.Extensions.ManagedBitmapIntExtensions;
using Pfz.Threading;
namespace Pfz.Drawing
{
/// <summary>
/// A ManagedBitmap that uses argb 32-bit ints as the pixel values and also presents a
/// System.Drawing.Bitmap wrapper that shows/edits it's content without copy.
/// </summary>
[Serializable]
public class Argb32Bitmap:
ManagedBitmap<int>,
ISerializable,
IDisposable
{
private GCHandle fPinnedHandle;
/// <summary>
/// Creates a new Argb32Bitmap with the given Width and Height.
/// </summary>
public Argb32Bitmap(int width, int height):
base(width, height)
{
p_InitializeSystemBitmap();
}
/// <summary>
/// Creates a new Argb32Bitmap using the given baseArray and width.
/// </summary>
public Argb32Bitmap(int[] baseArray, int width):
base(baseArray, width)
{
p_InitializeSystemBitmap();
}
/// <summary>
/// Creates a new Argb32Bitmap as a copy of the given System.Drawing.Bitmap, using the given
/// convert delegate.
/// </summary>
public Argb32Bitmap(Bitmap bitmap, Func<byte, int> convertFromIndexed8Delegate):
base(bitmap, convertFromIndexed8Delegate)
{
p_InitializeSystemBitmap();
}
/// <summary>
/// Creates a new Argb32Bitmap as a copy of the given System.Drawing.Bitmap, using the given
/// convert delegate.
/// </summary>
public Argb32Bitmap(Bitmap bitmap, Func<int, int> convertFromArgbDelegate):
base(bitmap, convertFromArgbDelegate)
{
p_InitializeSystemBitmap();
}
/// <summary>
/// Creates a clone of the given System.Drawing.Bitmap.
/// </summary>
public Argb32Bitmap(Bitmap sourceBitmap):
this(p_GetSourceBitmapWidth(sourceBitmap), sourceBitmap.Height)
{
if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb)
this.CopyFrom(sourceBitmap);
else
{
Bitmap newBitmap = null;
AbortSafe.Run
(
() => newBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format32bppArgb),
() =>
{
Graphics graphics = null;
AbortSafe.Run
(
() => graphics = Graphics.FromImage(newBitmap),
() =>
{
graphics.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
this.CopyFrom(newBitmap);
},
() =>
{
if (graphics != null)
graphics.Dispose();
}
);
},
() =>
{
if (newBitmap != null)
newBitmap.Dispose();
}
);
}
}
private static int p_GetSourceBitmapWidth(Bitmap sourceBitmap)
{
if (sourceBitmap == null)
throw new ArgumentNullException("sourceBitmap");
return sourceBitmap.Width;
}
/// <summary>
/// Disposes the System.Drawing.Bitmap generated by this bitmap.
/// </summary>
~Argb32Bitmap()
{
Dispose(false);
}
/// <summary>
/// Disposes the System.Drawing.Bitmap generated by this bitmap.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Must be implemented by child classes to free any additional resource.
/// </summary>
protected virtual void Dispose(bool disposing)
{
// even if this Dispose is called from the destructor, we need to free
// our systemBitmap. It's better that someone gets an exception because
// he is using a disposed bitmap than to free the PinnedHandle and later
// corrupt the memory of some other object.
var systemBitmap = AsSystemBitmap;
if (systemBitmap != null)
{
AsSystemBitmap = null;
systemBitmap.Dispose();
}
if (fPinnedHandle.IsAllocated)
fPinnedHandle.Free();
}
private void p_InitializeSystemBitmap()
{
AbortSafe.Run
(
() =>
{
fPinnedHandle = GCHandle.Alloc(PixelArray, GCHandleType.Pinned);
AsSystemBitmap = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppArgb, fPinnedHandle.AddrOfPinnedObject());
}
);
}
/// <summary>
/// Gets a System.Drawing.Bitmap object that uses the same pixels as this ManagedBitmap.
/// </summary>
public Bitmap AsSystemBitmap { get; private set; }
/// <summary>
/// Gets the color from the given coordinate.
/// </summary>
public Color GetColor(int x, int y)
{
return Color.FromArgb(this[x, y]);
}
/// <summary>
/// Sets the color at a given coordinate.
/// </summary>
public void SetColor(int x, int y, Color color)
{
this[x, y] = color.ToArgb();
}
#region ISerializable Members
/// <summary>
/// Creates the Argb32Bitmap from serialization.
/// </summary>
protected Argb32Bitmap(SerializationInfo info, StreamingContext context):
base((int[])info.GetValue("PixelArray", typeof(int[])), info.GetInt32("Width"))
{
p_InitializeSystemBitmap();
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("PixelArray", PixelArray);
info.AddValue("Width", Width);
}
#endregion
}
}