using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using FreeImageAPI.Metadata;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace FreeImageAPI
{
///
/// Provides methods for working with the standard bitmap palette.
///
public sealed class Palette : MemoryArray
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private GCHandle paletteHandle;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private RGBQUAD[] array;
///
/// Initializes a new instance for the given FreeImage bitmap.
///
/// Handle to a FreeImage bitmap.
/// is null.
/// is not
/// -or-
/// has more than 8bpp.
public Palette(FIBITMAP dib)
: base(FreeImage.GetPalette(dib), (int)FreeImage.GetColorsUsed(dib))
{
if (dib.IsNull)
{
throw new ArgumentNullException("dib");
}
if (FreeImage.GetImageType(dib) != FREE_IMAGE_TYPE.FIT_BITMAP)
{
throw new ArgumentException("dib");
}
if (FreeImage.GetBPP(dib) > 8u)
{
throw new ArgumentException("dib");
}
}
///
/// Initializes a new instance for the given FITAG that contains
/// a palette.
///
/// The tag containing the palette.
/// is null.
/// is not
/// .
public Palette(FITAG tag)
: base(FreeImage.GetTagValue(tag), (int)FreeImage.GetTagCount(tag))
{
if (FreeImage.GetTagType(tag) != FREE_IMAGE_MDTYPE.FIDT_PALETTE)
{
throw new ArgumentException("tag");
}
}
///
/// Initializes a new instance for the given MetadataTag that contains
/// a palette.
///
/// The tag containing the palette.
/// is null.
/// is not
/// .
public Palette(MetadataTag tag)
: base(FreeImage.GetTagValue(tag.tag), (int)tag.Count)
{
if (FreeImage.GetTagType(tag) != FREE_IMAGE_MDTYPE.FIDT_PALETTE)
{
throw new ArgumentException("tag");
}
}
///
/// Initializes a new instance for the given array of that contains
/// a palette.
///
/// A RGBQUAD array containing the palette data to initialize this instance.
public Palette(RGBQUAD[] palette)
{
unsafe
{
this.array = (RGBQUAD[])palette.Clone();
this.paletteHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
base.baseAddress = (byte*)this.paletteHandle.AddrOfPinnedObject();
base.length = (int)this.array.Length;
// Create an array containing a single element.
// Due to the fact, that it's not possible to create pointers
// of generic types, an array is used to obtain the memory
// address of an element of T.
base.buffer = new RGBQUAD[1];
// The array is pinned immediately to prevent the GC from
// moving it to a different position in memory.
base.handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
// The array and its content have beed pinned, so that its address
// can be safely requested and stored for the whole lifetime
// of the instace.
base.ptr = (byte*)base.handle.AddrOfPinnedObject();
}
}
///
/// Initializes a new instance for the given array of that contains
/// a palette.
///
/// A Color array containing the palette data to initialize this instance.
public Palette(Color[] palette)
: this(RGBQUAD.ToRGBQUAD(palette))
{
}
///
/// Initializes a new instance with the specified size.
///
/// The size of the palette.
public Palette(int size)
: this(new RGBQUAD[size])
{
}
///
/// Gets or sets the palette through an array of .
///
public RGBQUAD[] AsArray
{
get
{
return Data;
}
set
{
Data = value;
}
}
///
/// Get an array of that the block of memory represents.
/// This property is used for internal palette operations.
///
internal unsafe Color[] ColorData
{
get
{
EnsureNotDisposed();
Color[] data = new Color[length];
for (int i = 0; i < length; i++)
{
data[i] = Color.FromArgb((int)(((uint*)baseAddress)[i] | 0xFF000000));
}
return data;
}
}
///
/// Returns the palette as an array of .
///
/// The palette as an array of .
public RGBQUAD[] ToArray()
{
return Data;
}
///
/// Creates a linear palette based on the provided .
///
/// The used to colorize the palette.
///
/// Only call this method on linear palettes.
///
public void Colorize(Color color)
{
Colorize(color, 0.5d);
}
///
/// Creates a linear palette based on the provided .
///
/// The used to colorize the palette.
/// The position of the color within the new palette.
/// 0 < < 1.
///
/// Only call this method on linear palettes.
///
public void Colorize(Color color, double splitSize)
{
Colorize(color, (int)(length * splitSize));
}
///
/// Creates a linear palette based on the provided .
///
/// The used to colorize the palette.
/// The position of the color within the new palette.
/// 0 < < .
///
/// Only call this method on linear palettes.
///
public void Colorize(Color color, int splitSize)
{
EnsureNotDisposed();
if (splitSize < 1 || splitSize >= length)
{
throw new ArgumentOutOfRangeException("splitSize");
}
RGBQUAD[] pal = new RGBQUAD[length];
double red = color.R;
double green = color.G;
double blue = color.B;
int i = 0;
double r, g, b;
r = red / splitSize;
g = green / splitSize;
b = blue / splitSize;
for (; i <= splitSize; i++)
{
pal[i].rgbRed = (byte)(i * r);
pal[i].rgbGreen = (byte)(i * g);
pal[i].rgbBlue = (byte)(i * b);
}
r = (255 - red) / (length - splitSize);
g = (255 - green) / (length - splitSize);
b = (255 - blue) / (length - splitSize);
for (; i < length; i++)
{
pal[i].rgbRed = (byte)(red + ((i - splitSize) * r));
pal[i].rgbGreen = (byte)(green + ((i - splitSize) * g));
pal[i].rgbBlue = (byte)(blue + ((i - splitSize) * b));
}
Data = pal;
}
///
/// Creates a linear grayscale palette.
///
public void CreateGrayscalePalette()
{
Colorize(Color.White, length - 1);
}
///
/// Creates a linear grayscale palette.
///
/// true to create an inverse grayscale palette.
public void CreateGrayscalePalette(bool inverse)
{
Colorize(Color.White, inverse ? 0 : length - 1);
}
///
/// Creates a linear palette with the specified .
///
///
/// A linear grayscale palette contains all shades of colors from
/// black to white. This method creates a similar palette with the white
/// color being replaced by the specified color.
///
/// The used to create the palette.
/// true to create an inverse palette.
public void CreateGrayscalePalette(Color color, bool inverse)
{
Colorize(color, inverse ? 0 : length - 1);
}
///
/// Reverses the palette.
///
public void Reverse()
{
EnsureNotDisposed();
if (array != null)
{
Array.Reverse(array);
}
else
{
RGBQUAD[] localArray = Data;
Array.Reverse(localArray);
Data = localArray;
}
}
///
/// Copies the values from the specified to this instance.
///
/// The palette to copy from.
///
/// is a null reference.
public void CopyFrom(Palette palette)
{
EnsureNotDisposed();
if (palette == null)
{
throw new ArgumentNullException("palette");
}
CopyFrom(palette.Data, 0, 0, Math.Min(palette.Length, this.Length));
}
///
/// Copies the values from the specified to this instance,
/// starting at the specified .
///
/// The palette to copy from.
/// The position in this instance where the values
/// will be copied to.
///
/// is a null reference.
///
/// is outside the range of valid indexes.
public void CopyFrom(Palette palette, int offset)
{
EnsureNotDisposed();
CopyFrom(palette.Data, 0, offset, Math.Min(palette.Length, this.Length - offset));
}
///
/// Saves this to the specified file.
///
///
/// A string that contains the name of the file to which to save this .
///
public void Save(string filename)
{
using (Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
Save(stream);
}
}
///
/// Saves this to the specified stream.
///
///
/// The where the image will be saved.
///
public void Save(Stream stream)
{
Save(new BinaryWriter(stream));
}
///
/// Saves this using the specified writer.
///
///
/// The used to save the image.
///
public void Save(BinaryWriter writer)
{
EnsureNotDisposed();
writer.Write(ToByteArray());
}
///
/// Loads a palette from the specified file.
///
/// The name of the palette file.
public void Load(string filename)
{
using (Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
Load(stream);
}
}
///
/// Loads a palette from the specified stream.
///
/// The stream to load the palette from.
public void Load(Stream stream)
{
Load(new BinaryReader(stream));
}
///
/// Loads a palette from the reader.
///
/// The reader to load the palette from.
public void Load(BinaryReader reader)
{
EnsureNotDisposed();
unsafe
{
int size = length * sizeof(RGBQUAD);
byte[] data = reader.ReadBytes(size);
fixed (byte* src = data)
{
CopyMemory(baseAddress, src, data.Length);
}
}
}
///
/// Releases allocated handles associated with this instance.
///
/// true to release managed resources.
protected override void Dispose(bool disposing)
{
if (paletteHandle.IsAllocated)
paletteHandle.Free();
array = null;
base.Dispose(disposing);
}
}
}