diff options
Diffstat (limited to 'Wrapper/FreeImage.NET/cs/Library/FreeImageWrapper.cs')
-rw-r--r-- | Wrapper/FreeImage.NET/cs/Library/FreeImageWrapper.cs | 5319 |
1 files changed, 5319 insertions, 0 deletions
diff --git a/Wrapper/FreeImage.NET/cs/Library/FreeImageWrapper.cs b/Wrapper/FreeImage.NET/cs/Library/FreeImageWrapper.cs new file mode 100644 index 0000000..4bbf619 --- /dev/null +++ b/Wrapper/FreeImage.NET/cs/Library/FreeImageWrapper.cs @@ -0,0 +1,5319 @@ +// ========================================================== +// FreeImage 3 .NET wrapper +// Original FreeImage 3 functions and .NET compatible derived functions +// +// Design and implementation by +// - Jean-Philippe Goerke (jpgoerke@users.sourceforge.net) +// - Carsten Klein (cklein05@users.sourceforge.net) +// +// Contributors: +// - David Boland (davidboland@vodafone.ie) +// +// Main reference : MSDN Knowlede Base +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +// ========================================================== +// CVS +// $Revision: 1.16 $ +// $Date: 2009/09/16 14:35:13 $ +// $Id: FreeImageWrapper.cs,v 1.16 2009/09/16 14:35:13 cklein05 Exp $ +// ========================================================== + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using FreeImageAPI.IO; +using FreeImageAPI.Metadata; + +namespace FreeImageAPI +{ + /// <summary> + /// Static class importing functions from the FreeImage library + /// and providing additional functions. + /// </summary> + public static partial class FreeImage + { + #region Constants + + /// <summary> + /// Array containing all 'FREE_IMAGE_MDMODEL's. + /// </summary> + public static readonly FREE_IMAGE_MDMODEL[] FREE_IMAGE_MDMODELS = + (FREE_IMAGE_MDMODEL[])Enum.GetValues(typeof(FREE_IMAGE_MDMODEL)); + + /// <summary> + /// Stores handles used to read from streams. + /// </summary> + private static Dictionary<FIMULTIBITMAP, fi_handle> streamHandles = + new Dictionary<FIMULTIBITMAP, fi_handle>(); + + /// <summary> + /// Version of the wrapper library. + /// </summary> + private static Version WrapperVersion; + + private const int DIB_RGB_COLORS = 0; + private const int DIB_PAL_COLORS = 1; + private const int CBM_INIT = 0x4; + + /// <summary> + /// An uncompressed format. + /// </summary> + public const int BI_RGB = 0; + + /// <summary> + /// A run-length encoded (RLE) format for bitmaps with 8 bpp. The compression format is a 2-byte + /// format consisting of a count byte followed by a byte containing a color index. + /// </summary> + public const int BI_RLE8 = 1; + + /// <summary> + /// An RLE format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting + /// of a count byte followed by two word-length color indexes. + /// </summary> + public const int BI_RLE4 = 2; + + /// <summary> + /// Specifies that the bitmap is not compressed and that the color table consists of three + /// <b>DWORD</b> color masks that specify the red, green, and blue components, respectively, + /// of each pixel. This is valid when used with 16- and 32-bpp bitmaps. + /// </summary> + public const int BI_BITFIELDS = 3; + + /// <summary> + /// <b>Windows 98/Me, Windows 2000/XP:</b> Indicates that the image is a JPEG image. + /// </summary> + public const int BI_JPEG = 4; + + /// <summary> + /// <b>Windows 98/Me, Windows 2000/XP:</b> Indicates that the image is a PNG image. + /// </summary> + public const int BI_PNG = 5; + + #endregion + + #region General functions + + /// <summary> + /// Returns the internal version of this FreeImage .NET wrapper. + /// </summary> + /// <returns>The internal version of this FreeImage .NET wrapper.</returns> + public static Version GetWrapperVersion() + { + if (WrapperVersion == null) + { + try + { + object[] attributes = Assembly.GetAssembly(typeof(FreeImage)) + .GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false); + if ((attributes != null) && (attributes.Length != 0)) + { + AssemblyFileVersionAttribute attribute = + attributes[0] as AssemblyFileVersionAttribute; + if ((attribute != null) && (attribute.Version != null)) + { + return (WrapperVersion = new Version(attribute.Version)); + } + } + } + catch + { + + } + + WrapperVersion = new Version(); + } + + return WrapperVersion; + } + + /// <summary> + /// Returns the version of the native FreeImage library. + /// </summary> + /// <returns>The version of the native FreeImage library.</returns> + public static Version GetNativeVersion() + { + return new Version(GetVersion()); + } + + /// <summary> + /// Returns a value indicating if the FreeImage library is available or not. + /// See remarks for further details. + /// </summary> + /// <returns><c>false</c> if the file is not available or out of date; + /// <c>true</c>, otherwise.</returns> + /// <remarks> + /// The FreeImage.NET library is a wrapper for the native C++ library + /// (FreeImage.dll ... dont mix ist up with this library FreeImageNet.dll). + /// The native library <b>must</b> be either in the same folder as the program's + /// executable or in a folder contained in the envirent variable <i>PATH</i> + /// (for example %WINDIR%\System32).<para/> + /// Further more must both libraries, including the program itself, + /// be the same architecture (x86 or x64). + /// </remarks> + public static bool IsAvailable() + { + try + { + // Call a static fast executing function + Version nativeVersion = new Version(GetVersion()); + Version wrapperVersion = GetWrapperVersion(); + // No exception thrown, the library seems to be present + return + (nativeVersion.Major >= wrapperVersion.Major) && + (nativeVersion.Minor >= wrapperVersion.Minor) && + (nativeVersion.Build >= wrapperVersion.Build); + } + catch (DllNotFoundException) + { + return false; + } + catch (EntryPointNotFoundException) + { + return false; + } + catch (BadImageFormatException) + { + return false; + } + } + + #endregion + + #region Bitmap management functions + + /// <summary> + /// Creates a new bitmap in memory. + /// </summary> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new Bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP Allocate(int width, int height, int bpp) + { + return Allocate(width, height, bpp, 0, 0, 0); + } + + /// <summary> + /// Creates a new bitmap in memory. + /// </summary> + /// <param name="type">Type of the image.</param> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new Bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp) + { + return AllocateT(type, width, height, bpp, 0, 0, 0); + } + + /// <summary> + /// Allocates a new image of the specified width, height and bit depth and optionally + /// fills it with the specified color. See remarks for further details. + /// </summary> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmaps.</param> + /// <param name="color">The color to fill the bitmap with or <c>null</c>.</param> + /// <param name="options">Options to enable or disable function-features.</param> + /// <param name="palette">The palette of the bitmap or <c>null</c>.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function is an extension to <see cref="Allocate"/>, which additionally supports + /// specifying a palette to be set for the newly create image, as well as specifying a + /// background color, the newly created image should initially be filled with. + /// <para/> + /// Basically, this function internally relies on function <see cref="Allocate"/>, followed by a + /// call to <see cref="FillBackground<T>"/>. This is why both parameters + /// <paramref name="color"/> and <paramref name="options"/> behave the same as it is + /// documented for function <see cref="FillBackground<T>"/>. + /// So, please refer to the documentation of <see cref="FillBackground<T>"/> to + /// learn more about parameters <paramref name="color"/> and <paramref name="options"/>. + /// <para/> + /// The palette specified through parameter <paramref name="palette"/> is only copied to the + /// newly created image, if the desired bit depth is smaller than or equal to 8 bits per pixel. + /// In other words, the <paramref name="palette"/> parameter is only taken into account for + /// palletized images. So, for an 8-bit image, the length is 256, for an 4-bit image it is 16 + /// and it is 2 for a 1-bit image. In other words, this function does not support partial palettes. + /// <para/> + /// However, specifying a palette is not necesarily needed, even for palletized images. This + /// function is capable of implicitly creating a palette, if <paramref name="palette"/> is <c>null</c>. + /// If the specified background color is a greyscale value (red = green = blue) or if option + /// <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> is specified, a greyscale palette + /// is created. For a 1-bit image, only if the specified background color is either black or white, + /// a monochrome palette, consisting of black and white only is created. In any case, the darker + /// colors are stored at the smaller palette indices. + /// <para/> + /// If the specified background color is not a greyscale value, or is neither black nor white + /// for a 1-bit image, solely this specified color is injected into the otherwise black-initialized + /// palette. For this operation, option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> + /// is implicit, so the specified <paramref name="color"/> is applied to the palette entry, + /// specified by the background color's <see cref="RGBQUAD.rgbReserved"/> field. + /// The image is then filled with this palette index. + /// <para/> + /// This function returns a newly created image as function <see cref="Allocate"/> does, if both + /// parameters <paramref name="color"/> and <paramref name="palette"/> are <c>null</c>. + /// If only <paramref name="color"/> is <c>null</c>, the palette pointed to by + /// parameter <paramref name="palette"/> is initially set for the new image, if a palletized + /// image of type <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> is created. + /// However, in the latter case, this function returns an image, whose + /// pixels are all initialized with zeros so, the image will be filled with the color of the + /// first palette entry. + /// </remarks> + public static FIBITMAP AllocateEx(int width, int height, int bpp, + RGBQUAD? color, FREE_IMAGE_COLOR_OPTIONS options, RGBQUAD[] palette) + { + return AllocateEx(width, height, bpp, color, options, palette, 0, 0, 0); + } + + /// <summary> + /// Allocates a new image of the specified width, height and bit depth and optionally + /// fills it with the specified color. See remarks for further details. + /// </summary> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmaps.</param> + /// <param name="color">The color to fill the bitmap with or <c>null</c>.</param> + /// <param name="options">Options to enable or disable function-features.</param> + /// <param name="palette">The palette of the bitmap or <c>null</c>.</param> + /// <param name="red_mask">Red part of the color layout. + /// eg: 0xFF0000</param> + /// <param name="green_mask">Green part of the color layout. + /// eg: 0x00FF00</param> + /// <param name="blue_mask">Blue part of the color layout. + /// eg: 0x0000FF</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function is an extension to <see cref="Allocate"/>, which additionally supports + /// specifying a palette to be set for the newly create image, as well as specifying a + /// background color, the newly created image should initially be filled with. + /// <para/> + /// Basically, this function internally relies on function <see cref="Allocate"/>, followed by a + /// call to <see cref="FillBackground<T>"/>. This is why both parameters + /// <paramref name="color"/> and <paramref name="options"/> behave the same as it is + /// documented for function <see cref="FillBackground<T>"/>. + /// So, please refer to the documentation of <see cref="FillBackground<T>"/> to + /// learn more about parameters <paramref name="color"/> and <paramref name="options"/>. + /// <para/> + /// The palette specified through parameter <paramref name="palette"/> is only copied to the + /// newly created image, if the desired bit depth is smaller than or equal to 8 bits per pixel. + /// In other words, the <paramref name="palette"/> parameter is only taken into account for + /// palletized images. So, for an 8-bit image, the length is 256, for an 4-bit image it is 16 + /// and it is 2 for a 1-bit image. In other words, this function does not support partial palettes. + /// <para/> + /// However, specifying a palette is not necesarily needed, even for palletized images. This + /// function is capable of implicitly creating a palette, if <paramref name="palette"/> is <c>null</c>. + /// If the specified background color is a greyscale value (red = green = blue) or if option + /// <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> is specified, a greyscale palette + /// is created. For a 1-bit image, only if the specified background color is either black or white, + /// a monochrome palette, consisting of black and white only is created. In any case, the darker + /// colors are stored at the smaller palette indices. + /// <para/> + /// If the specified background color is not a greyscale value, or is neither black nor white + /// for a 1-bit image, solely this specified color is injected into the otherwise black-initialized + /// palette. For this operation, option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> + /// is implicit, so the specified <paramref name="color"/> is applied to the palette entry, + /// specified by the background color's <see cref="RGBQUAD.rgbReserved"/> field. + /// The image is then filled with this palette index. + /// <para/> + /// This function returns a newly created image as function <see cref="Allocate"/> does, if both + /// parameters <paramref name="color"/> and <paramref name="palette"/> are <c>null</c>. + /// If only <paramref name="color"/> is <c>null</c>, the palette pointed to by + /// parameter <paramref name="palette"/> is initially set for the new image, if a palletized + /// image of type <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> is created. + /// However, in the latter case, this function returns an image, whose + /// pixels are all initialized with zeros so, the image will be filled with the color of the + /// first palette entry. + /// </remarks> + public static FIBITMAP AllocateEx(int width, int height, int bpp, + RGBQUAD? color, FREE_IMAGE_COLOR_OPTIONS options, RGBQUAD[] palette, + uint red_mask, uint green_mask, uint blue_mask) + { + if ((palette != null) && (bpp <= 8) && (palette.Length < (1 << bpp))) + return FIBITMAP.Zero; + + if (color.HasValue) + { + GCHandle handle = new GCHandle(); + try + { + RGBQUAD[] buffer = new RGBQUAD[] { color.Value }; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + return AllocateEx(width, height, bpp, handle.AddrOfPinnedObject(), + options, palette, red_mask, green_mask, blue_mask); + } + finally + { + if (handle.IsAllocated) + handle.Free(); + } + } + else + { + return AllocateEx(width, height, bpp, IntPtr.Zero, + options, palette, red_mask, green_mask, blue_mask); + } + } + + /// <summary> + /// Allocates a new image of the specified type, width, height and bit depth and optionally + /// fills it with the specified color. See remarks for further details. + /// </summary> + /// <typeparam name="T">The type of the specified color.</typeparam> + /// <param name="type">Type of the image.</param> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap</param> + /// <param name="color">The color to fill the bitmap with or <c>null</c>.</param> + /// <param name="options">Options to enable or disable function-features.</param> + /// <param name="palette">The palette of the bitmap or <c>null</c>.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function is an extension to <see cref="AllocateT"/>, which additionally supports + /// specifying a palette to be set for the newly create image, as well as specifying a + /// background color, the newly created image should initially be filled with. + /// <para/> + /// Basically, this function internally relies on function <see cref="AllocateT"/>, followed by a + /// call to <see cref="FillBackground<T>"/>. This is why both parameters + /// <paramref name="color"/> and <paramref name="options"/> behave the same as it is + /// documented for function <see cref="FillBackground<T>"/>. So, please refer to the + /// documentation of <see cref="FillBackground<T>"/> to learn more about parameters color and options. + /// <para/> + /// The palette specified through parameter palette is only copied to the newly created + /// image, if its image type is <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> and the desired bit + /// depth is smaller than or equal to 8 bits per pixel. In other words, the <paramref name="palette"/> + /// palette is only taken into account for palletized images. However, if the preceding conditions + /// match and if <paramref name="palette"/> is not <c>null</c>, the palette is assumed to be at + /// least as large as the size of a fully populated palette for the desired bit depth. + /// So, for an 8-bit image, this length is 256, for an 4-bit image it is 16 and it is + /// 2 for a 1-bit image. In other words, this function does not support partial palettes. + /// <para/> + /// However, specifying a palette is not necesarily needed, even for palletized images. This + /// function is capable of implicitly creating a palette, if <paramref name="palette"/> is <c>null</c>. + /// If the specified background color is a greyscale value (red = green = blue) or if option + /// <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> is specified, a greyscale palette + /// is created. For a 1-bit image, only if the specified background color is either black or white, + /// a monochrome palette, consisting of black and white only is created. In any case, the darker + /// colors are stored at the smaller palette indices. + /// <para/> + /// If the specified background color is not a greyscale value, or is neither black nor white + /// for a 1-bit image, solely this specified color is injected into the otherwise black-initialized + /// palette. For this operation, option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> + /// is implicit, so the specified color is applied to the palette entry, specified by the + /// background color's <see cref="RGBQUAD.rgbReserved"/> field. The image is then filled with + /// this palette index. + /// <para/> + /// This function returns a newly created image as function <see cref="AllocateT"/> does, if both + /// parameters <paramref name="color"/> and <paramref name="palette"/> are <c>null</c>. + /// If only <paramref name="color"/> is <c>null</c>, the palette pointed to by + /// parameter <paramref name="palette"/> is initially set for the new image, if a palletized + /// image of type <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> is created. + /// However, in the latter case, this function returns an image, whose + /// pixels are all initialized with zeros so, the image will be filled with the color of the + /// first palette entry. + /// </remarks> + public static FIBITMAP AllocateExT<T>(FREE_IMAGE_TYPE type, int width, int height, int bpp, + T? color, FREE_IMAGE_COLOR_OPTIONS options, RGBQUAD[] palette) where T : struct + { + return AllocateExT(type, width, height, bpp, color, options, palette, 0, 0, 0); + } + + /// <summary> + /// Allocates a new image of the specified type, width, height and bit depth and optionally + /// fills it with the specified color. See remarks for further details. + /// </summary> + /// <typeparam name="T">The type of the specified color.</typeparam> + /// <param name="type">Type of the image.</param> + /// <param name="width">Width of the new bitmap.</param> + /// <param name="height">Height of the new bitmap.</param> + /// <param name="bpp">Bit depth of the new bitmap. + /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap</param> + /// <param name="color">The color to fill the bitmap with or <c>null</c>.</param> + /// <param name="options">Options to enable or disable function-features.</param> + /// <param name="palette">The palette of the bitmap or <c>null</c>.</param> + /// <param name="red_mask">Red part of the color layout. + /// eg: 0xFF0000</param> + /// <param name="green_mask">Green part of the color layout. + /// eg: 0x00FF00</param> + /// <param name="blue_mask">Blue part of the color layout. + /// eg: 0x0000FF</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function is an extension to <see cref="AllocateT"/>, which additionally supports + /// specifying a palette to be set for the newly create image, as well as specifying a + /// background color, the newly created image should initially be filled with. + /// <para/> + /// Basically, this function internally relies on function <see cref="AllocateT"/>, followed by a + /// call to <see cref="FillBackground<T>"/>. This is why both parameters + /// <paramref name="color"/> and <paramref name="options"/> behave the same as it is + /// documented for function <see cref="FillBackground<T>"/>. So, please refer to the + /// documentation of <see cref="FillBackground<T>"/> to learn more about parameters color and options. + /// <para/> + /// The palette specified through parameter palette is only copied to the newly created + /// image, if its image type is <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> and the desired bit + /// depth is smaller than or equal to 8 bits per pixel. In other words, the <paramref name="palette"/> + /// palette is only taken into account for palletized images. However, if the preceding conditions + /// match and if <paramref name="palette"/> is not <c>null</c>, the palette is assumed to be at + /// least as large as the size of a fully populated palette for the desired bit depth. + /// So, for an 8-bit image, this length is 256, for an 4-bit image it is 16 and it is + /// 2 for a 1-bit image. In other words, this function does not support partial palettes. + /// <para/> + /// However, specifying a palette is not necesarily needed, even for palletized images. This + /// function is capable of implicitly creating a palette, if <paramref name="palette"/> is <c>null</c>. + /// If the specified background color is a greyscale value (red = green = blue) or if option + /// <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> is specified, a greyscale palette + /// is created. For a 1-bit image, only if the specified background color is either black or white, + /// a monochrome palette, consisting of black and white only is created. In any case, the darker + /// colors are stored at the smaller palette indices. + /// <para/> + /// If the specified background color is not a greyscale value, or is neither black nor white + /// for a 1-bit image, solely this specified color is injected into the otherwise black-initialized + /// palette. For this operation, option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> + /// is implicit, so the specified color is applied to the palette entry, specified by the + /// background color's <see cref="RGBQUAD.rgbReserved"/> field. The image is then filled with + /// this palette index. + /// <para/> + /// This function returns a newly created image as function <see cref="AllocateT"/> does, if both + /// parameters <paramref name="color"/> and <paramref name="palette"/> are <c>null</c>. + /// If only <paramref name="color"/> is <c>null</c>, the palette pointed to by + /// parameter <paramref name="palette"/> is initially set for the new image, if a palletized + /// image of type <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> is created. + /// However, in the latter case, this function returns an image, whose + /// pixels are all initialized with zeros so, the image will be filled with the color of the + /// first palette entry. + /// </remarks> + public static FIBITMAP AllocateExT<T>(FREE_IMAGE_TYPE type, int width, int height, int bpp, + T? color, FREE_IMAGE_COLOR_OPTIONS options, RGBQUAD[] palette, + uint red_mask, uint green_mask, uint blue_mask) where T : struct + { + if ((palette != null) && (bpp <= 8) && (palette.Length < (1 << bpp))) + return FIBITMAP.Zero; + + if (!CheckColorType(type, color)) + return FIBITMAP.Zero; + + if (color.HasValue) + { + GCHandle handle = new GCHandle(); + try + { + T[] buffer = new T[] { color.Value }; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + return AllocateExT(type, width, height, bpp, handle.AddrOfPinnedObject(), + options, palette, red_mask, green_mask, blue_mask); + } + finally + { + if (handle.IsAllocated) + handle.Free(); + } + } + else + { + return AllocateExT(type, width, height, bpp, IntPtr.Zero, + options, palette, red_mask, green_mask, blue_mask); + } + } + + /// <summary> + /// Converts a FreeImage bitmap to a .NET <see cref="System.Drawing.Bitmap"/>. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>The converted .NET <see cref="System.Drawing.Bitmap"/>.</returns> + /// <remarks>Copying metadata has been disabled until a proper way + /// of reading and storing metadata in a .NET bitmap is found.</remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The image type of <paramref name="dib"/> is not FIT_BITMAP.</exception> + public static Bitmap GetBitmap(FIBITMAP dib) + { + return GetBitmap(dib, true); + } + + /// <summary> + /// Converts a FreeImage bitmap to a .NET <see cref="System.Drawing.Bitmap"/>. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="copyMetadata">When true existing metadata will be copied.</param> + /// <returns>The converted .NET <see cref="System.Drawing.Bitmap"/>.</returns> + /// <remarks>Copying metadata has been disabled until a proper way + /// of reading and storing metadata in a .NET bitmap is found.</remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The image type of <paramref name="dib"/> is not FIT_BITMAP.</exception> + internal static Bitmap GetBitmap(FIBITMAP dib, bool copyMetadata) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + if (GetImageType(dib) != FREE_IMAGE_TYPE.FIT_BITMAP) + { + throw new ArgumentException("Only bitmaps with type of FIT_BITMAP can be converted."); + } + + PixelFormat format = GetPixelFormat(dib); + + if ((format == PixelFormat.Undefined) && (GetBPP(dib) == 16u)) + { + throw new ArgumentException("Only 16bit 555 and 565 are supported."); + } + + int height = (int)GetHeight(dib); + int width = (int)GetWidth(dib); + int pitch = (int)GetPitch(dib); + + Bitmap result = new Bitmap(width, height, format); + BitmapData data; + // Locking the complete bitmap in writeonly mode + data = result.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, format); + // Writing the bitmap data directly into the new created .NET bitmap. + ConvertToRawBits(data.Scan0, dib, pitch, GetBPP(dib), + GetRedMask(dib), GetGreenMask(dib), GetBlueMask(dib), true); + // Unlock the bitmap + result.UnlockBits(data); + // Apply the bitmaps resolution + result.SetResolution(GetResolutionX(dib), GetResolutionY(dib)); + // Check whether the bitmap has a palette + if (GetPalette(dib) != IntPtr.Zero) + { + // Get the bitmaps palette to apply changes + ColorPalette palette = result.Palette; + // Get the orgininal palette + Color[] colorPalette = new Palette(dib).ColorData; + // Get the maximum number of palette entries to copy + int entriesToCopy = Math.Min(colorPalette.Length, palette.Entries.Length); + + // Check whether the bitmap is transparent + if (IsTransparent(dib)) + { + byte[] transTable = GetTransparencyTableEx(dib); + int i = 0; + int maxEntriesWithTrans = Math.Min(entriesToCopy, transTable.Length); + // Copy palette entries and include transparency + for (; i < maxEntriesWithTrans; i++) + { + palette.Entries[i] = Color.FromArgb(transTable[i], colorPalette[i]); + } + // Copy palette entries and that have no transparancy + for (; i < entriesToCopy; i++) + { + palette.Entries[i] = Color.FromArgb(0xFF, colorPalette[i]); + } + } + else + { + for (int i = 0; i < entriesToCopy; i++) + { + palette.Entries[i] = colorPalette[i]; + } + } + + // Set the bitmaps palette + result.Palette = palette; + } + // Copy metadata + if (copyMetadata) + { + try + { + List<PropertyItem> list = new List<PropertyItem>(); + // Get a list of all types + FITAG tag; + FIMETADATA mData; + foreach (FREE_IMAGE_MDMODEL model in FREE_IMAGE_MDMODELS) + { + // Get a unique search handle + mData = FindFirstMetadata(model, dib, out tag); + // Check if metadata exists for this type + if (mData.IsNull) continue; + do + { + PropertyItem propItem = CreatePropertyItem(); + propItem.Len = (int)GetTagLength(tag); + propItem.Id = (int)GetTagID(tag); + propItem.Type = (short)GetTagType(tag); + byte[] buffer = new byte[propItem.Len]; + + unsafe + { + byte* src = (byte*)GetTagValue(tag); + fixed (byte* dst = buffer) + { + CopyMemory(dst, src, (uint)propItem.Len); + } + } + + propItem.Value = buffer; + list.Add(propItem); + } + while (FindNextMetadata(mData, out tag)); + FindCloseMetadata(mData); + } + foreach (PropertyItem propItem in list) + { + result.SetPropertyItem(propItem); + } + } + catch + { + } + } + return result; + } + + /// <summary> + /// Converts an .NET <see cref="System.Drawing.Bitmap"/> into a FreeImage bitmap. + /// </summary> + /// <param name="bitmap">The <see cref="System.Drawing.Bitmap"/> to convert.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks>Copying metadata has been disabled until a proper way + /// of reading and storing metadata in a .NET bitmap is found.</remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="bitmap"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The bitmaps pixelformat is invalid.</exception> + public static FIBITMAP CreateFromBitmap(Bitmap bitmap) + { + return CreateFromBitmap(bitmap, false); + } + + /// <summary> + /// Converts an .NET <see cref="System.Drawing.Bitmap"/> into a FreeImage bitmap. + /// </summary> + /// <param name="bitmap">The <see cref="System.Drawing.Bitmap"/> to convert.</param> + /// <param name="copyMetadata">When true existing metadata will be copied.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks>Copying metadata has been disabled until a proper way + /// of reading and storing metadata in a .NET bitmap is found.</remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="bitmap"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The bitmaps pixelformat is invalid.</exception> + internal static FIBITMAP CreateFromBitmap(Bitmap bitmap, bool copyMetadata) + { + if (bitmap == null) + { + throw new ArgumentNullException("bitmap"); + } + uint bpp, red_mask, green_mask, blue_mask; + FREE_IMAGE_TYPE type; + if (!GetFormatParameters(bitmap.PixelFormat, out type, out bpp, out red_mask, out green_mask, out blue_mask)) + { + throw new ArgumentException("The bitmaps pixelformat is invalid."); + } + + // Locking the complete bitmap in readonly mode + BitmapData data = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); + // Copying the bitmap data directly from the .NET bitmap + FIBITMAP result = ConvertFromRawBits( + data.Scan0, + type, + data.Width, + data.Height, + data.Stride, + bpp, + red_mask, + green_mask, + blue_mask, + true); + bitmap.UnlockBits(data); + // Handle palette + if (GetPalette(result) != IntPtr.Zero) + { + Palette palette = new Palette(result); + Color[] colors = bitmap.Palette.Entries; + // Only copy available palette entries + int entriesToCopy = Math.Min(palette.Length, colors.Length); + byte[] transTable = new byte[entriesToCopy]; + for (int i = 0; i < entriesToCopy; i++) + { + RGBQUAD color = (RGBQUAD)colors[i]; + color.rgbReserved = 0x00; + palette[i] = color; + transTable[i] = colors[i].A; + } + if ((bitmap.Flags & (int)ImageFlags.HasAlpha) != 0) + { + FreeImage.SetTransparencyTable(result, transTable); + } + } + // Handle meta data + // Disabled + //if (copyMetadata) + //{ + // foreach (PropertyItem propItem in bitmap.PropertyItems) + // { + // FITAG tag = CreateTag(); + // SetTagLength(tag, (uint)propItem.Len); + // SetTagID(tag, (ushort)propItem.Id); + // SetTagType(tag, (FREE_IMAGE_MDTYPE)propItem.Type); + // SetTagValue(tag, propItem.Value); + // SetMetadata(FREE_IMAGE_MDMODEL.FIMD_EXIF_EXIF, result, "", tag); + // } + //} + return result; + } + + /// <summary> + /// Converts a raw bitmap to a FreeImage bitmap. + /// </summary> + /// <param name="bits">Array of bytes containing the raw bitmap.</param> + /// <param name="type">The type of the raw bitmap.</param> + /// <param name="width">The width in pixels of the raw bitmap.</param> + /// <param name="height">The height in pixels of the raw bitmap.</param> + /// <param name="pitch">Defines the total width of a scanline in the raw bitmap, + /// including padding bytes.</param> + /// <param name="bpp">The bit depth (bits per pixel) of the raw bitmap.</param> + /// <param name="red_mask">The bit mask describing the bits used to store a single + /// pixel's red component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="green_mask">The bit mask describing the bits used to store a single + /// pixel's green component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="blue_mask">The bit mask describing the bits used to store a single + /// pixel's blue component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="topdown">If true, the raw bitmap is stored in top-down order (top-left pixel first) + /// and in bottom-up order (bottom-left pixel first) otherwise.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static unsafe FIBITMAP ConvertFromRawBits( + byte[] bits, + FREE_IMAGE_TYPE type, + int width, + int height, + int pitch, + uint bpp, + uint red_mask, + uint green_mask, + uint blue_mask, + bool topdown) + { + fixed (byte* ptr = bits) + { + return ConvertFromRawBits( + (IntPtr)ptr, + type, + width, + height, + pitch, + bpp, + red_mask, + green_mask, + blue_mask, + topdown); + } + } + + /// <summary> + /// Converts a raw bitmap to a FreeImage bitmap. + /// </summary> + /// <param name="bits">Pointer to the memory block containing the raw bitmap.</param> + /// <param name="type">The type of the raw bitmap.</param> + /// <param name="width">The width in pixels of the raw bitmap.</param> + /// <param name="height">The height in pixels of the raw bitmap.</param> + /// <param name="pitch">Defines the total width of a scanline in the raw bitmap, + /// including padding bytes.</param> + /// <param name="bpp">The bit depth (bits per pixel) of the raw bitmap.</param> + /// <param name="red_mask">The bit mask describing the bits used to store a single + /// pixel's red component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="green_mask">The bit mask describing the bits used to store a single + /// pixel's green component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="blue_mask">The bit mask describing the bits used to store a single + /// pixel's blue component in the raw bitmap. This is only applied to 16-bpp raw bitmaps.</param> + /// <param name="topdown">If true, the raw bitmap is stored in top-down order (top-left pixel first) + /// and in bottom-up order (bottom-left pixel first) otherwise.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static unsafe FIBITMAP ConvertFromRawBits( + IntPtr bits, + FREE_IMAGE_TYPE type, + int width, + int height, + int pitch, + uint bpp, + uint red_mask, + uint green_mask, + uint blue_mask, + bool topdown) + { + byte* addr = (byte*)bits; + if ((addr == null) || (width <= 0) || (height <= 0)) + { + return FIBITMAP.Zero; + } + + FIBITMAP dib = AllocateT(type, width, height, (int)bpp, red_mask, green_mask, blue_mask); + if (dib != FIBITMAP.Zero) + { + if (topdown) + { + for (int i = height - 1; i >= 0; --i) + { + CopyMemory((byte*)GetScanLine(dib, i), addr, (int)GetLine(dib)); + addr += pitch; + } + } + else + { + for (int i = 0; i < height; ++i) + { + CopyMemory((byte*)GetScanLine(dib, i), addr, (int)GetLine(dib)); + addr += pitch; + } + } + } + return dib; + } + + /// <summary> + /// Saves a .NET <see cref="System.Drawing.Bitmap"/> to a file. + /// </summary> + /// <param name="bitmap">The .NET <see cref="System.Drawing.Bitmap"/> to save.</param> + /// <param name="filename">Name of the file to save to.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="bitmap"/> or <paramref name="filename"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The bitmaps pixelformat is invalid.</exception> + public static bool SaveBitmap(Bitmap bitmap, string filename) + { + return SaveBitmap( + bitmap, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + FREE_IMAGE_SAVE_FLAGS.DEFAULT); + } + + /// <summary> + /// Saves a .NET <see cref="System.Drawing.Bitmap"/> to a file. + /// </summary> + /// <param name="bitmap">The .NET <see cref="System.Drawing.Bitmap"/> to save.</param> + /// <param name="filename">Name of the file to save to.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="bitmap"/> or <paramref name="filename"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The bitmaps pixelformat is invalid.</exception> + public static bool SaveBitmap(Bitmap bitmap, string filename, FREE_IMAGE_SAVE_FLAGS flags) + { + return SaveBitmap( + bitmap, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + flags); + } + + /// <summary> + /// Saves a .NET <see cref="System.Drawing.Bitmap"/> to a file. + /// </summary> + /// <param name="bitmap">The .NET <see cref="System.Drawing.Bitmap"/> to save.</param> + /// <param name="filename">Name of the file to save to.</param> + /// <param name="format">Format of the bitmap. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="bitmap"/> or <paramref name="filename"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// The bitmaps pixelformat is invalid.</exception> + public static bool SaveBitmap( + Bitmap bitmap, + string filename, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags) + { + FIBITMAP dib = CreateFromBitmap(bitmap); + bool result = SaveEx(dib, filename, format, flags); + Unload(dib); + return result; + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// The file will be loaded with default loading flags. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists.</exception> + public static FIBITMAP LoadEx(string filename) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return LoadEx(filename, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// Load flags can be provided by the flags parameter. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists.</exception> + public static FIBITMAP LoadEx(string filename, FREE_IMAGE_LOAD_FLAGS flags) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return LoadEx(filename, flags, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the files + /// real format is being analysed. If no plugin can read the file, format remains + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> and 0 is returned. + /// The file will be loaded with default loading flags. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadEx it will be returned in format.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists.</exception> + public static FIBITMAP LoadEx(string filename, ref FREE_IMAGE_FORMAT format) + { + return LoadEx(filename, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the files + /// real format is being analysed. If no plugin can read the file, format remains + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> and 0 is returned. + /// Load flags can be provided by the flags parameter. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadEx it will be returned in format. + /// </param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists.</exception> + public static FIBITMAP LoadEx(string filename, FREE_IMAGE_LOAD_FLAGS flags, ref FREE_IMAGE_FORMAT format) + { + // check if file exists + if (!File.Exists(filename)) + { + throw new FileNotFoundException(filename + " could not be found."); + } + FIBITMAP dib = new FIBITMAP(); + if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + // query all plugins to see if one can read the file + format = GetFileType(filename, 0); + } + // check if the plugin is capable of loading files + if (FIFSupportsReading(format)) + { + dib = Load(format, filename, flags); + } + return dib; + } + + /// <summary> + /// Loads a .NET <see cref="System.Drawing.Bitmap"/> from a file. + /// </summary> + /// <param name="filename">Name of the file to be loaded.</param> + /// <param name="format">Format of the image. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>The loaded .NET <see cref="System.Drawing.Bitmap"/>.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists.</exception> + /// <exception cref="ArgumentException"> + /// The image type of the image is not <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/>.</exception> + public static Bitmap LoadBitmap(string filename, FREE_IMAGE_LOAD_FLAGS flags, ref FREE_IMAGE_FORMAT format) + { + FIBITMAP dib = LoadEx(filename, flags, ref format); + Bitmap result = GetBitmap(dib, true); + Unload(dib); + return result; + } + + /// <summary> + /// Deletes a previously loaded FreeImage bitmap from memory and resets the handle to 0. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + public static void UnloadEx(ref FIBITMAP dib) + { + if (!dib.IsNull) + { + Unload(dib); + dib.SetNull(); + } + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// The format is taken off the filename. + /// If no suitable format was found false will be returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx(FIBITMAP dib, string filename) + { + return SaveEx( + ref dib, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> + /// the format is taken off the filename. + /// If no suitable format was found false will be returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="format">Format of the image. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + FIBITMAP dib, + string filename, + FREE_IMAGE_FORMAT format) + { + return SaveEx( + ref dib, + filename, + format, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// The format is taken off the filename. + /// If no suitable format was found false will be returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success. + /// If the function failed and returned false, the bitmap was not unloaded.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + ref FIBITMAP dib, + string filename, + bool unloadSource) + { + return SaveEx( + ref dib, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + unloadSource); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// The format is taken off the filename. + /// If no suitable format was found false will be returned. + /// Save flags can be provided by the flags parameter. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + FIBITMAP dib, + string filename, + FREE_IMAGE_SAVE_FLAGS flags) + { + return SaveEx( + ref dib, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + flags, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// The format is taken off the filename. + /// If no suitable format was found false will be returned. + /// Save flags can be provided by the flags parameter. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success. + /// If the function failed and returned false, the bitmap was not unloaded.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + ref FIBITMAP dib, + string filename, + FREE_IMAGE_SAVE_FLAGS flags, + bool unloadSource) + { + return SaveEx( + ref dib, + filename, + FREE_IMAGE_FORMAT.FIF_UNKNOWN, + flags, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + unloadSource); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> + /// the format is taken off the filename. + /// If no suitable format was found false will be returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="format">Format of the image. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success. + /// If the function failed and returned false, the bitmap was not unloaded.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + ref FIBITMAP dib, + string filename, + FREE_IMAGE_FORMAT format, + bool unloadSource) + { + return SaveEx( + ref dib, + filename, + format, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + unloadSource); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> + /// the format is taken off the filename. + /// If no suitable format was found false will be returned. + /// Save flags can be provided by the flags parameter. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="format">Format of the image. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + FIBITMAP dib, + string filename, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags) + { + return SaveEx( + ref dib, + filename, + format, + flags, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a file. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> + /// the format is taken off the filename. + /// If no suitable format was found false will be returned. + /// Save flags can be provided by the flags parameter. + /// The bitmaps color depth can be set by 'colorDepth'. + /// If set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> a suitable color depth + /// will be taken if available. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="filename">The complete name of the file to save to. + /// The extension will be corrected if it is no valid extension for the + /// selected format or if no extension was specified.</param> + /// <param name="format">Format of the image. If the format should be taken from the + /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="colorDepth">The new color depth of the bitmap. + /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if Save should take the + /// best suitable color depth. + /// If a color depth is selected that the provided format cannot write an + /// error-message will be thrown.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success. + /// If the function failed and returned false, the bitmap was not unloaded.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentException"> + /// A direct color conversion failed.</exception> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception> + public static bool SaveEx( + ref FIBITMAP dib, + string filename, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags, + FREE_IMAGE_COLOR_DEPTH colorDepth, + bool unloadSource) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + if (filename == null) + { + throw new ArgumentNullException("filename"); + } + bool result = false; + // Gets format from filename if the format is unknown + if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + format = GetFIFFromFilename(filename); + } + if (format != FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + // Checks writing support + if (FIFSupportsWriting(format) && FIFSupportsExportType(format, GetImageType(dib))) + { + // Check valid filename and correct it if needed + if (!IsFilenameValidForFIF(format, filename)) + { + string extension = GetPrimaryExtensionFromFIF(format); + filename = Path.ChangeExtension(filename, extension); + } + + FIBITMAP dibToSave = PrepareBitmapColorDepth(dib, format, colorDepth); + try + { + result = Save(format, dibToSave, filename, flags); + } + finally + { + // Always unload a temporary created bitmap. + if (dibToSave != dib) + { + UnloadEx(ref dibToSave); + } + // On success unload the bitmap + if (result && unloadSource) + { + UnloadEx(ref dib); + } + } + } + } + return result; + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// The stream must be set to the correct position before calling LoadFromStream. + /// </summary> + /// <param name="stream">The stream to read from.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> is not capable of reading.</exception> + public static FIBITMAP LoadFromStream(Stream stream) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return LoadFromStream(stream, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// The stream must be set to the correct position before calling LoadFromStream. + /// </summary> + /// <param name="stream">The stream to read from.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> is not capable of reading.</exception> + public static FIBITMAP LoadFromStream(Stream stream, FREE_IMAGE_LOAD_FLAGS flags) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return LoadFromStream(stream, flags, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the + /// bitmaps real format is being analysed. + /// The stream must be set to the correct position before calling LoadFromStream. + /// </summary> + /// <param name="stream">The stream to read from.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadFromStream it will be returned in format.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> is not capable of reading.</exception> + public static FIBITMAP LoadFromStream(Stream stream, ref FREE_IMAGE_FORMAT format) + { + return LoadFromStream(stream, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); + } + + /// <summary> + /// Loads a FreeImage bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> + /// the bitmaps real format is being analysed. + /// The stream must be set to the correct position before calling LoadFromStream. + /// </summary> + /// <param name="stream">The stream to read from.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadFromStream it will be returned in format.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> is not capable of reading.</exception> + public static FIBITMAP LoadFromStream( + Stream stream, + FREE_IMAGE_LOAD_FLAGS flags, + ref FREE_IMAGE_FORMAT format) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + if (!stream.CanRead) + { + throw new ArgumentException("stream is not capable of reading."); + } + // Wrap the source stream if it is unable to seek (which is required by FreeImage) + stream = (stream.CanSeek) ? stream : new StreamWrapper(stream, true); + + stream.Position = 0L; + if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + // Get the format of the bitmap + format = GetFileTypeFromStream(stream); + // Restore the streams position + stream.Position = 0L; + } + if (!FIFSupportsReading(format)) + { + return FIBITMAP.Zero; + } + // Create a 'FreeImageIO' structure for calling 'LoadFromHandle' + // using the internal structure 'FreeImageStreamIO'. + FreeImageIO io = FreeImageStreamIO.io; + using (fi_handle handle = new fi_handle(stream)) + { + return LoadFromHandle(format, ref io, handle, flags); + } + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format) + { + return SaveToStream( + ref dib, + stream, + format, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + ref FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format, + bool unloadSource) + { + return SaveToStream( + ref dib, + stream, + format, + FREE_IMAGE_SAVE_FLAGS.DEFAULT, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + unloadSource); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags) + { + return SaveToStream( + ref dib, + stream, + format, + flags, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + ref FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags, + bool unloadSource) + { + return SaveToStream( + ref dib, stream, + format, + flags, + FREE_IMAGE_COLOR_DEPTH.FICD_AUTO, + unloadSource); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="colorDepth">The new color depth of the bitmap. + /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if SaveToStream should + /// take the best suitable color depth. + /// If a color depth is selected that the provided format cannot write an + /// error-message will be thrown.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags, + FREE_IMAGE_COLOR_DEPTH colorDepth) + { + return SaveToStream( + ref dib, + stream, + format, + flags, + colorDepth, + false); + } + + /// <summary> + /// Saves a previously loaded FreeImage bitmap to a stream. + /// The stream must be set to the correct position before calling SaveToStream. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="stream">The stream to write to.</param> + /// <param name="format">Format of the image.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="colorDepth">The new color depth of the bitmap. + /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if SaveToStream should + /// take the best suitable color depth. + /// If a color depth is selected that the provided format cannot write an + /// error-message will be thrown.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> cannot write.</exception> + public static bool SaveToStream( + ref FIBITMAP dib, + Stream stream, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_SAVE_FLAGS flags, + FREE_IMAGE_COLOR_DEPTH colorDepth, + bool unloadSource) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + if (!stream.CanWrite) + { + throw new ArgumentException("stream is not capable of writing."); + } + if ((!FIFSupportsWriting(format)) || (!FIFSupportsExportType(format, GetImageType(dib)))) + { + return false; + } + + FIBITMAP dibToSave = PrepareBitmapColorDepth(dib, format, colorDepth); + bool result = false; + + try + { + // Create a 'FreeImageIO' structure for calling 'SaveToHandle' + FreeImageIO io = FreeImageStreamIO.io; + + using (fi_handle handle = new fi_handle(stream)) + { + result = SaveToHandle(format, dibToSave, ref io, handle, flags); + } + } + finally + { + // Always unload a temporary created bitmap. + if (dibToSave != dib) + { + UnloadEx(ref dibToSave); + } + // On success unload the bitmap + if (result && unloadSource) + { + UnloadEx(ref dib); + } + } + + return result; + } + + #endregion + + #region Plugin functions + + /// <summary> + /// Checks if an extension is valid for a certain format. + /// </summary> + /// <param name="fif">The desired format.</param> + /// <param name="extension">The desired extension.</param> + /// <returns>True if the extension is valid for the given format, false otherwise.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="extension"/> is null.</exception> + public static bool IsExtensionValidForFIF(FREE_IMAGE_FORMAT fif, string extension) + { + return IsExtensionValidForFIF(fif, extension, StringComparison.CurrentCultureIgnoreCase); + } + + /// <summary> + /// Checks if an extension is valid for a certain format. + /// </summary> + /// <param name="fif">The desired format.</param> + /// <param name="extension">The desired extension.</param> + /// <param name="comparisonType">The string comparison type.</param> + /// <returns>True if the extension is valid for the given format, false otherwise.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="extension"/> is null.</exception> + public static bool IsExtensionValidForFIF(FREE_IMAGE_FORMAT fif, string extension, StringComparison comparisonType) + { + if (extension == null) + { + throw new ArgumentNullException("extension"); + } + bool result = false; + // Split up the string and compare each with the given extension + string tempList = GetFIFExtensionList(fif); + if (tempList != null) + { + string[] extensionList = tempList.Split(','); + foreach (string ext in extensionList) + { + if (extension.Equals(ext, comparisonType)) + { + result = true; + break; + } + } + } + return result; + } + + /// <summary> + /// Checks if a filename is valid for a certain format. + /// </summary> + /// <param name="fif">The desired format.</param> + /// <param name="filename">The desired filename.</param> + /// <returns>True if the filename is valid for the given format, false otherwise.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="filename"/> is null.</exception> + public static bool IsFilenameValidForFIF(FREE_IMAGE_FORMAT fif, string filename) + { + return IsFilenameValidForFIF(fif, filename, StringComparison.CurrentCultureIgnoreCase); + } + + /// <summary> + /// Checks if a filename is valid for a certain format. + /// </summary> + /// <param name="fif">The desired format.</param> + /// <param name="filename">The desired filename.</param> + /// <param name="comparisonType">The string comparison type.</param> + /// <returns>True if the filename is valid for the given format, false otherwise.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="filename"/> is null.</exception> + public static bool IsFilenameValidForFIF(FREE_IMAGE_FORMAT fif, string filename, StringComparison comparisonType) + { + if (filename == null) + { + throw new ArgumentNullException("filename"); + } + bool result = false; + // Extract the filenames extension if it exists + string extension = Path.GetExtension(filename); + if (extension.Length != 0) + { + extension = extension.Remove(0, 1); + result = IsExtensionValidForFIF(fif, extension, comparisonType); + } + return result; + } + + /// <summary> + /// This function returns the primary (main or most commonly used?) extension of a certain + /// image format (fif). This is done by returning the first of all possible extensions + /// returned by GetFIFExtensionList(). + /// That assumes, that the plugin returns the extensions in ordered form.</summary> + /// <param name="fif">The image format to obtain the primary extension for.</param> + /// <returns>The primary extension of the specified image format.</returns> + public static string GetPrimaryExtensionFromFIF(FREE_IMAGE_FORMAT fif) + { + string result = null; + string extensions = GetFIFExtensionList(fif); + if (extensions != null) + { + int position = extensions.IndexOf(','); + if (position < 0) + { + result = extensions; + } + else + { + result = extensions.Substring(0, position); + } + } + return result; + } + + #endregion + + #region Multipage functions + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx(string filename) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return OpenMultiBitmapEx( + filename, + ref format, + FREE_IMAGE_LOAD_FLAGS.DEFAULT, + false, + false, + false); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="keep_cache_in_memory">When true performance is increased at the cost of memory.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx(string filename, bool keep_cache_in_memory) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return OpenMultiBitmapEx( + filename, + ref format, + FREE_IMAGE_LOAD_FLAGS.DEFAULT, + false, + false, + keep_cache_in_memory); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="read_only">When true the bitmap will be loaded read only.</param> + /// <param name="keep_cache_in_memory">When true performance is increased at the cost of memory.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx( + string filename, + bool read_only, + bool keep_cache_in_memory) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return OpenMultiBitmapEx( + filename, + ref format, + FREE_IMAGE_LOAD_FLAGS.DEFAULT, + false, + read_only, + keep_cache_in_memory); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="create_new">When true a new bitmap is created.</param> + /// <param name="read_only">When true the bitmap will be loaded read only.</param> + /// <param name="keep_cache_in_memory">When true performance is increased at the cost of memory.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx( + string filename, + bool create_new, + bool read_only, + bool keep_cache_in_memory) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return OpenMultiBitmapEx( + filename, + ref format, + FREE_IMAGE_LOAD_FLAGS.DEFAULT, + create_new, + read_only, + keep_cache_in_memory); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the files real + /// format is being analysed. If no plugin can read the file, format remains + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> and 0 is returned. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadEx it will be returned in format.</param> + /// <param name="create_new">When true a new bitmap is created.</param> + /// <param name="read_only">When true the bitmap will be loaded read only.</param> + /// <param name="keep_cache_in_memory">When true performance is increased at the cost of memory.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx( + string filename, + ref FREE_IMAGE_FORMAT format, + bool create_new, + bool read_only, + bool keep_cache_in_memory) + { + return OpenMultiBitmapEx( + filename, + ref format, + FREE_IMAGE_LOAD_FLAGS.DEFAULT, + create_new, + read_only, + keep_cache_in_memory); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the files + /// real format is being analysed. If no plugin can read the file, format remains + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> and 0 is returned. + /// Load flags can be provided by the flags parameter. + /// </summary> + /// <param name="filename">The complete name of the file to load.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>. + /// In case a suitable format was found by LoadEx it will be returned in format.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="create_new">When true a new bitmap is created.</param> + /// <param name="read_only">When true the bitmap will be loaded read only.</param> + /// <param name="keep_cache_in_memory">When true performance is increased at the cost of memory.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="FileNotFoundException"> + /// <paramref name="filename"/> does not exists while opening.</exception> + public static FIMULTIBITMAP OpenMultiBitmapEx( + string filename, + ref FREE_IMAGE_FORMAT format, + FREE_IMAGE_LOAD_FLAGS flags, + bool create_new, + bool read_only, + bool keep_cache_in_memory) + { + if (!File.Exists(filename) && !create_new) + { + throw new FileNotFoundException(filename + " could not be found."); + } + if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + // Check if a plugin can read the data + format = GetFileType(filename, 0); + } + FIMULTIBITMAP dib = new FIMULTIBITMAP(); + if (FIFSupportsReading(format)) + { + dib = OpenMultiBitmap(format, filename, create_new, read_only, keep_cache_in_memory, flags); + } + return dib; + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// </summary> + /// <param name="stream">The stream to load the bitmap from.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + public static FIMULTIBITMAP OpenMultiBitmapFromStream(Stream stream) + { + FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; + return OpenMultiBitmapFromStream(stream, ref format, FREE_IMAGE_LOAD_FLAGS.DEFAULT); + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap. + /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> the files + /// real format is being analysed. If no plugin can read the file, format remains + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/> and 0 is returned. + /// Load flags can be provided by the flags parameter. + /// </summary> + /// <param name="stream">The stream to load the bitmap from.</param> + /// <param name="format">Format of the image. If the format is unknown use + /// <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/></param>. + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + public static FIMULTIBITMAP OpenMultiBitmapFromStream(Stream stream, ref FREE_IMAGE_FORMAT format, FREE_IMAGE_LOAD_FLAGS flags) + { + if (stream == null) + return FIMULTIBITMAP.Zero; + + if (!stream.CanSeek) + stream = new StreamWrapper(stream, true); + + FIMULTIBITMAP mdib = FIMULTIBITMAP.Zero; + FreeImageIO io = FreeImageStreamIO.io; + fi_handle handle = new fi_handle(stream); + + try + { + if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN) + { + format = GetFileTypeFromHandle(ref io, handle, checked((int)stream.Length)); + } + + mdib = OpenMultiBitmapFromHandle(format, ref io, handle, flags); + + if (mdib.IsNull) + { + handle.Dispose(); + } + else + { + streamHandles.Add(mdib, handle); + } + + return mdib; + } + catch + { + if (!mdib.IsNull) + CloseMultiBitmap(mdib, FREE_IMAGE_SAVE_FLAGS.DEFAULT); + + if (handle != null) + handle.Dispose(); + + throw; + } + } + + /// <summary> + /// Closes a previously opened multi-page bitmap and, when the bitmap was not opened read-only, applies any changes made to it. + /// </summary> + /// <param name="bitmap">Handle to a FreeImage multi-paged bitmap.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + public static bool CloseMultiBitmap(FIMULTIBITMAP bitmap, FREE_IMAGE_SAVE_FLAGS flags) + { + if (CloseMultiBitmap_(bitmap, flags)) + { + fi_handle handle; + if (streamHandles.TryGetValue(bitmap, out handle)) + { + streamHandles.Remove(bitmap); + handle.Dispose(); + } + return true; + } + return false; + } + + /// <summary> + /// Closes a previously opened multi-page bitmap and, when the bitmap was not opened read-only, + /// applies any changes made to it. + /// On success the handle will be reset to null. + /// </summary> + /// <param name="bitmap">Handle to a FreeImage multi-paged bitmap.</param> + /// <returns>Returns true on success, false on failure.</returns> + public static bool CloseMultiBitmapEx(ref FIMULTIBITMAP bitmap) + { + return CloseMultiBitmapEx(ref bitmap, FREE_IMAGE_SAVE_FLAGS.DEFAULT); + } + + /// <summary> + /// Closes a previously opened multi-page bitmap and, when the bitmap was not opened read-only, + /// applies any changes made to it. + /// On success the handle will be reset to null. + /// </summary> + /// <param name="bitmap">Handle to a FreeImage multi-paged bitmap.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <returns>Returns true on success, false on failure.</returns> + public static bool CloseMultiBitmapEx(ref FIMULTIBITMAP bitmap, FREE_IMAGE_SAVE_FLAGS flags) + { + bool result = false; + if (!bitmap.IsNull) + { + if (CloseMultiBitmap(bitmap, flags)) + { + bitmap.SetNull(); + result = true; + } + } + return result; + } + + /// <summary> + /// Retrieves the number of pages that are locked in a multi-paged bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage multi-paged bitmap.</param> + /// <returns>Number of locked pages.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static int GetLockedPageCount(FIMULTIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + int result = 0; + GetLockedPageNumbers(dib, null, ref result); + return result; + } + + /// <summary> + /// Retrieves a list locked pages of a multi-paged bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage multi-paged bitmap.</param> + /// <returns>List containing the indexes of the locked pages.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static int[] GetLockedPages(FIMULTIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + // Get the number of pages and create an array to save the information + int count = 0; + int[] result = null; + // Get count + if (GetLockedPageNumbers(dib, result, ref count)) + { + result = new int[count]; + // Fill array + if (!GetLockedPageNumbers(dib, result, ref count)) + { + result = null; + } + } + return result; + } + + /// <summary> + /// Loads a FreeImage multi-paged bitmap from a stream and returns the + /// FreeImage memory stream used as temporary buffer. + /// The bitmap can not be modified by calling + /// <see cref="FreeImage.AppendPage(FIMULTIBITMAP,FIBITMAP)"/>, + /// <see cref="FreeImage.InsertPage(FIMULTIBITMAP,Int32,FIBITMAP)"/>, + /// <see cref="FreeImage.MovePage(FIMULTIBITMAP,Int32,Int32)"/> or + /// <see cref="FreeImage.DeletePage(FIMULTIBITMAP,Int32)"/>. + /// </summary> + /// <param name="stream">The stream to read from.</param> + /// <param name="format">Format of the image.</param> + /// <param name="flags">Flags to enable or disable plugin-features.</param> + /// <param name="memory">The temporary memory buffer used to load the bitmap.</param> + /// <returns>Handle to a FreeImage multi-paged bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> can not read.</exception> + public static FIMULTIBITMAP LoadMultiBitmapFromStream( + Stream stream, + FREE_IMAGE_FORMAT format, + FREE_IMAGE_LOAD_FLAGS flags, + out FIMEMORY memory) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + if (!stream.CanRead) + { + throw new ArgumentException("stream"); + } + const int blockSize = 1024; + int bytesRead; + byte[] buffer = new byte[blockSize]; + + stream = stream.CanSeek ? stream : new StreamWrapper(stream, true); + memory = OpenMemory(IntPtr.Zero, 0); + + do + { + bytesRead = stream.Read(buffer, 0, blockSize); + WriteMemory(buffer, (uint)blockSize, (uint)1, memory); + } + while (bytesRead == blockSize); + + return LoadMultiBitmapFromMemory(format, memory, flags); + } + + #endregion + + #region Filetype functions + + /// <summary> + /// Orders FreeImage to analyze the bitmap signature. + /// In case the stream is not seekable, the stream will have been used + /// and must be recreated for loading. + /// </summary> + /// <param name="stream">Name of the stream to analyze.</param> + /// <returns>Type of the bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="stream"/> is null.</exception> + /// <exception cref="ArgumentException"> + /// <paramref name="stream"/> can not read.</exception> + public static FREE_IMAGE_FORMAT GetFileTypeFromStream(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + if (!stream.CanRead) + { + throw new ArgumentException("stream is not capable of reading."); + } + // Wrap the stream if it cannot seek + stream = (stream.CanSeek) ? stream : new StreamWrapper(stream, true); + // Create a 'FreeImageIO' structure for the stream + FreeImageIO io = FreeImageStreamIO.io; + using (fi_handle handle = new fi_handle(stream)) + { + return GetFileTypeFromHandle(ref io, handle, 0); + } + } + + #endregion + + #region Pixel access functions + + /// <summary> + /// Retrieves an hBitmap for a FreeImage bitmap. + /// Call FreeHbitmap(IntPtr) to free the handle. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="hdc">A reference device context. + /// Use IntPtr.Zero if no reference is available.</param> + /// <param name="unload">When true dib will be unloaded if the function succeeded.</param> + /// <returns>The hBitmap for the FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe IntPtr GetHbitmap(FIBITMAP dib, IntPtr hdc, bool unload) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + IntPtr hBitmap = IntPtr.Zero; + bool release = false; + IntPtr ppvBits = IntPtr.Zero; + // Check if we have destination + if (release = (hdc == IntPtr.Zero)) + { + // We don't so request dc + hdc = GetDC(IntPtr.Zero); + } + if (hdc != IntPtr.Zero) + { + // Get pointer to the infoheader of the bitmap + IntPtr info = GetInfo(dib); + // Create a bitmap in the dc + hBitmap = CreateDIBSection(hdc, info, DIB_RGB_COLORS, out ppvBits, IntPtr.Zero, 0); + if (hBitmap != IntPtr.Zero && ppvBits != IntPtr.Zero) + { + // Copy the data into the dc + CopyMemory(ppvBits, GetBits(dib), (GetHeight(dib) * GetPitch(dib))); + // Success: we unload the bitmap + if (unload) + { + Unload(dib); + } + } + // We have to release the dc + if (release) + { + ReleaseDC(IntPtr.Zero, hdc); + } + } + return hBitmap; + } + + /// <summary> + /// Returns an HBITMAP created by the <c>CreateDIBitmap()</c> function which in turn + /// has always the same color depth as the reference DC, which may be provided + /// through <paramref name="hdc"/>. The desktop DC will be used, + /// if <c>IntPtr.Zero</c> DC is specified. + /// Call <see cref="FreeImage.FreeHbitmap(IntPtr)"/> to free the handle. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="hdc">Handle to a device context.</param> + /// <param name="unload">When true the structure will be unloaded on success. + /// If the function failed and returned false, the bitmap was not unloaded.</param> + /// <returns>If the function succeeds, the return value is a handle to the + /// compatible bitmap. If the function fails, the return value is <see cref="IntPtr.Zero"/>.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static IntPtr GetBitmapForDevice(FIBITMAP dib, IntPtr hdc, bool unload) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + IntPtr hbitmap = IntPtr.Zero; + bool release = false; + if (release = (hdc == IntPtr.Zero)) + { + hdc = GetDC(IntPtr.Zero); + } + if (hdc != IntPtr.Zero) + { + hbitmap = CreateDIBitmap( + hdc, + GetInfoHeader(dib), + CBM_INIT, + GetBits(dib), + GetInfo(dib), + DIB_RGB_COLORS); + if (unload) + { + Unload(dib); + } + if (release) + { + ReleaseDC(IntPtr.Zero, hdc); + } + } + return hbitmap; + } + + /// <summary> + /// Creates a FreeImage DIB from a Device Context/Compatible Bitmap. + /// </summary> + /// <param name="hbitmap">Handle to the bitmap.</param> + /// <param name="hdc">Handle to a device context.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="hbitmap"/> is null.</exception> + public unsafe static FIBITMAP CreateFromHbitmap(IntPtr hbitmap, IntPtr hdc) + { + if (hbitmap == IntPtr.Zero) + { + throw new ArgumentNullException("hbitmap"); + } + + FIBITMAP dib = new FIBITMAP(); + BITMAP bm; + uint colors; + bool release; + + if (GetObject(hbitmap, sizeof(BITMAP), (IntPtr)(&bm)) != 0) + { + dib = Allocate(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0, 0, 0); + if (!dib.IsNull) + { + colors = GetColorsUsed(dib); + if (release = (hdc == IntPtr.Zero)) + { + hdc = GetDC(IntPtr.Zero); + } + if (GetDIBits( + hdc, + hbitmap, + 0, + (uint)bm.bmHeight, + GetBits(dib), + GetInfo(dib), + DIB_RGB_COLORS) != 0) + { + if (colors != 0) + { + BITMAPINFOHEADER* bmih = (BITMAPINFOHEADER*)GetInfo(dib); + bmih[0].biClrImportant = bmih[0].biClrUsed = colors; + } + } + else + { + UnloadEx(ref dib); + } + if (release) + { + ReleaseDC(IntPtr.Zero, hdc); + } + } + } + + return dib; + } + + /// <summary> + /// Frees a bitmap handle. + /// </summary> + /// <param name="hbitmap">Handle to a bitmap.</param> + /// <returns>True on success, false on failure.</returns> + public static bool FreeHbitmap(IntPtr hbitmap) + { + return DeleteObject(hbitmap); + } + + #endregion + + #region Bitmap information functions + + /// <summary> + /// Retrieves a DIB's resolution in X-direction measured in 'dots per inch' (DPI) and not in + /// 'dots per meter'. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>The resolution in 'dots per inch'.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static uint GetResolutionX(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + return (uint)(0.5d + 0.0254d * GetDotsPerMeterX(dib)); + } + + /// <summary> + /// Retrieves a DIB's resolution in Y-direction measured in 'dots per inch' (DPI) and not in + /// 'dots per meter'. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>The resolution in 'dots per inch'.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static uint GetResolutionY(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + return (uint)(0.5d + 0.0254d * GetDotsPerMeterY(dib)); + } + + /// <summary> + /// Sets a DIB's resolution in X-direction measured in 'dots per inch' (DPI) and not in + /// 'dots per meter'. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="res">The new resolution in 'dots per inch'.</param> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static void SetResolutionX(FIBITMAP dib, uint res) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + SetDotsPerMeterX(dib, (uint)((double)res / 0.0254d + 0.5d)); + } + + /// <summary> + /// Sets a DIB's resolution in Y-direction measured in 'dots per inch' (DPI) and not in + /// 'dots per meter'. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="res">The new resolution in 'dots per inch'.</param> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static void SetResolutionY(FIBITMAP dib, uint res) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + SetDotsPerMeterY(dib, (uint)((double)res / 0.0254d + 0.5d)); + } + + /// <summary> + /// Returns whether the image is a greyscale image or not. + /// The function scans all colors in the bitmaps palette for entries where + /// red, green and blue are not all the same (not a grey color). + /// Supports 1-, 4- and 8-bit bitmaps. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>True if the image is a greyscale image, else false.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe bool IsGreyscaleImage(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + bool result = true; + uint bpp = GetBPP(dib); + switch (bpp) + { + case 1: + case 4: + case 8: + RGBQUAD* palette = (RGBQUAD*)GetPalette(dib); + uint paletteLength = GetColorsUsed(dib); + for (int i = 0; i < paletteLength; i++) + { + if (palette[i].rgbRed != palette[i].rgbGreen || + palette[i].rgbRed != palette[i].rgbBlue) + { + result = false; + break; + } + } + break; + default: + result = false; + break; + } + return result; + } + + /// <summary> + /// Returns a structure that represents the palette of a FreeImage bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>A structure representing the bitmaps palette.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static Palette GetPaletteEx(FIBITMAP dib) + { + return new Palette(dib); + } + + /// <summary> + /// Returns the <see cref="BITMAPINFOHEADER"/> structure of a FreeImage bitmap. + /// The structure is a copy, so changes will have no effect on + /// the bitmap itself. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns><see cref="BITMAPINFOHEADER"/> structure of the bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe BITMAPINFOHEADER GetInfoHeaderEx(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + return *(BITMAPINFOHEADER*)GetInfoHeader(dib); + } + + /// <summary> + /// Returns the <see cref="BITMAPINFO"/> structure of a FreeImage bitmap. + /// The structure is a copy, so changes will have no effect on + /// the bitmap itself. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns><see cref="BITMAPINFO"/> structure of the bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static BITMAPINFO GetInfoEx(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + BITMAPINFO result = new BITMAPINFO(); + result.bmiHeader = GetInfoHeaderEx(dib); + IntPtr ptr = GetPalette(dib); + if (ptr == IntPtr.Zero) + { + result.bmiColors = new RGBQUAD[0]; + } + else + { + result.bmiColors = new MemoryArray<RGBQUAD>(ptr, (int)result.bmiHeader.biClrUsed).Data; + } + return result; + } + + /// <summary> + /// Returns the pixelformat of the bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns><see cref="System.Drawing.Imaging.PixelFormat"/> of the bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static PixelFormat GetPixelFormat(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + + PixelFormat result = PixelFormat.Undefined; + + if (GetImageType(dib) == FREE_IMAGE_TYPE.FIT_BITMAP) + { + switch (GetBPP(dib)) + { + case 1: + result = PixelFormat.Format1bppIndexed; + break; + case 4: + result = PixelFormat.Format4bppIndexed; + break; + case 8: + result = PixelFormat.Format8bppIndexed; + break; + case 16: + if ((GetBlueMask(dib) == FI16_565_BLUE_MASK) && + (GetGreenMask(dib) == FI16_565_GREEN_MASK) && + (GetRedMask(dib) == FI16_565_RED_MASK)) + { + result = PixelFormat.Format16bppRgb565; + } + if ((GetBlueMask(dib) == FI16_555_BLUE_MASK) && + (GetGreenMask(dib) == FI16_555_GREEN_MASK) && + (GetRedMask(dib) == FI16_555_RED_MASK)) + { + result = PixelFormat.Format16bppRgb555; + } + break; + case 24: + result = PixelFormat.Format24bppRgb; + break; + case 32: + result = PixelFormat.Format32bppArgb; + break; + } + } + return result; + } + + /// <summary> + /// Retrieves all parameters needed to create a new FreeImage bitmap from + /// the format of a .NET <see cref="System.Drawing.Image"/>. + /// </summary> + /// <param name="format">The <see cref="System.Drawing.Imaging.PixelFormat"/> + /// of the .NET <see cref="System.Drawing.Image"/>.</param> + /// <param name="type">Returns the type used for the new bitmap.</param> + /// <param name="bpp">Returns the color depth for the new bitmap.</param> + /// <param name="red_mask">Returns the red_mask for the new bitmap.</param> + /// <param name="green_mask">Returns the green_mask for the new bitmap.</param> + /// <param name="blue_mask">Returns the blue_mask for the new bitmap.</param> + /// <returns>True in case a matching conversion exists; else false. + /// </returns> + public static bool GetFormatParameters( + PixelFormat format, + out FREE_IMAGE_TYPE type, + out uint bpp, + out uint red_mask, + out uint green_mask, + out uint blue_mask) + { + bool result = false; + type = FREE_IMAGE_TYPE.FIT_UNKNOWN; + bpp = 0; + red_mask = 0; + green_mask = 0; + blue_mask = 0; + switch (format) + { + case PixelFormat.Format1bppIndexed: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 1; + result = true; + break; + case PixelFormat.Format4bppIndexed: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 4; + result = true; + break; + case PixelFormat.Format8bppIndexed: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 8; + result = true; + break; + case PixelFormat.Format16bppRgb565: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 16; + red_mask = FI16_565_RED_MASK; + green_mask = FI16_565_GREEN_MASK; + blue_mask = FI16_565_BLUE_MASK; + result = true; + break; + case PixelFormat.Format16bppRgb555: + case PixelFormat.Format16bppArgb1555: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 16; + red_mask = FI16_555_RED_MASK; + green_mask = FI16_555_GREEN_MASK; + blue_mask = FI16_555_BLUE_MASK; + result = true; + break; + case PixelFormat.Format24bppRgb: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 24; + red_mask = FI_RGBA_RED_MASK; + green_mask = FI_RGBA_GREEN_MASK; + blue_mask = FI_RGBA_BLUE_MASK; + result = true; + break; + case PixelFormat.Format32bppRgb: + case PixelFormat.Format32bppArgb: + case PixelFormat.Format32bppPArgb: + type = FREE_IMAGE_TYPE.FIT_BITMAP; + bpp = 32; + red_mask = FI_RGBA_RED_MASK; + green_mask = FI_RGBA_GREEN_MASK; + blue_mask = FI_RGBA_BLUE_MASK; + result = true; + break; + case PixelFormat.Format16bppGrayScale: + type = FREE_IMAGE_TYPE.FIT_UINT16; + bpp = 16; + result = true; + break; + case PixelFormat.Format48bppRgb: + type = FREE_IMAGE_TYPE.FIT_RGB16; + bpp = 48; + result = true; + break; + case PixelFormat.Format64bppArgb: + case PixelFormat.Format64bppPArgb: + type = FREE_IMAGE_TYPE.FIT_RGBA16; + bpp = 64; + result = true; + break; + } + return result; + } + + /// <summary> + /// Returns the <see cref="FREE_IMAGE_FORMAT"/> for the specified + /// <see cref="ImageFormat"/>. + /// </summary> + /// <param name="imageFormat">The <see cref="ImageFormat"/> + /// for which to return the corresponding <see cref="FREE_IMAGE_FORMAT"/>.</param> + /// <returns>The <see cref="FREE_IMAGE_FORMAT"/> for the specified + /// <see cref="ImageFormat"/></returns> + public static FREE_IMAGE_FORMAT GetFormat(ImageFormat imageFormat) + { + if (imageFormat != null) + { + if (imageFormat.Equals(ImageFormat.Bmp)) + return FREE_IMAGE_FORMAT.FIF_BMP; + if (imageFormat.Equals(ImageFormat.Gif)) + return FREE_IMAGE_FORMAT.FIF_GIF; + if (imageFormat.Equals(ImageFormat.Icon)) + return FREE_IMAGE_FORMAT.FIF_ICO; + if (imageFormat.Equals(ImageFormat.Jpeg)) + return FREE_IMAGE_FORMAT.FIF_JPEG; + if (imageFormat.Equals(ImageFormat.Png)) + return FREE_IMAGE_FORMAT.FIF_PNG; + if (imageFormat.Equals(ImageFormat.Tiff)) + return FREE_IMAGE_FORMAT.FIF_TIFF; + } + return FREE_IMAGE_FORMAT.FIF_UNKNOWN; + } + + /// <summary> + /// Retrieves all parameters needed to create a new FreeImage bitmap from + /// raw bits <see cref="System.Drawing.Image"/>. + /// </summary> + /// <param name="type">The <see cref="FREE_IMAGE_TYPE"/> + /// of the data in memory.</param> + /// <param name="bpp">The color depth for the data.</param> + /// <param name="red_mask">Returns the red_mask for the data.</param> + /// <param name="green_mask">Returns the green_mask for the data.</param> + /// <param name="blue_mask">Returns the blue_mask for the data.</param> + /// <returns>True in case a matching conversion exists; else false. + /// </returns> + public static bool GetTypeParameters( + FREE_IMAGE_TYPE type, + int bpp, + out uint red_mask, + out uint green_mask, + out uint blue_mask) + { + bool result = false; + red_mask = 0; + green_mask = 0; + blue_mask = 0; + switch (type) + { + case FREE_IMAGE_TYPE.FIT_BITMAP: + switch (bpp) + { + case 1: + case 4: + case 8: + result = true; + break; + case 16: + result = true; + red_mask = FI16_555_RED_MASK; + green_mask = FI16_555_GREEN_MASK; + blue_mask = FI16_555_BLUE_MASK; + break; + case 24: + case 32: + result = true; + red_mask = FI_RGBA_RED_MASK; + green_mask = FI_RGBA_GREEN_MASK; + blue_mask = FI_RGBA_BLUE_MASK; + break; + } + break; + case FREE_IMAGE_TYPE.FIT_UNKNOWN: + break; + default: + result = true; + break; + } + return result; + } + + /// <summary> + /// Compares two FreeImage bitmaps. + /// </summary> + /// <param name="dib1">The first bitmap to compare.</param> + /// <param name="dib2">The second bitmap to compare.</param> + /// <param name="flags">Determines which components of the bitmaps will be compared.</param> + /// <returns>True in case both bitmaps match the compare conditions, false otherwise.</returns> + public static bool Compare(FIBITMAP dib1, FIBITMAP dib2, FREE_IMAGE_COMPARE_FLAGS flags) + { + // Check whether one bitmap is null + if (dib1.IsNull ^ dib2.IsNull) + { + return false; + } + // Check whether both pointers are the same + if (dib1 == dib2) + { + return true; + } + if (((flags & FREE_IMAGE_COMPARE_FLAGS.HEADER) > 0) && (!CompareHeader(dib1, dib2))) + { + return false; + } + if (((flags & FREE_IMAGE_COMPARE_FLAGS.PALETTE) > 0) && (!ComparePalette(dib1, dib2))) + { + return false; + } + if (((flags & FREE_IMAGE_COMPARE_FLAGS.DATA) > 0) && (!CompareData(dib1, dib2))) + { + return false; + } + if (((flags & FREE_IMAGE_COMPARE_FLAGS.METADATA) > 0) && (!CompareMetadata(dib1, dib2))) + { + return false; + } + return true; + } + + private static unsafe bool CompareHeader(FIBITMAP dib1, FIBITMAP dib2) + { + IntPtr i1 = GetInfoHeader(dib1); + IntPtr i2 = GetInfoHeader(dib2); + return CompareMemory((void*)i1, (void*)i2, sizeof(BITMAPINFOHEADER)); + } + + private static unsafe bool ComparePalette(FIBITMAP dib1, FIBITMAP dib2) + { + IntPtr pal1 = GetPalette(dib1), pal2 = GetPalette(dib2); + bool hasPalette1 = pal1 != IntPtr.Zero; + bool hasPalette2 = pal2 != IntPtr.Zero; + if (hasPalette1 ^ hasPalette2) + { + return false; + } + if (!hasPalette1) + { + return true; + } + uint colors = GetColorsUsed(dib1); + if (colors != GetColorsUsed(dib2)) + { + return false; + } + return CompareMemory((void*)pal1, (void*)pal2, sizeof(RGBQUAD) * colors); + } + + private static unsafe bool CompareData(FIBITMAP dib1, FIBITMAP dib2) + { + uint width = GetWidth(dib1); + if (width != GetWidth(dib2)) + { + return false; + } + uint height = GetHeight(dib1); + if (height != GetHeight(dib2)) + { + return false; + } + uint bpp = GetBPP(dib1); + if (bpp != GetBPP(dib2)) + { + return false; + } + if (GetColorType(dib1) != GetColorType(dib2)) + { + return false; + } + FREE_IMAGE_TYPE type = GetImageType(dib1); + if (type != GetImageType(dib2)) + { + return false; + } + if (GetRedMask(dib1) != GetRedMask(dib2)) + { + return false; + } + if (GetGreenMask(dib1) != GetGreenMask(dib2)) + { + return false; + } + if (GetBlueMask(dib1) != GetBlueMask(dib2)) + { + return false; + } + + byte* ptr1, ptr2; + int fullBytes; + int shift; + uint line = GetLine(dib1); + + if (type == FREE_IMAGE_TYPE.FIT_BITMAP) + { + switch (bpp) + { + case 32: + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (!CompareMemory(ptr1, ptr2, line)) + { + return false; + } + } + break; + case 24: + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (!CompareMemory(ptr1, ptr2, line)) + { + return false; + } + } + break; + case 16: + short* sPtr1, sPtr2; + short mask = (short)(GetRedMask(dib1) | GetGreenMask(dib1) | GetBlueMask(dib1)); + if (mask == -1) + { + for (int i = 0; i < height; i++) + { + sPtr1 = (short*)GetScanLine(dib1, i); + sPtr2 = (short*)GetScanLine(dib2, i); + if (!CompareMemory(sPtr1, sPtr1, line)) + { + return false; + } + } + } + else + { + for (int i = 0; i < height; i++) + { + sPtr1 = (short*)GetScanLine(dib1, i); + sPtr2 = (short*)GetScanLine(dib2, i); + for (int x = 0; x < width; x++) + { + if ((sPtr1[x] & mask) != (sPtr2[x] & mask)) + { + return false; + } + } + } + } + break; + case 8: + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (!CompareMemory(ptr1, ptr2, line)) + { + return false; + } + } + break; + case 4: + fullBytes = (int)width / 2; + shift = (width % 2) == 0 ? 8 : 4; + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (fullBytes != 0) + { + if (!CompareMemory(ptr1, ptr2, fullBytes)) + { + return false; + } + ptr1 += fullBytes; + ptr2 += fullBytes; + } + if (shift != 8) + { + if ((ptr1[0] >> shift) != (ptr2[0] >> shift)) + { + return false; + } + } + } + break; + case 1: + fullBytes = (int)width / 8; + shift = 8 - ((int)width % 8); + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (fullBytes != 0) + { + if (!CompareMemory(ptr1, ptr2, fullBytes)) + { + return false; + } + ptr1 += fullBytes; + ptr2 += fullBytes; + } + if (shift != 8) + { + if ((ptr1[0] >> shift) != (ptr2[0] >> shift)) + { + return false; + } + } + } + break; + default: + throw new NotSupportedException("Only 1, 4, 8, 16, 24 and 32 bpp bitmaps are supported."); + } + } + else + { + for (int i = 0; i < height; i++) + { + ptr1 = (byte*)GetScanLine(dib1, i); + ptr2 = (byte*)GetScanLine(dib2, i); + if (!CompareMemory(ptr1, ptr2, line)) + { + return false; + } + } + } + return true; + } + + private static bool CompareMetadata(FIBITMAP dib1, FIBITMAP dib2) + { + MetadataTag tag1, tag2; + + foreach (FREE_IMAGE_MDMODEL metadataModel in FREE_IMAGE_MDMODELS) + { + if (GetMetadataCount(metadataModel, dib1) != + GetMetadataCount(metadataModel, dib2)) + { + return false; + } + if (GetMetadataCount(metadataModel, dib1) == 0) + { + continue; + } + + FIMETADATA mdHandle = FindFirstMetadata(metadataModel, dib1, out tag1); + if (mdHandle.IsNull) + { + continue; + } + do + { + if ((!GetMetadata(metadataModel, dib2, tag1.Key, out tag2)) || (tag1 != tag2)) + { + FindCloseMetadata(mdHandle); + return false; + } + } + while (FindNextMetadata(mdHandle, out tag1)); + FindCloseMetadata(mdHandle); + } + + return true; + } + + /// <summary> + /// Returns the FreeImage bitmap's transparency table. + /// The array is empty in case the bitmap has no transparency table. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>The FreeImage bitmap's transparency table.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe byte[] GetTransparencyTableEx(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + uint count = GetTransparencyCount(dib); + byte[] result = new byte[count]; + byte* ptr = (byte*)GetTransparencyTable(dib); + fixed (byte* dst = result) + { + CopyMemory(dst, ptr, count); + } + return result; + } + + /// <summary> + /// Set the FreeImage bitmap's transparency table. Only affects palletised bitmaps. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="table">The FreeImage bitmap's new transparency table.</param> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> or <paramref name="table"/> is null.</exception> + public static void SetTransparencyTable(FIBITMAP dib, byte[] table) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + if (table == null) + { + throw new ArgumentNullException("table"); + } + SetTransparencyTable(dib, table, table.Length); + } + + /// <summary> + /// This function returns the number of unique colors actually used by the + /// specified 1-, 4-, 8-, 16-, 24- or 32-bit image. This might be different from + /// what function FreeImage_GetColorsUsed() returns, which actually returns the + /// palette size for palletised images. Works for + /// <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/> type images only. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>Returns the number of unique colors used by the image specified or + /// zero, if the image type cannot be handled.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe int GetUniqueColors(FIBITMAP dib) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + + int result = 0; + + if (GetImageType(dib) == FREE_IMAGE_TYPE.FIT_BITMAP) + { + BitArray bitArray; + int uniquePalEnts; + int hashcode; + byte[] lut; + int width = (int)GetWidth(dib); + int height = (int)GetHeight(dib); + + switch (GetBPP(dib)) + { + case 1: + + result = 1; + lut = CreateShrunkenPaletteLUT(dib, out uniquePalEnts); + if (uniquePalEnts == 1) + { + break; + } + + if ((*(byte*)GetScanLine(dib, 0) & 0x80) == 0) + { + for (int y = 0; y < height; y++) + { + byte* scanline = (byte*)GetScanLine(dib, y); + int mask = 0x80; + for (int x = 0; x < width; x++) + { + if ((scanline[x / 8] & mask) > 0) + { + return 2; + } + mask = (mask == 0x1) ? 0x80 : (mask >> 1); + } + } + } + else + { + for (int y = 0; y < height; y++) + { + byte* scanline = (byte*)GetScanLine(dib, y); + int mask = 0x80; + for (int x = 0; x < width; x++) + { + if ((scanline[x / 8] & mask) == 0) + { + return 2; + } + mask = (mask == 0x1) ? 0x80 : (mask >> 1); + } + } + } + break; + + case 4: + + bitArray = new BitArray(0x10); + lut = CreateShrunkenPaletteLUT(dib, out uniquePalEnts); + if (uniquePalEnts == 1) + { + result = 1; + break; + } + + for (int y = 0; (y < height) && (result < uniquePalEnts); y++) + { + byte* scanline = (byte*)GetScanLine(dib, y); + bool top = true; + for (int x = 0; (x < width) && (result < uniquePalEnts); x++) + { + if (top) + { + hashcode = lut[scanline[x / 2] >> 4]; + } + else + { + hashcode = lut[scanline[x / 2] & 0xF]; + } + top = !top; + if (!bitArray[hashcode]) + { + bitArray[hashcode] = true; + result++; + } + } + } + break; + + case 8: + + bitArray = new BitArray(0x100); + lut = CreateShrunkenPaletteLUT(dib, out uniquePalEnts); + if (uniquePalEnts == 1) + { + result = 1; + break; + } + + for (int y = 0; (y < height) && (result < uniquePalEnts); y++) + { + byte* scanline = (byte*)GetScanLine(dib, y); + for (int x = 0; (x < width) && (result < uniquePalEnts); x++) + { + hashcode = lut[scanline[x]]; + if (!bitArray[hashcode]) + { + bitArray[hashcode] = true; + result++; + } + } + } + break; + + case 16: + + bitArray = new BitArray(0x10000); + + for (int y = 0; y < height; y++) + { + short* scanline = (short*)GetScanLine(dib, y); + for (int x = 0; x < width; x++, scanline++) + { + hashcode = *scanline; + if (!bitArray[hashcode]) + { + bitArray[hashcode] = true; + result++; + } + } + } + break; + + case 24: + + bitArray = new BitArray(0x1000000); + + for (int y = 0; y < height; y++) + { + byte* scanline = (byte*)GetScanLine(dib, y); + for (int x = 0; x < width; x++, scanline += 3) + { + hashcode = *((int*)scanline) & 0x00FFFFFF; + if (!bitArray[hashcode]) + { + bitArray[hashcode] = true; + result++; + } + } + } + break; + + case 32: + + bitArray = new BitArray(0x1000000); + + for (int y = 0; y < height; y++) + { + int* scanline = (int*)GetScanLine(dib, y); + for (int x = 0; x < width; x++, scanline++) + { + hashcode = *scanline & 0x00FFFFFF; + if (!bitArray[hashcode]) + { + bitArray[hashcode] = true; + result++; + } + } + } + break; + } + } + return result; + } + + /// <summary> + /// Verifies whether the FreeImage bitmap is 16bit 555. + /// </summary> + /// <param name="dib">The FreeImage bitmap to verify.</param> + /// <returns><b>true</b> if the bitmap is RGB16-555; otherwise <b>false</b>.</returns> + public static bool IsRGB555(FIBITMAP dib) + { + return ((GetRedMask(dib) == FI16_555_RED_MASK) && + (GetGreenMask(dib) == FI16_555_GREEN_MASK) && + (GetBlueMask(dib) == FI16_555_BLUE_MASK)); + } + + /// <summary> + /// Verifies whether the FreeImage bitmap is 16bit 565. + /// </summary> + /// <param name="dib">The FreeImage bitmap to verify.</param> + /// <returns><b>true</b> if the bitmap is RGB16-565; otherwise <b>false</b>.</returns> + public static bool IsRGB565(FIBITMAP dib) + { + return ((GetRedMask(dib) == FI16_565_RED_MASK) && + (GetGreenMask(dib) == FI16_565_GREEN_MASK) && + (GetBlueMask(dib) == FI16_565_BLUE_MASK)); + } + + #endregion + + #region ICC profile functions + + /// <summary> + /// Creates a new ICC-Profile for a FreeImage bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="data">The data of the new ICC-Profile.</param> + /// <returns>The new ICC-Profile of the bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIICCPROFILE CreateICCProfileEx(FIBITMAP dib, byte[] data) + { + return new FIICCPROFILE(dib, data); + } + + /// <summary> + /// Creates a new ICC-Profile for a FreeImage bitmap. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="data">The data of the new ICC-Profile.</param> + /// <param name="size">The number of bytes of <paramref name="data"/> to use.</param> + /// <returns>The new ICC-Profile of the FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIICCPROFILE CreateICCProfileEx(FIBITMAP dib, byte[] data, int size) + { + return new FIICCPROFILE(dib, data, size); + } + + #endregion + + #region Conversion functions + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion) + { + return ConvertColorDepth( + dib, + conversion, + 128, + FREE_IMAGE_DITHER.FID_FS, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + false); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + bool unloadSource) + { + return ConvertColorDepth( + dib, + conversion, + 128, + FREE_IMAGE_DITHER.FID_FS, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + unloadSource); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="threshold">Threshold value when converting with + /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD"/>.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + byte threshold) + { + return ConvertColorDepth( + dib, + conversion, + threshold, + FREE_IMAGE_DITHER.FID_FS, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + false); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="ditherMethod">Dither algorithm when converting + /// with <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER"/>.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + FREE_IMAGE_DITHER ditherMethod) + { + return ConvertColorDepth( + dib, + conversion, + 128, + ditherMethod, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + false); + } + + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="quantizationMethod">The quantization algorithm for conversion to 8-bit color depth.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + FREE_IMAGE_QUANTIZE quantizationMethod) + { + return ConvertColorDepth( + dib, + conversion, + 128, + FREE_IMAGE_DITHER.FID_FS, + quantizationMethod, + false); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="threshold">Threshold value when converting with + /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD"/>.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + byte threshold, + bool unloadSource) + { + return ConvertColorDepth( + dib, + conversion, + threshold, + FREE_IMAGE_DITHER.FID_FS, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + unloadSource); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="ditherMethod">Dither algorithm when converting with + /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER"/>.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + FREE_IMAGE_DITHER ditherMethod, + bool unloadSource) + { + return ConvertColorDepth( + dib, + conversion, + 128, + ditherMethod, + FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, + unloadSource); + } + + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="quantizationMethod">The quantization algorithm for conversion to 8-bit color depth.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + FREE_IMAGE_QUANTIZE quantizationMethod, + bool unloadSource) + { + return ConvertColorDepth( + dib, + conversion, + 128, + FREE_IMAGE_DITHER.FID_FS, + quantizationMethod, + unloadSource); + } + + /// <summary> + /// Converts a FreeImage bitmap from one color depth to another. + /// If the conversion fails the original FreeImage bitmap is returned. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="conversion">The desired output format.</param> + /// <param name="threshold">Threshold value when converting with + /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD"/>.</param> + /// <param name="ditherMethod">Dither algorithm when converting with + /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER"/>.</param> + /// <param name="quantizationMethod">The quantization algorithm for conversion to 8-bit color depth.</param> + /// <param name="unloadSource">When true the structure will be unloaded on success.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + internal static FIBITMAP ConvertColorDepth( + FIBITMAP dib, + FREE_IMAGE_COLOR_DEPTH conversion, + byte threshold, + FREE_IMAGE_DITHER ditherMethod, + FREE_IMAGE_QUANTIZE quantizationMethod, + bool unloadSource) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + + FIBITMAP result = new FIBITMAP(); + FIBITMAP dibTemp = new FIBITMAP(); + uint bpp = GetBPP(dib); + bool reorderPalette = ((conversion & FREE_IMAGE_COLOR_DEPTH.FICD_REORDER_PALETTE) > 0); + bool forceGreyscale = ((conversion & FREE_IMAGE_COLOR_DEPTH.FICD_FORCE_GREYSCALE) > 0); + + if (GetImageType(dib) == FREE_IMAGE_TYPE.FIT_BITMAP) + { + switch (conversion & (FREE_IMAGE_COLOR_DEPTH)0xFF) + { + case FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD: + + if (bpp != 1) + { + if (forceGreyscale) + { + result = Threshold(dib, threshold); + } + else + { + dibTemp = ConvertTo24Bits(dib); + result = ColorQuantizeEx(dibTemp, quantizationMethod, 2, null, 1); + Unload(dibTemp); + } + } + else + { + bool isGreyscale = IsGreyscaleImage(dib); + if ((forceGreyscale && (!isGreyscale)) || + (reorderPalette && isGreyscale)) + { + result = Threshold(dib, threshold); + } + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER: + + if (bpp != 1) + { + if (forceGreyscale) + { + result = Dither(dib, ditherMethod); + } + else + { + dibTemp = ConvertTo24Bits(dib); + result = ColorQuantizeEx(dibTemp, quantizationMethod, 2, null, 1); + Unload(dibTemp); + } + } + else + { + bool isGreyscale = IsGreyscaleImage(dib); + if ((forceGreyscale && (!isGreyscale)) || + (reorderPalette && isGreyscale)) + { + result = Dither(dib, ditherMethod); + } + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_04_BPP: + + if (bpp != 4) + { + // Special case when 1bpp and FIC_PALETTE + if (forceGreyscale || + ((bpp == 1) && (GetColorType(dib) == FREE_IMAGE_COLOR_TYPE.FIC_PALETTE))) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo4Bits(dibTemp); + Unload(dibTemp); + } + else + { + dibTemp = ConvertTo24Bits(dib); + result = ColorQuantizeEx(dibTemp, quantizationMethod, 16, null, 4); + Unload(dibTemp); + } + } + else + { + bool isGreyscale = IsGreyscaleImage(dib); + if ((forceGreyscale && (!isGreyscale)) || + (reorderPalette && isGreyscale)) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo4Bits(dibTemp); + Unload(dibTemp); + } + } + + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_08_BPP: + + if (bpp != 8) + { + if (forceGreyscale) + { + result = ConvertToGreyscale(dib); + } + else + { + dibTemp = ConvertTo24Bits(dib); + result = ColorQuantize(dibTemp, quantizationMethod); + Unload(dibTemp); + } + } + else + { + bool isGreyscale = IsGreyscaleImage(dib); + if ((forceGreyscale && (!isGreyscale)) || (reorderPalette && isGreyscale)) + { + result = ConvertToGreyscale(dib); + } + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_16_BPP_555: + + if (forceGreyscale) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo16Bits555(dibTemp); + Unload(dibTemp); + } + else if (bpp != 16 || GetRedMask(dib) != FI16_555_RED_MASK || GetGreenMask(dib) != FI16_555_GREEN_MASK || GetBlueMask(dib) != FI16_555_BLUE_MASK) + { + result = ConvertTo16Bits555(dib); + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_16_BPP: + + if (forceGreyscale) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo16Bits565(dibTemp); + Unload(dibTemp); + } + else if (bpp != 16 || GetRedMask(dib) != FI16_565_RED_MASK || GetGreenMask(dib) != FI16_565_GREEN_MASK || GetBlueMask(dib) != FI16_565_BLUE_MASK) + { + result = ConvertTo16Bits565(dib); + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_24_BPP: + + if (forceGreyscale) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo24Bits(dibTemp); + Unload(dibTemp); + } + else if (bpp != 24) + { + result = ConvertTo24Bits(dib); + } + break; + + case FREE_IMAGE_COLOR_DEPTH.FICD_32_BPP: + + if (forceGreyscale) + { + dibTemp = ConvertToGreyscale(dib); + result = ConvertTo32Bits(dibTemp); + Unload(dibTemp); + } + else if (bpp != 32) + { + result = ConvertTo32Bits(dib); + } + break; + } + } + + if (result.IsNull) + { + return dib; + } + if (unloadSource) + { + Unload(dib); + } + + return result; + } + + /// <summary> + /// ColorQuantizeEx is an extension to the <see cref="ColorQuantize(FIBITMAP, FREE_IMAGE_QUANTIZE)"/> + /// method that provides additional options used to quantize a 24-bit image to any + /// number of colors (up to 256), as well as quantize a 24-bit image using a + /// provided palette. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="quantize">Specifies the color reduction algorithm to be used.</param> + /// <param name="PaletteSize">Size of the desired output palette.</param> + /// <param name="ReservePalette">The provided palette.</param> + /// <param name="minColorDepth"><b>true</b> to create a bitmap with the smallest possible + /// color depth for the specified <paramref name="PaletteSize"/>.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP ColorQuantizeEx(FIBITMAP dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, RGBQUAD[] ReservePalette, bool minColorDepth) + { + FIBITMAP result; + if (minColorDepth) + { + int bpp; + if (PaletteSize >= 256) + bpp = 8; + else if (PaletteSize > 2) + bpp = 4; + else + bpp = 1; + result = ColorQuantizeEx(dib, quantize, PaletteSize, ReservePalette, bpp); + } + else + { + result = ColorQuantizeEx(dib, quantize, PaletteSize, ReservePalette, 8); + } + return result; + } + + /// <summary> + /// ColorQuantizeEx is an extension to the <see cref="ColorQuantize(FIBITMAP, FREE_IMAGE_QUANTIZE)"/> + /// method that provides additional options used to quantize a 24-bit image to any + /// number of colors (up to 256), as well as quantize a 24-bit image using a + /// partial or full provided palette. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="quantize">Specifies the color reduction algorithm to be used.</param> + /// <param name="PaletteSize">Size of the desired output palette.</param> + /// <param name="ReservePalette">The provided palette.</param> + /// <param name="bpp">The desired color depth of the created image.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP ColorQuantizeEx(FIBITMAP dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, RGBQUAD[] ReservePalette, int bpp) + { + unsafe + { + FIBITMAP result = FIBITMAP.Zero; + FIBITMAP temp = FIBITMAP.Zero; + int reservedSize = (ReservePalette == null) ? 0 : ReservePalette.Length; + + if (bpp == 8) + { + result = ColorQuantizeEx(dib, quantize, PaletteSize, reservedSize, ReservePalette); + } + else if (bpp == 4) + { + temp = ColorQuantizeEx(dib, quantize, Math.Min(16, PaletteSize), reservedSize, ReservePalette); + if (!temp.IsNull) + { + result = Allocate((int)GetWidth(temp), (int)GetHeight(temp), 4, 0, 0, 0); + CloneMetadata(result, temp); + CopyMemory(GetPalette(result), GetPalette(temp), sizeof(RGBQUAD) * 16); + + for (int y = (int)GetHeight(temp) - 1; y >= 0; y--) + { + Scanline<byte> srcScanline = new Scanline<byte>(temp, y); + Scanline<FI4BIT> dstScanline = new Scanline<FI4BIT>(result, y); + + for (int x = (int)GetWidth(temp) - 1; x >= 0; x--) + { + dstScanline[x] = srcScanline[x]; + } + } + } + } + else if (bpp == 1) + { + temp = ColorQuantizeEx(dib, quantize, 2, reservedSize, ReservePalette); + if (!temp.IsNull) + { + result = Allocate((int)GetWidth(temp), (int)GetHeight(temp), 1, 0, 0, 0); + CloneMetadata(result, temp); + CopyMemory(GetPalette(result), GetPalette(temp), sizeof(RGBQUAD) * 2); + + for (int y = (int)GetHeight(temp) - 1; y >= 0; y--) + { + Scanline<byte> srcScanline = new Scanline<byte>(temp, y); + Scanline<FI1BIT> dstScanline = new Scanline<FI1BIT>(result, y); + + for (int x = (int)GetWidth(temp) - 1; x >= 0; x--) + { + dstScanline[x] = srcScanline[x]; + } + } + } + } + + UnloadEx(ref temp); + return result; + } + } + + #endregion + + #region Metadata + + /// <summary> + /// Copies metadata from one FreeImage bitmap to another. + /// </summary> + /// <param name="src">Source FreeImage bitmap containing the metadata.</param> + /// <param name="dst">FreeImage bitmap to copy the metadata to.</param> + /// <param name="flags">Flags to switch different copy modes.</param> + /// <returns>Returns -1 on failure else the number of copied tags.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="src"/> or <paramref name="dst"/> is null.</exception> + public static int CloneMetadataEx(FIBITMAP src, FIBITMAP dst, FREE_IMAGE_METADATA_COPY flags) + { + if (src.IsNull) + { + throw new ArgumentNullException("src"); + } + if (dst.IsNull) + { + throw new ArgumentNullException("dst"); + } + + FITAG tag = new FITAG(), tag2 = new FITAG(); + int copied = 0; + + // Clear all existing metadata + if ((flags & FREE_IMAGE_METADATA_COPY.CLEAR_EXISTING) > 0) + { + foreach (FREE_IMAGE_MDMODEL model in FREE_IMAGE_MDMODELS) + { + if (!SetMetadata(model, dst, null, tag)) + { + return -1; + } + } + } + + bool keep = !((flags & FREE_IMAGE_METADATA_COPY.REPLACE_EXISTING) > 0); + + foreach (FREE_IMAGE_MDMODEL model in FREE_IMAGE_MDMODELS) + { + FIMETADATA mData = FindFirstMetadata(model, src, out tag); + if (mData.IsNull) continue; + do + { + string key = GetTagKey(tag); + if (!(keep && GetMetadata(model, dst, key, out tag2))) + { + if (SetMetadata(model, dst, key, tag)) + { + copied++; + } + } + } + while (FindNextMetadata(mData, out tag)); + FindCloseMetadata(mData); + } + + return copied; + } + + /// <summary> + /// Returns the comment of a JPEG, PNG or GIF image. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <returns>Comment of the FreeImage bitmp, or null in case no comment exists.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static string GetImageComment(FIBITMAP dib) + { + string result = null; + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + FITAG tag; + if (GetMetadata(FREE_IMAGE_MDMODEL.FIMD_COMMENTS, dib, "Comment", out tag)) + { + MetadataTag metadataTag = new MetadataTag(tag, FREE_IMAGE_MDMODEL.FIMD_COMMENTS); + result = metadataTag.Value as string; + } + return result; + } + + /// <summary> + /// Sets the comment of a JPEG, PNG or GIF image. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="comment">New comment of the FreeImage bitmap. + /// Use null to remove the comment.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static bool SetImageComment(FIBITMAP dib, string comment) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + bool result; + if (comment != null) + { + FITAG tag = CreateTag(); + MetadataTag metadataTag = new MetadataTag(tag, FREE_IMAGE_MDMODEL.FIMD_COMMENTS); + metadataTag.Value = comment; + result = SetMetadata(FREE_IMAGE_MDMODEL.FIMD_COMMENTS, dib, "Comment", tag); + DeleteTag(tag); + } + else + { + result = SetMetadata(FREE_IMAGE_MDMODEL.FIMD_COMMENTS, dib, "Comment", FITAG.Zero); + } + return result; + } + + /// <summary> + /// Retrieve a metadata attached to a FreeImage bitmap. + /// </summary> + /// <param name="model">The metadata model to look for.</param> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="key">The metadata field name.</param> + /// <param name="tag">A <see cref="MetadataTag"/> structure returned by the function.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static bool GetMetadata( + FREE_IMAGE_MDMODEL model, + FIBITMAP dib, + string key, + out MetadataTag tag) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + + FITAG _tag; + bool result; + if (GetMetadata(model, dib, key, out _tag)) + { + tag = new MetadataTag(_tag, model); + result = true; + } + else + { + tag = null; + result = false; + } + return result; + } + + /// <summary> + /// Attach a new metadata tag to a FreeImage bitmap. + /// </summary> + /// <param name="model">The metadata model used to store the tag.</param> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="key">The tag field name.</param> + /// <param name="tag">The <see cref="MetadataTag"/> to be attached.</param> + /// <returns>Returns true on success, false on failure.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static bool SetMetadata( + FREE_IMAGE_MDMODEL model, + FIBITMAP dib, + string key, + MetadataTag tag) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + return SetMetadata(model, dib, key, tag.tag); + } + + /// <summary> + /// Provides information about the first instance of a tag that matches the metadata model. + /// </summary> + /// <param name="model">The model to match.</param> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="tag">Tag that matches the metadata model.</param> + /// <returns>Unique search handle that can be used to call FindNextMetadata or FindCloseMetadata. + /// Null if the metadata model does not exist.</returns> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static FIMETADATA FindFirstMetadata( + FREE_IMAGE_MDMODEL model, + FIBITMAP dib, + out MetadataTag tag) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + FITAG _tag; + FIMETADATA result = FindFirstMetadata(model, dib, out _tag); + if (result.IsNull) + { + tag = null; + return result; + } + tag = new MetadataTag(_tag, model); + if (metaDataSearchHandler.ContainsKey(result)) + { + metaDataSearchHandler[result] = model; + } + else + { + metaDataSearchHandler.Add(result, model); + } + return result; + } + + /// <summary> + /// Find the next tag, if any, that matches the metadata model argument in a previous call + /// to FindFirstMetadata, and then alters the tag object contents accordingly. + /// </summary> + /// <param name="mdhandle">Unique search handle provided by FindFirstMetadata.</param> + /// <param name="tag">Tag that matches the metadata model.</param> + /// <returns>Returns true on success, false on failure.</returns> + public static bool FindNextMetadata(FIMETADATA mdhandle, out MetadataTag tag) + { + FITAG _tag; + bool result; + if (FindNextMetadata(mdhandle, out _tag)) + { + tag = new MetadataTag(_tag, metaDataSearchHandler[mdhandle]); + result = true; + } + else + { + tag = null; + result = false; + } + return result; + } + + /// <summary> + /// Closes the specified metadata search handle and releases associated resources. + /// </summary> + /// <param name="mdhandle">The handle to close.</param> + public static void FindCloseMetadata(FIMETADATA mdhandle) + { + if (metaDataSearchHandler.ContainsKey(mdhandle)) + { + metaDataSearchHandler.Remove(mdhandle); + } + FindCloseMetadata_(mdhandle); + } + + /// <summary> + /// This dictionary links FIMETADATA handles and FREE_IMAGE_MDMODEL models. + /// </summary> + private static Dictionary<FIMETADATA, FREE_IMAGE_MDMODEL> metaDataSearchHandler + = new Dictionary<FIMETADATA, FREE_IMAGE_MDMODEL>(1); + + #endregion + + #region Rotation and Flipping + + /// <summary> + /// This function rotates a 1-, 8-bit greyscale or a 24-, 32-bit color image by means of 3 shears. + /// 1-bit images rotation is limited to integer multiple of 90°. + /// <c>null</c> is returned for other values. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="angle">The angle of rotation.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP Rotate(FIBITMAP dib, double angle) + { + return Rotate(dib, angle, IntPtr.Zero); + } + + /// <summary> + /// This function rotates a 1-, 8-bit greyscale or a 24-, 32-bit color image by means of 3 shears. + /// 1-bit images rotation is limited to integer multiple of 90°. + /// <c>null</c> is returned for other values. + /// </summary> + /// <typeparam name="T">The type of the color to use as background.</typeparam> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="angle">The angle of rotation.</param> + /// <param name="backgroundColor">The color used used to fill the bitmap's background.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + public static FIBITMAP Rotate<T>(FIBITMAP dib, double angle, T? backgroundColor) where T : struct + { + if (backgroundColor.HasValue) + { + GCHandle handle = new GCHandle(); + try + { + T[] buffer = new T[] { backgroundColor.Value }; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + return Rotate(dib, angle, handle.AddrOfPinnedObject()); + } + finally + { + if (handle.IsAllocated) + handle.Free(); + } + } + else + { + return Rotate(dib, angle, IntPtr.Zero); + } + } + + /// <summary> + /// Rotates a 4-bit color FreeImage bitmap. + /// Allowed values for <paramref name="angle"/> are 90, 180 and 270. + /// In case <paramref name="angle"/> is 0 or 360 a clone is returned. + /// 0 is returned for other values or in case the rotation fails. + /// </summary> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="angle">The angle of rotation.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function is kind of temporary due to FreeImage's lack of + /// rotating 4-bit images. It's particularly used by <see cref="FreeImageBitmap"/>'s + /// method RotateFlip. This function will be removed as soon as FreeImage + /// supports rotating 4-bit images. + /// </remarks> + /// <exception cref="ArgumentNullException"> + /// <paramref name="dib"/> is null.</exception> + public static unsafe FIBITMAP Rotate4bit(FIBITMAP dib, double angle) + { + if (dib.IsNull) + { + throw new ArgumentNullException("dib"); + } + + FIBITMAP result = new FIBITMAP(); + int ang = (int)angle; + + if ((GetImageType(dib) == FREE_IMAGE_TYPE.FIT_BITMAP) && + (GetBPP(dib) == 4) && + ((ang % 90) == 0)) + { + int width, height, xOrg, yOrg; + Scanline<FI4BIT>[] src, dst; + width = (int)GetWidth(dib); + height = (int)GetHeight(dib); + byte index = 0; + switch (ang) + { + case 90: + result = Allocate(height, width, 4, 0, 0, 0); + if (result.IsNull) + { + break; + } + CopyPalette(dib, result); + src = Get04BitScanlines(dib); + dst = Get04BitScanlines(result); + for (int y = 0; y < width; y++) + { + yOrg = height - 1; + for (int x = 0; x < height; x++, yOrg--) + { + index = src[yOrg][y]; + dst[y][x] = index; + } + } + break; + case 180: + result = Allocate(width, height, 4, 0, 0, 0); + if (result.IsNull) + { + break; + } + CopyPalette(dib, result); + src = Get04BitScanlines(dib); + dst = Get04BitScanlines(result); + + yOrg = height - 1; + for (int y = 0; y < height; y++, yOrg--) + { + xOrg = width - 1; + for (int x = 0; x < width; x++, xOrg--) + { + index = src[yOrg][xOrg]; + dst[y][x] = index; + } + } + break; + case 270: + result = Allocate(height, width, 4, 0, 0, 0); + if (result.IsNull) + { + break; + } + CopyPalette(dib, result); + src = Get04BitScanlines(dib); + dst = Get04BitScanlines(result); + xOrg = width - 1; + for (int y = 0; y < width; y++, xOrg--) + { + for (int x = 0; x < height; x++) + { + index = src[x][xOrg]; + dst[y][x] = index; + } + } + break; + case 0: + case 360: + result = Clone(dib); + break; + } + } + return result; + } + + #endregion + + #region Upsampling / downsampling + + /// <summary> + /// Enlarges or shrinks the FreeImage bitmap selectively per side and fills newly added areas + /// with the specified background color. See remarks for further details. + /// </summary> + /// <typeparam name="T">The type of the specified color.</typeparam> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="left">The number of pixels, the image should be enlarged on its left side. + /// Negative values shrink the image on its left side.</param> + /// <param name="top">The number of pixels, the image should be enlarged on its top side. + /// Negative values shrink the image on its top side.</param> + /// <param name="right">The number of pixels, the image should be enlarged on its right side. + /// Negative values shrink the image on its right side.</param> + /// <param name="bottom">The number of pixels, the image should be enlarged on its bottom side. + /// Negative values shrink the image on its bottom side.</param> + /// <param name="color">The color, the enlarged sides of the image should be filled with.</param> + /// <param name="options">Options that affect the color search process for palletized images.</param> + /// <returns>Handle to a FreeImage bitmap.</returns> + /// <remarks> + /// This function enlarges or shrinks an image selectively per side. + /// The main purpose of this function is to add borders to an image. + /// To add a border to any of the image's sides, a positive integer value must be passed in + /// any of the parameters <paramref name="left"/>, <paramref name="top"/>, <paramref name="right"/> + /// or <paramref name="bottom"/>. This value represents the border's + /// width in pixels. Newly created parts of the image (the border areas) are filled with the + /// specified <paramref name="color"/>. + /// Specifying a negative integer value for a certain side, will shrink or crop the image on + /// this side. Consequently, specifying zero for a certain side will not change the image's + /// extension on that side. + /// <para/> + /// So, calling this function with all parameters <paramref name="left"/>, <paramref name="top"/>, + /// <paramref name="right"/> and <paramref name="bottom"/> set to zero, is + /// effectively the same as calling function <see cref="Clone"/>; setting all parameters + /// <paramref name="left"/>, <paramref name="top"/>, <paramref name="right"/> and + /// <paramref name="bottom"/> to value equal to or smaller than zero, my easily be substituted + /// by a call to function <see cref="Copy"/>. Both these cases produce a new image, which is + /// guaranteed not to be larger than the input image. Thus, since the specified + /// <paramref name="color"/> is not needed in these cases, <paramref name="color"/> + /// may be <c>null</c>. + /// <para/> + /// Both parameters <paramref name="color"/> and <paramref name="options"/> work according to + /// function <see cref="FillBackground<T>"/>. So, please refer to the documentation of + /// <see cref="FillBackground<T>"/> to learn more about parameters <paramref name="color"/> + /// and <paramref name="options"/>. For palletized images, the palette of the input image is + /// transparently copied to the newly created enlarged or shrunken image, so any color look-ups + /// are performed on this palette. + /// </remarks> + /// <example> + /// // create a white color<br/> + /// RGBQUAD c;<br/> + /// c.rgbRed = 0xFF;<br/> + /// c.rgbGreen = 0xFF;<br/> + /// c.rgbBlue = 0xFF;<br/> + /// c.rgbReserved = 0x00;<br/> + /// <br/> + /// // add a white, symmetric 10 pixel wide border to the image<br/> + /// dib2 = FreeImage_EnlargeCanvas(dib, 10, 10, 10, 10, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);<br/> + /// <br/> + /// // add white, 20 pixel wide stripes to the top and bottom side of the image<br/> + /// dib3 = FreeImage_EnlargeCanvas(dib, 0, 20, 0, 20, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);<br/> + /// <br/> + /// // add white, 30 pixel wide stripes to the right side of the image and<br/> + /// // cut off the 40 leftmost pixel columns<br/> + /// dib3 = FreeImage_EnlargeCanvas(dib, -40, 0, 30, 0, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);<br/> + /// </example> + public static FIBITMAP EnlargeCanvas<T>(FIBITMAP dib, int left, int top, int right, int bottom, + T? color, FREE_IMAGE_COLOR_OPTIONS options) where T : struct + { + if (dib.IsNull) + return FIBITMAP.Zero; + + if (!CheckColorType(GetImageType(dib), color)) + return FIBITMAP.Zero; + + if (color.HasValue) + { + GCHandle handle = new GCHandle(); + try + { + T[] buffer = new T[] { color.Value }; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + return EnlargeCanvas(dib, left, top, right, bottom, handle.AddrOfPinnedObject(), options); + } + finally + { + if (handle.IsAllocated) + handle.Free(); + } + } + else + { + return EnlargeCanvas(dib, left, top, right, bottom, IntPtr.Zero, options); + } + } + + #endregion + + #region Color + + /// <summary> + /// Sets all pixels of the specified image to the color provided through the + /// <paramref name="color"/> parameter. See remarks for further details. + /// </summary> + /// <typeparam name="T">The type of the specified color.</typeparam> + /// <param name="dib">Handle to a FreeImage bitmap.</param> + /// <param name="color">The color to fill the bitmap with. See remarks for further details.</param> + /// <param name="options">Options that affect the color search process for palletized images.</param> + /// <returns><c>true</c> on success, <c>false</c> on failure.</returns> + /// <remarks> + /// This function sets all pixels of an image to the color provided through + /// the <paramref name="color"/> parameter. <see cref="RGBQUAD"/> is used for standard type images. + /// For non standard type images the underlaying structure is used. + /// <para/> + /// So, <paramref name="color"/> must be of type <see cref="Double"/>, if the image to be filled is of type + /// <see cref="FREE_IMAGE_TYPE.FIT_DOUBLE"/> and must be a <see cref="FIRGBF"/> structure if the + /// image is of type <see cref="FREE_IMAGE_TYPE.FIT_RGBF"/> and so on. + /// <para/> + /// However, the fill color is always specified through a <see cref="RGBQUAD"/> structure + /// for all images of type <see cref="FREE_IMAGE_TYPE.FIT_BITMAP"/>. + /// So, for 32- and 24-bit images, the red, green and blue members of the <see cref="RGBQUAD"/> + /// structure are directly used for the image's red, green and blue channel respectively. + /// Although alpha transparent <see cref="RGBQUAD"/> colors are + /// supported, the alpha channel of a 32-bit image never gets modified by this function. + /// A fill color with an alpha value smaller than 255 gets blended with the image's actual + /// background color, which is determined from the image's bottom-left pixel. + /// So, currently using alpha enabled colors, assumes the image to be unicolor before the + /// fill operation. However, the <see cref="RGBQUAD.rgbReserved"/> field is only taken into account, + /// if option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_RGBA"/> has been specified. + /// <para/> + /// For 16-bit images, the red-, green- and blue components of the specified color are + /// transparently translated into either the 16-bit 555 or 565 representation. This depends + /// on the image's actual red- green- and blue masks. + /// <para/> + /// Special attention must be payed for palletized images. Generally, the RGB color specified + /// is looked up in the image's palette. The found palette index is then used to fill the image. + /// There are some option flags, that affect this lookup process: + /// <list type="table"> + /// <listheader> + /// <term>Value</term> + /// <description>Meaning</description> + /// </listheader> + /// <item> + /// <term><see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_DEFAULT"/></term> + /// <description> + /// Uses the color, that is nearest to the specified color. + /// This is the default behavior and should always find a + /// color in the palette. However, the visual result may + /// far from what was expected and mainly depends on the + /// image's palette. + /// </description> + /// </item> + /// <item> + /// <term><see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_EQUAL_COLOR"/></term> + /// <description> + /// Searches the image's palette for the specified color + /// but only uses the returned palette index, if the specified + /// color exactly matches the palette entry. Of course, + /// depending on the image's actual palette entries, this + /// operation may fail. In this case, the function falls back + /// to option <see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/> + /// and uses the RGBQUAD's rgbReserved member (or its low nibble for 4-bit images + /// or its least significant bit (LSB) for 1-bit images) as + /// the palette index used for the fill operation. + /// </description> + /// </item> + /// <item> + /// <term><see cref="FREE_IMAGE_COLOR_OPTIONS.FICO_ALPHA_IS_INDEX"/></term> + /// <description> + /// Does not perform any color lookup from the palette, but + /// uses the RGBQUAD's alpha channel member rgbReserved as + /// the palette index to be used for the fill operation. + /// However, for 4-bit images, only the low nibble of the + /// rgbReserved member are used and for 1-bit images, only + /// the least significant bit (LSB) is used. + /// </description> + /// </item> + /// </list> + /// </remarks> + public static bool FillBackground<T>(FIBITMAP dib, T color, FREE_IMAGE_COLOR_OPTIONS options) + where T : struct + { + if (dib.IsNull) + return false; + + if (!CheckColorType(GetImageType(dib), color)) + return false; + + GCHandle handle = new GCHandle(); + try + { + T[] buffer = new T[] { color }; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + return FillBackground(dib, handle.AddrOfPinnedObject(), options); + } + finally + { + if (handle.IsAllocated) + handle.Free(); + } + } + + #endregion + + #region Wrapper functions + + /// <summary> + /// Returns the next higher possible color depth. + /// </summary> + /// <param name="bpp">Color depth to increase.</param> + /// <returns>The next higher color depth or 0 if there is no valid color depth.</returns> + internal static int GetNextColorDepth(int bpp) + { + int result = 0; + switch (bpp) + { + case 1: + result = 4; + break; + case 4: + result = 8; + break; + case 8: + result = 16; + break; + case 16: + result = 24; + break; + case 24: + result = 32; + break; + } + return result; + } + + /// <summary> + /// Returns the next lower possible color depth. + /// </summary> + /// <param name="bpp">Color depth to decrease.</param> + /// <returns>The next lower color depth or 0 if there is no valid color depth.</returns> + internal static int GetPrevousColorDepth(int bpp) + { + int result = 0; + switch (bpp) + { + case 32: + result = 24; + break; + case 24: + result = 16; + break; + case 16: + result = 8; + break; + case 8: + result = 4; + break; + case 4: + result = 1; + break; + } + return result; + } + + /// <summary> + /// Reads a null-terminated c-string. + /// </summary> + /// <param name="ptr">Pointer to the first char of the string.</param> + /// <returns>The converted string.</returns> + internal static unsafe string PtrToStr(byte* ptr) + { + string result = null; + if (ptr != null) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + + while (*ptr != 0) + { + sb.Append((char)(*(ptr++))); + } + result = sb.ToString(); + } + return result; + } + + internal static unsafe byte[] CreateShrunkenPaletteLUT(FIBITMAP dib, out int uniqueColors) + { + byte[] result = null; + uniqueColors = 0; + + if ((!dib.IsNull) && (GetImageType(dib) == FREE_IMAGE_TYPE.FIT_BITMAP) && (GetBPP(dib) <= 8)) + { + int size = (int)GetColorsUsed(dib); + List<RGBQUAD> newPalette = new List<RGBQUAD>(size); + List<byte> lut = new List<byte>(size); + RGBQUAD* palette = (RGBQUAD*)GetPalette(dib); + RGBQUAD color; + int index; + + for (int i = 0; i < size; i++) + { + color = palette[i]; + color.rgbReserved = 255; // ignore alpha + + index = newPalette.IndexOf(color); + if (index < 0) + { + newPalette.Add(color); + lut.Add((byte)(newPalette.Count - 1)); + } + else + { + lut.Add((byte)index); + } + } + result = lut.ToArray(); + uniqueColors = newPalette.Count; + } + return result; + } + + internal static PropertyItem CreatePropertyItem() + { + return (PropertyItem)Activator.CreateInstance(typeof(PropertyItem), true); + } + + private static unsafe void CopyPalette(FIBITMAP src, FIBITMAP dst) + { + RGBQUAD* orgPal = (RGBQUAD*)GetPalette(src); + RGBQUAD* newPal = (RGBQUAD*)GetPalette(dst); + uint size = (uint)(sizeof(RGBQUAD) * GetColorsUsed(src)); + CopyMemory(newPal, orgPal, size); + } + + private static unsafe Scanline<FI4BIT>[] Get04BitScanlines(FIBITMAP dib) + { + int height = (int)GetHeight(dib); + Scanline<FI4BIT>[] array = new Scanline<FI4BIT>[height]; + for (int i = 0; i < height; i++) + { + array[i] = new Scanline<FI4BIT>(dib, i); + } + return array; + } + + /// <summary> + /// Changes a bitmaps color depth. + /// Used by SaveEx and SaveToStream. + /// </summary> + private static FIBITMAP PrepareBitmapColorDepth(FIBITMAP dibToSave, FREE_IMAGE_FORMAT format, FREE_IMAGE_COLOR_DEPTH colorDepth) + { + FREE_IMAGE_TYPE type = GetImageType(dibToSave); + if (type == FREE_IMAGE_TYPE.FIT_BITMAP) + { + int bpp = (int)GetBPP(dibToSave); + int targetBpp = (int)(colorDepth & FREE_IMAGE_COLOR_DEPTH.FICD_COLOR_MASK); + + if (colorDepth != FREE_IMAGE_COLOR_DEPTH.FICD_AUTO) + { + // A fix colordepth was chosen + if (FIFSupportsExportBPP(format, targetBpp)) + { + dibToSave = ConvertColorDepth(dibToSave, colorDepth, false); + } + else + { + throw new ArgumentException("FreeImage\n\nFreeImage Library plugin " + + GetFormatFromFIF(format) + " is unable to write images with a color depth of " + + targetBpp + " bpp."); + } + } + else + { + // Auto selection was chosen + if (!FIFSupportsExportBPP(format, bpp)) + { + // The color depth is not supported + int bppUpper = bpp; + int bppLower = bpp; + // Check from the bitmaps current color depth in both directions + do + { + bppUpper = GetNextColorDepth(bppUpper); + if (FIFSupportsExportBPP(format, bppUpper)) + { + dibToSave = ConvertColorDepth(dibToSave, (FREE_IMAGE_COLOR_DEPTH)bppUpper, false); + break; + } + bppLower = GetPrevousColorDepth(bppLower); + if (FIFSupportsExportBPP(format, bppLower)) + { + dibToSave = ConvertColorDepth(dibToSave, (FREE_IMAGE_COLOR_DEPTH)bppLower, false); + break; + } + } while (!((bppLower == 0) && (bppUpper == 0))); + } + } + } + return dibToSave; + } + + /// <summary> + /// Compares blocks of memory. + /// </summary> + /// <param name="buf1">A pointer to a block of memory to compare.</param> + /// <param name="buf2">A pointer to a block of memory to compare.</param> + /// <param name="length">Specifies the number of bytes to be compared.</param> + /// <returns>true, if all bytes compare as equal, false otherwise.</returns> + public static unsafe bool CompareMemory(void* buf1, void* buf2, uint length) + { + return (length == RtlCompareMemory(buf1, buf2, length)); + } + + /// <summary> + /// Compares blocks of memory. + /// </summary> + /// <param name="buf1">A pointer to a block of memory to compare.</param> + /// <param name="buf2">A pointer to a block of memory to compare.</param> + /// <param name="length">Specifies the number of bytes to be compared.</param> + /// <returns>true, if all bytes compare as equal, false otherwise.</returns> + public static unsafe bool CompareMemory(void* buf1, void* buf2, long length) + { + return (length == RtlCompareMemory(buf1, buf2, checked((uint)length))); + } + + /// <summary> + /// Compares blocks of memory. + /// </summary> + /// <param name="buf1">A pointer to a block of memory to compare.</param> + /// <param name="buf2">A pointer to a block of memory to compare.</param> + /// <param name="length">Specifies the number of bytes to be compared.</param> + /// <returns>true, if all bytes compare as equal, false otherwise.</returns> + public static unsafe bool CompareMemory(IntPtr buf1, IntPtr buf2, uint length) + { + return (length == RtlCompareMemory(buf1.ToPointer(), buf2.ToPointer(), length)); + } + + /// <summary> + /// Compares blocks of memory. + /// </summary> + /// <param name="buf1">A pointer to a block of memory to compare.</param> + /// <param name="buf2">A pointer to a block of memory to compare.</param> + /// <param name="length">Specifies the number of bytes to be compared.</param> + /// <returns>true, if all bytes compare as equal, false otherwise.</returns> + public static unsafe bool CompareMemory(IntPtr buf1, IntPtr buf2, long length) + { + return (length == RtlCompareMemory(buf1.ToPointer(), buf2.ToPointer(), checked((uint)length))); + } + + /// <summary> + /// Moves a block of memory from one location to another. + /// </summary> + /// <param name="dst">A pointer to the starting address of the move destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to be moved.</param> + /// <param name="size">The size of the block of memory to move, in bytes.</param> + public static unsafe void MoveMemory(void* dst, void* src, long size) + { + MoveMemory(dst, src, checked((uint)size)); + } + + /// <summary> + /// Moves a block of memory from one location to another. + /// </summary> + /// <param name="dst">A pointer to the starting address of the move destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to be moved.</param> + /// <param name="size">The size of the block of memory to move, in bytes.</param> + public static unsafe void MoveMemory(IntPtr dst, IntPtr src, uint size) + { + MoveMemory(dst.ToPointer(), src.ToPointer(), size); + } + + /// <summary> + /// Moves a block of memory from one location to another. + /// </summary> + /// <param name="dst">A pointer to the starting address of the move destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to be moved.</param> + /// <param name="size">The size of the block of memory to move, in bytes.</param> + public static unsafe void MoveMemory(IntPtr dst, IntPtr src, long size) + { + MoveMemory(dst.ToPointer(), src.ToPointer(), checked((uint)size)); + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(void*, void*, uint)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(byte* dest, byte* src, int len) + { + if (len >= 0x10) + { + do + { + *((int*)dest) = *((int*)src); + *((int*)(dest + 4)) = *((int*)(src + 4)); + *((int*)(dest + 8)) = *((int*)(src + 8)); + *((int*)(dest + 12)) = *((int*)(src + 12)); + dest += 0x10; + src += 0x10; + } + while ((len -= 0x10) >= 0x10); + } + if (len > 0) + { + if ((len & 8) != 0) + { + *((int*)dest) = *((int*)src); + *((int*)(dest + 4)) = *((int*)(src + 4)); + dest += 8; + src += 8; + } + if ((len & 4) != 0) + { + *((int*)dest) = *((int*)src); + dest += 4; + src += 4; + } + if ((len & 2) != 0) + { + *((short*)dest) = *((short*)src); + dest += 2; + src += 2; + } + if ((len & 1) != 0) + { + *dest = *src; + } + } + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(void*, void*, long)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(byte* dest, byte* src, long len) + { + CopyMemory(dest, src, checked((int)len)); + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(void*, void*, long)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(void* dest, void* src, long len) + { + CopyMemory((byte*)dest, (byte*)src, checked((int)len)); + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(void*, void*, uint)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(void* dest, void* src, int len) + { + CopyMemory((byte*)dest, (byte*)src, len); + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(IntPtr, IntPtr, uint)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(IntPtr dest, IntPtr src, int len) + { + CopyMemory((byte*)dest, (byte*)src, len); + } + + /// <summary> + /// Copies a block of memory from one location to another. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + /// <remarks> + /// <b>CopyMemory</b> runs faster than <see cref="MoveMemory(IntPtr, IntPtr, long)"/>. + /// However, if both blocks overlap the result is undefined. + /// </remarks> + public static unsafe void CopyMemory(IntPtr dest, IntPtr src, long len) + { + CopyMemory((byte*)dest, (byte*)src, checked((int)len)); + } + + /// <summary> + /// Copies a block of memory into an array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, void* src, int len) + { + GCHandle handle = GCHandle.Alloc(dest, GCHandleType.Pinned); + try + { + CopyMemory((byte*)handle.AddrOfPinnedObject(), (byte*)src, len); + } + finally + { + handle.Free(); + } + } + + /// <summary> + /// Copies a block of memory into an array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, void* src, long len) + { + CopyMemory(dest, (byte*)src, checked((int)len)); + } + + /// <summary> + /// Copies a block of memory into an array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, IntPtr src, int len) + { + CopyMemory(dest, (byte*)src, len); + } + + /// <summary> + /// Copies a block of memory into an array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">A pointer to the starting address of the block of memory to copy.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, IntPtr src, long len) + { + CopyMemory(dest, (byte*)src, checked((int)len)); + } + + /// <summary> + /// Copies the content of an array to a memory location. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(void* dest, Array src, int len) + { + GCHandle handle = GCHandle.Alloc(src, GCHandleType.Pinned); + try + { + CopyMemory((byte*)dest, (byte*)handle.AddrOfPinnedObject(), len); + } + finally + { + handle.Free(); + } + } + + /// <summary> + /// Copies the content of an array to a memory location. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(void* dest, Array src, long len) + { + CopyMemory((byte*)dest, src, checked((int)len)); + } + + /// <summary> + /// Copies the content of an array to a memory location. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(IntPtr dest, Array src, int len) + { + CopyMemory((byte*)dest, src, len); + } + + /// <summary> + /// Copies the content of an array to a memory location. + /// </summary> + /// <param name="dest">A pointer to the starting address of the copied block's destination.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the block of memory to copy, in bytes.</param> + public static unsafe void CopyMemory(IntPtr dest, Array src, long len) + { + CopyMemory((byte*)dest, src, checked((int)len)); + } + + /// <summary> + /// Copies the content of one array into another array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the content to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, Array src, int len) + { + GCHandle dHandle = GCHandle.Alloc(dest, GCHandleType.Pinned); + try + { + GCHandle sHandle = GCHandle.Alloc(src, GCHandleType.Pinned); + try + { + CopyMemory((byte*)dHandle.AddrOfPinnedObject(), (byte*)sHandle.AddrOfPinnedObject(), len); + } + finally + { + sHandle.Free(); + } + } + finally + { + dHandle.Free(); + } + } + + /// <summary> + /// Copies the content of one array into another array. + /// </summary> + /// <param name="dest">An array used as the destination of the copy process.</param> + /// <param name="src">An array used as the source of the copy process.</param> + /// <param name="len">The size of the content to copy, in bytes.</param> + public static unsafe void CopyMemory(Array dest, Array src, long len) + { + CopyMemory(dest, src, checked((int)len)); + } + + internal static string ColorToString(Color color) + { + return string.Format( + System.Globalization.CultureInfo.CurrentCulture, + "{{Name={0}, ARGB=({1}, {2}, {3}, {4})}}", + new object[] { color.Name, color.A, color.R, color.G, color.B }); + } + + internal static void Resize(ref string str, int length) + { + if ((str != null) && (length >= 0) && (str.Length != length)) + { + char[] chars = str.ToCharArray(); + Array.Resize(ref chars, length); + str = new string(chars); + } + } + + internal static void Resize(ref string str, int min, int max) + { + if ((str != null) && (min >= 0) && (max >= 0) && (min <= max)) + { + if (str.Length < min) + { + char[] chars = str.ToCharArray(); + Array.Resize(ref chars, min); + str = new string(chars); + } + else if (str.Length > max) + { + char[] chars = str.ToCharArray(); + Array.Resize(ref chars, max); + str = new string(chars); + } + } + } + + internal static void Resize<T>(ref T[] array, int length) + { + if ((array != null) && (length >= 0) && (array.Length != length)) + { + Array.Resize(ref array, length); + } + } + + internal static void Resize<T>(ref T[] array, int min, int max) + { + if ((array != null) && (min >= 0) && (max >= 0) && (min <= max)) + { + if (array.Length < min) + { + Array.Resize(ref array, min); + } + else if (array.Length > max) + { + Array.Resize(ref array, max); + } + } + } + + internal static bool CheckColorType<T>(FREE_IMAGE_TYPE imageType, T color) + { + Type type = typeof(T); + bool result; + switch (imageType) + { + case FREE_IMAGE_TYPE.FIT_BITMAP: + result = (type == typeof(RGBQUAD)); break; + case FREE_IMAGE_TYPE.FIT_COMPLEX: + result = (type == typeof(FICOMPLEX)); break; + case FREE_IMAGE_TYPE.FIT_DOUBLE: + result = (type == typeof(double)); break; + case FREE_IMAGE_TYPE.FIT_FLOAT: + result = (type == typeof(float)); break; + case FREE_IMAGE_TYPE.FIT_INT16: + result = (type == typeof(Int16)); break; + case FREE_IMAGE_TYPE.FIT_INT32: + result = (type == typeof(Int32)); break; + case FREE_IMAGE_TYPE.FIT_RGB16: + result = (type == typeof(FIRGB16)); break; + case FREE_IMAGE_TYPE.FIT_RGBA16: + result = (type == typeof(FIRGBA16)); break; + case FREE_IMAGE_TYPE.FIT_RGBAF: + result = (type == typeof(FIRGBAF)); break; + case FREE_IMAGE_TYPE.FIT_RGBF: + result = (type == typeof(FIRGBF)); break; + case FREE_IMAGE_TYPE.FIT_UINT16: + result = (type == typeof(UInt16)); break; + case FREE_IMAGE_TYPE.FIT_UINT32: + result = (type == typeof(UInt32)); break; + default: + result = false; break; + } + return result; + } + + #endregion + + #region Dll-Imports + + /// <summary> + /// Retrieves a handle to a display device context (DC) for the client area of a specified window + /// or for the entire screen. You can use the returned handle in subsequent GDI functions to draw in the DC. + /// </summary> + /// <param name="hWnd">Handle to the window whose DC is to be retrieved. + /// If this value is IntPtr.Zero, GetDC retrieves the DC for the entire screen. </param> + /// <returns>If the function succeeds, the return value is a handle to the DC for the specified window's client area. + /// If the function fails, the return value is NULL.</returns> + [DllImport("user32.dll")] + private static extern IntPtr GetDC(IntPtr hWnd); + + /// <summary> + /// Releases a device context (DC), freeing it for use by other applications. + /// The effect of the ReleaseDC function depends on the type of DC. It frees only common and window DCs. + /// It has no effect on class or private DCs. + /// </summary> + /// <param name="hWnd">Handle to the window whose DC is to be released.</param> + /// <param name="hDC">Handle to the DC to be released.</param> + /// <returns>Returns true on success, false on failure.</returns> + [DllImport("user32.dll")] + private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + /// <summary> + /// Creates a DIB that applications can write to directly. + /// The function gives you a pointer to the location of the bitmap bit values. + /// You can supply a handle to a file-mapping object that the function will use to create the bitmap, + /// or you can let the system allocate the memory for the bitmap. + /// </summary> + /// <param name="hdc">Handle to a device context.</param> + /// <param name="pbmi">Pointer to a BITMAPINFO structure that specifies various attributes of the DIB, + /// including the bitmap dimensions and colors.</param> + /// <param name="iUsage">Specifies the type of data contained in the bmiColors array member of the BITMAPINFO structure + /// pointed to by pbmi (either logical palette indexes or literal RGB values).</param> + /// <param name="ppvBits">Pointer to a variable that receives a pointer to the location of the DIB bit values.</param> + /// <param name="hSection">Handle to a file-mapping object that the function will use to create the DIB. + /// This parameter can be NULL.</param> + /// <param name="dwOffset">Specifies the offset from the beginning of the file-mapping object referenced by hSection + /// where storage for the bitmap bit values is to begin. This value is ignored if hSection is NULL.</param> + /// <returns>If the function succeeds, the return value is a handle to the newly created DIB, + /// and *ppvBits points to the bitmap bit values. If the function fails, the return value is NULL, and *ppvBits is NULL.</returns> + [DllImport("gdi32.dll")] + private static extern IntPtr CreateDIBSection( + IntPtr hdc, + [In] IntPtr pbmi, + uint iUsage, + out IntPtr ppvBits, + IntPtr hSection, + uint dwOffset); + + /// <summary> + /// Deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. + /// After the object is deleted, the specified handle is no longer valid. + /// </summary> + /// <param name="hObject">Handle to a logical pen, brush, font, bitmap, region, or palette.</param> + /// <returns>Returns true on success, false on failure.</returns> + [DllImport("gdi32.dll")] + private static extern bool DeleteObject(IntPtr hObject); + + /// <summary> + /// Creates a compatible bitmap (DDB) from a DIB and, optionally, sets the bitmap bits. + /// </summary> + /// <param name="hdc">Handle to a device context.</param> + /// <param name="lpbmih">Pointer to a bitmap information header structure.</param> + /// <param name="fdwInit">Specifies how the system initializes the bitmap bits - (use 4).</param> + /// <param name="lpbInit">Pointer to an array of bytes containing the initial bitmap data.</param> + /// <param name="lpbmi">Pointer to a BITMAPINFO structure that describes the dimensions + /// and color format of the array pointed to by the lpbInit parameter.</param> + /// <param name="fuUsage">Specifies whether the bmiColors member of the BITMAPINFO structure + /// was initialized - (use 0).</param> + /// <returns>Handle to a DIB or null on failure.</returns> + [DllImport("gdi32.dll")] + private static extern IntPtr CreateDIBitmap( + IntPtr hdc, + IntPtr lpbmih, + uint fdwInit, + IntPtr lpbInit, + IntPtr lpbmi, + uint fuUsage); + + /// <summary> + /// Retrieves information for the specified graphics object. + /// </summary> + /// <param name="hgdiobj">Handle to the graphics object of interest.</param> + /// <param name="cbBuffer">Specifies the number of bytes of information to + /// be written to the buffer.</param> + /// <param name="lpvObject">Pointer to a buffer that receives the information + /// about the specified graphics object.</param> + /// <returns>0 on failure.</returns> + [DllImport("gdi32.dll")] + private static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject); + + /// <summary> + /// Retrieves the bits of the specified compatible bitmap and copies them into a buffer + /// as a DIB using the specified format. + /// </summary> + /// <param name="hdc">Handle to the device context.</param> + /// <param name="hbmp">Handle to the bitmap. This must be a compatible bitmap (DDB).</param> + /// <param name="uStartScan">Specifies the first scan line to retrieve.</param> + /// <param name="cScanLines">Specifies the number of scan lines to retrieve.</param> + /// <param name="lpvBits">Pointer to a buffer to receive the bitmap data.</param> + /// <param name="lpbmi">Pointer to a BITMAPINFO structure that specifies the desired + /// format for the DIB data.</param> + /// <param name="uUsage">Specifies the format of the bmiColors member of the + /// BITMAPINFO structure - (use 0).</param> + /// <returns>0 on failure.</returns> + [DllImport("gdi32.dll")] + private static extern unsafe int GetDIBits( + IntPtr hdc, + IntPtr hbmp, + uint uStartScan, + uint cScanLines, + IntPtr lpvBits, + IntPtr lpbmi, + uint uUsage); + + /// <summary> + /// Moves a block of memory from one location to another. + /// </summary> + /// <param name="dst">Pointer to the starting address of the move destination.</param> + /// <param name="src">Pointer to the starting address of the block of memory to be moved.</param> + /// <param name="size">Size of the block of memory to move, in bytes.</param> + [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] + public static unsafe extern void MoveMemory(void* dst, void* src, uint size); + + /// <summary> + /// The RtlCompareMemory routine compares blocks of memory + /// and returns the number of bytes that are equivalent. + /// </summary> + /// <param name="buf1">A pointer to a block of memory to compare.</param> + /// <param name="buf2">A pointer to a block of memory to compare.</param> + /// <param name="count">Specifies the number of bytes to be compared.</param> + /// <returns>RtlCompareMemory returns the number of bytes that compare as equal. + /// If all bytes compare as equal, the input Length is returned.</returns> + [DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)] + internal static unsafe extern uint RtlCompareMemory(void* buf1, void* buf2, uint count); + + #endregion + } +}
\ No newline at end of file |