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); } } }