// ========================================================== // 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.19 $ // $Date: 2011/10/02 13:00:45 $ // $Id: FreeImageWrapper.cs,v 1.19 2011/10/02 13:00:45 drolon 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 { /// /// Static class importing functions from the FreeImage library /// and providing additional functions. /// public static partial class FreeImage { #region Constants /// /// Array containing all 'FREE_IMAGE_MDMODEL's. /// public static readonly FREE_IMAGE_MDMODEL[] FREE_IMAGE_MDMODELS = (FREE_IMAGE_MDMODEL[])Enum.GetValues(typeof(FREE_IMAGE_MDMODEL)); /// /// Stores handles used to read from streams. /// private static Dictionary streamHandles = new Dictionary(); /// /// Version of the wrapper library. /// private static Version WrapperVersion; private const int DIB_RGB_COLORS = 0; private const int DIB_PAL_COLORS = 1; private const int CBM_INIT = 0x4; /// /// An uncompressed format. /// public const int BI_RGB = 0; /// /// 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. /// public const int BI_RLE8 = 1; /// /// 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. /// public const int BI_RLE4 = 2; /// /// Specifies that the bitmap is not compressed and that the color table consists of three /// DWORD 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. /// public const int BI_BITFIELDS = 3; /// /// Windows 98/Me, Windows 2000/XP: Indicates that the image is a JPEG image. /// public const int BI_JPEG = 4; /// /// Windows 98/Me, Windows 2000/XP: Indicates that the image is a PNG image. /// public const int BI_PNG = 5; #endregion #region General functions /// /// Returns the internal version of this FreeImage .NET wrapper. /// /// The internal version of this FreeImage .NET wrapper. 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; } /// /// Returns the version of the native FreeImage library. /// /// The version of the native FreeImage library. public static Version GetNativeVersion() { return new Version(GetVersion()); } /// /// Returns a value indicating if the FreeImage library is available or not. /// See remarks for further details. /// /// false if the file is not available or out of date; /// true, otherwise. /// /// 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 must be either in the same folder as the program's /// executable or in a folder contained in the envirent variable PATH /// (for example %WINDIR%\System32). /// Further more must both libraries, including the program itself, /// be the same architecture (x86 or x64). /// 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.Major == wrapperVersion.Major) && (nativeVersion.Minor > wrapperVersion.Minor)) || ((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 /// /// Creates a new bitmap in memory. /// /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new Bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap /// Handle to a FreeImage bitmap. public static FIBITMAP Allocate(int width, int height, int bpp) { return Allocate(width, height, bpp, 0, 0, 0); } /// /// Creates a new bitmap in memory. /// /// Type of the image. /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new Bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap /// Handle to a FreeImage bitmap. public static FIBITMAP AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp) { return AllocateT(type, width, height, bpp, 0, 0, 0); } /// /// 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. /// /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmaps. /// The color to fill the bitmap with or null. /// Options to enable or disable function-features. /// The palette of the bitmap or null. /// Handle to a FreeImage bitmap. /// /// This function is an extension to , 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. /// /// Basically, this function internally relies on function , followed by a /// call to . This is why both parameters /// and behave the same as it is /// documented for function . /// So, please refer to the documentation of to /// learn more about parameters and . /// /// The palette specified through parameter 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 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. /// /// However, specifying a palette is not necesarily needed, even for palletized images. This /// function is capable of implicitly creating a palette, if is null. /// If the specified background color is a greyscale value (red = green = blue) or if option /// 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. /// /// 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 /// is implicit, so the specified is applied to the palette entry, /// specified by the background color's field. /// The image is then filled with this palette index. /// /// This function returns a newly created image as function does, if both /// parameters and are null. /// If only is null, the palette pointed to by /// parameter is initially set for the new image, if a palletized /// image of type 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. /// 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); } /// /// 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. /// /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmaps. /// The color to fill the bitmap with or null. /// Options to enable or disable function-features. /// The palette of the bitmap or null. /// Red part of the color layout. /// eg: 0xFF0000 /// Green part of the color layout. /// eg: 0x00FF00 /// Blue part of the color layout. /// eg: 0x0000FF /// Handle to a FreeImage bitmap. /// /// This function is an extension to , 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. /// /// Basically, this function internally relies on function , followed by a /// call to . This is why both parameters /// and behave the same as it is /// documented for function . /// So, please refer to the documentation of to /// learn more about parameters and . /// /// The palette specified through parameter 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 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. /// /// However, specifying a palette is not necesarily needed, even for palletized images. This /// function is capable of implicitly creating a palette, if is null. /// If the specified background color is a greyscale value (red = green = blue) or if option /// 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. /// /// 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 /// is implicit, so the specified is applied to the palette entry, /// specified by the background color's field. /// The image is then filled with this palette index. /// /// This function returns a newly created image as function does, if both /// parameters and are null. /// If only is null, the palette pointed to by /// parameter is initially set for the new image, if a palletized /// image of type 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. /// 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); } } /// /// 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. /// /// The type of the specified color. /// Type of the image. /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap /// The color to fill the bitmap with or null. /// Options to enable or disable function-features. /// The palette of the bitmap or null. /// Handle to a FreeImage bitmap. /// /// This function is an extension to , 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. /// /// Basically, this function internally relies on function , followed by a /// call to . This is why both parameters /// and behave the same as it is /// documented for function . So, please refer to the /// documentation of to learn more about parameters color and options. /// /// The palette specified through parameter palette is only copied to the newly created /// image, if its image type is and the desired bit /// depth is smaller than or equal to 8 bits per pixel. In other words, the /// palette is only taken into account for palletized images. However, if the preceding conditions /// match and if is not null, 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. /// /// However, specifying a palette is not necesarily needed, even for palletized images. This /// function is capable of implicitly creating a palette, if is null. /// If the specified background color is a greyscale value (red = green = blue) or if option /// 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. /// /// 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 /// is implicit, so the specified color is applied to the palette entry, specified by the /// background color's field. The image is then filled with /// this palette index. /// /// This function returns a newly created image as function does, if both /// parameters and are null. /// If only is null, the palette pointed to by /// parameter is initially set for the new image, if a palletized /// image of type 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. /// public static FIBITMAP AllocateExT(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); } /// /// 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. /// /// The type of the specified color. /// Type of the image. /// Width of the new bitmap. /// Height of the new bitmap. /// Bit depth of the new bitmap. /// Supported pixel depth: 1-, 4-, 8-, 16-, 24-, 32-bit per pixel for standard bitmap /// The color to fill the bitmap with or null. /// Options to enable or disable function-features. /// The palette of the bitmap or null. /// Red part of the color layout. /// eg: 0xFF0000 /// Green part of the color layout. /// eg: 0x00FF00 /// Blue part of the color layout. /// eg: 0x0000FF /// Handle to a FreeImage bitmap. /// /// This function is an extension to , 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. /// /// Basically, this function internally relies on function , followed by a /// call to . This is why both parameters /// and behave the same as it is /// documented for function . So, please refer to the /// documentation of to learn more about parameters color and options. /// /// The palette specified through parameter palette is only copied to the newly created /// image, if its image type is and the desired bit /// depth is smaller than or equal to 8 bits per pixel. In other words, the /// palette is only taken into account for palletized images. However, if the preceding conditions /// match and if is not null, 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. /// /// However, specifying a palette is not necesarily needed, even for palletized images. This /// function is capable of implicitly creating a palette, if is null. /// If the specified background color is a greyscale value (red = green = blue) or if option /// 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. /// /// 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 /// is implicit, so the specified color is applied to the palette entry, specified by the /// background color's field. The image is then filled with /// this palette index. /// /// This function returns a newly created image as function does, if both /// parameters and are null. /// If only is null, the palette pointed to by /// parameter is initially set for the new image, if a palletized /// image of type 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. /// public static FIBITMAP AllocateExT(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 (color.HasValue) { if (!CheckColorType(type, color.Value)) return FIBITMAP.Zero; 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); } } /// /// Converts a FreeImage bitmap to a .NET . /// /// Handle to a FreeImage bitmap. /// The converted .NET . /// Copying metadata has been disabled until a proper way /// of reading and storing metadata in a .NET bitmap is found. /// /// is null. /// /// The image type of is not FIT_BITMAP. public static Bitmap GetBitmap(FIBITMAP dib) { return GetBitmap(dib, true); } /// /// Converts a FreeImage bitmap to a .NET . /// /// Handle to a FreeImage bitmap. /// When true existing metadata will be copied. /// The converted .NET . /// Copying metadata has been disabled until a proper way /// of reading and storing metadata in a .NET bitmap is found. /// /// is null. /// /// The image type of is not FIT_BITMAP. 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 bitmap resolution if((GetResolutionX(dib) > 0) && (GetResolutionY(dib) > 0)) { // SetResolution will throw an exception when zero values are given on input 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 list = new List(); // 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; } /// /// Converts an .NET into a FreeImage bitmap. /// /// The to convert. /// Handle to a FreeImage bitmap. /// Copying metadata has been disabled until a proper way /// of reading and storing metadata in a .NET bitmap is found. /// /// is null. /// /// The bitmaps pixelformat is invalid. public static FIBITMAP CreateFromBitmap(Bitmap bitmap) { return CreateFromBitmap(bitmap, false); } /// /// Converts an .NET into a FreeImage bitmap. /// /// The to convert. /// When true existing metadata will be copied. /// Handle to a FreeImage bitmap. /// Copying metadata has been disabled until a proper way /// of reading and storing metadata in a .NET bitmap is found. /// /// is null. /// /// The bitmaps pixelformat is invalid. 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; } /// /// Converts a raw bitmap to a FreeImage bitmap. /// /// Array of bytes containing the raw bitmap. /// The type of the raw bitmap. /// The width in pixels of the raw bitmap. /// The height in pixels of the raw bitmap. /// Defines the total width of a scanline in the raw bitmap, /// including padding bytes. /// The bit depth (bits per pixel) of the raw bitmap. /// 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. /// 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. /// 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. /// 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. /// Handle to a FreeImage bitmap. 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); } } /// /// Converts a raw bitmap to a FreeImage bitmap. /// /// Pointer to the memory block containing the raw bitmap. /// The type of the raw bitmap. /// The width in pixels of the raw bitmap. /// The height in pixels of the raw bitmap. /// Defines the total width of a scanline in the raw bitmap, /// including padding bytes. /// The bit depth (bits per pixel) of the raw bitmap. /// 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. /// 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. /// 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. /// 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. /// Handle to a FreeImage bitmap. 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; } /// /// Saves a .NET to a file. /// /// The .NET to save. /// Name of the file to save to. /// Returns true on success, false on failure. /// /// or is null. /// /// The bitmaps pixelformat is invalid. public static bool SaveBitmap(Bitmap bitmap, string filename) { return SaveBitmap( bitmap, filename, FREE_IMAGE_FORMAT.FIF_UNKNOWN, FREE_IMAGE_SAVE_FLAGS.DEFAULT); } /// /// Saves a .NET to a file. /// /// The .NET to save. /// Name of the file to save to. /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. /// /// or is null. /// /// The bitmaps pixelformat is invalid. public static bool SaveBitmap(Bitmap bitmap, string filename, FREE_IMAGE_SAVE_FLAGS flags) { return SaveBitmap( bitmap, filename, FREE_IMAGE_FORMAT.FIF_UNKNOWN, flags); } /// /// Saves a .NET to a file. /// /// The .NET to save. /// Name of the file to save to. /// Format of the bitmap. If the format should be taken from the /// filename use . /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. /// /// or is null. /// /// The bitmaps pixelformat is invalid. 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; } /// /// Loads a FreeImage bitmap. /// The file will be loaded with default loading flags. /// /// The complete name of the file to load. /// Handle to a FreeImage bitmap. /// /// does not exists. 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); } /// /// Loads a FreeImage bitmap. /// Load flags can be provided by the flags parameter. /// /// The complete name of the file to load. /// Flags to enable or disable plugin-features. /// Handle to a FreeImage bitmap. /// /// does not exists. 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); } /// /// Loads a FreeImage bitmap. /// In case the loading format is the files /// real format is being analysed. If no plugin can read the file, format remains /// and 0 is returned. /// The file will be loaded with default loading flags. /// /// The complete name of the file to load. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadEx it will be returned in format. /// Handle to a FreeImage bitmap. /// /// does not exists. public static FIBITMAP LoadEx(string filename, ref FREE_IMAGE_FORMAT format) { return LoadEx(filename, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); } /// /// Loads a FreeImage bitmap. /// In case the loading format is the files /// real format is being analysed. If no plugin can read the file, format remains /// and 0 is returned. /// Load flags can be provided by the flags parameter. /// /// The complete name of the file to load. /// Flags to enable or disable plugin-features. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadEx it will be returned in format. /// /// Handle to a FreeImage bitmap. /// /// does not exists. 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; } /// /// Loads a .NET from a file. /// /// Name of the file to be loaded. /// Format of the image. If the format should be taken from the /// filename use . /// Flags to enable or disable plugin-features. /// The loaded .NET . /// /// does not exists. /// /// The image type of the image is not . 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; } /// /// Deletes a previously loaded FreeImage bitmap from memory and resets the handle to 0. /// /// Handle to a FreeImage bitmap. public static void UnloadEx(ref FIBITMAP dib) { if (!dib.IsNull) { Unload(dib); dib.SetNull(); } } /// /// 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. /// /// Handle to a FreeImage bitmap. /// 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. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// Saves a previously loaded FreeImage bitmap to a file. /// In case the loading format is /// the format is taken off the filename. /// If no suitable format was found false will be returned. /// /// Handle to a FreeImage bitmap. /// 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. /// Format of the image. If the format should be taken from the /// filename use . /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// 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. /// /// Handle to a FreeImage bitmap. /// 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. /// When true the structure will be unloaded on success. /// If the function failed and returned false, the bitmap was not unloaded. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// 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. /// /// Handle to a FreeImage bitmap. /// 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 /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// 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. /// /// Handle to a FreeImage bitmap. /// 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. /// Flags to enable or disable plugin-features. /// When true the structure will be unloaded on success. /// If the function failed and returned false, the bitmap was not unloaded. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// Saves a previously loaded FreeImage bitmap to a file. /// In case the loading format is /// the format is taken off the filename. /// If no suitable format was found false will be returned. /// /// Handle to a FreeImage bitmap. /// 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. /// Format of the image. If the format should be taken from the /// filename use . /// When true the structure will be unloaded on success. /// If the function failed and returned false, the bitmap was not unloaded. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// Saves a previously loaded FreeImage bitmap to a file. /// In case the loading format is /// 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. /// /// Handle to a FreeImage bitmap. /// 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. /// Format of the image. If the format should be taken from the /// filename use . /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. /// /// or is null. 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); } /// /// Saves a previously loaded FreeImage bitmap to a file. /// In case the loading format is /// 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 a suitable color depth /// will be taken if available. /// /// Handle to a FreeImage bitmap. /// 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. /// Format of the image. If the format should be taken from the /// filename use . /// Flags to enable or disable plugin-features. /// The new color depth of the bitmap. /// Set to 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. /// When true the structure will be unloaded on success. /// If the function failed and returned false, the bitmap was not unloaded. /// Returns true on success, false on failure. /// /// A direct color conversion failed. /// /// or is null. 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; } /// /// Loads a FreeImage bitmap. /// The stream must be set to the correct position before calling LoadFromStream. /// /// The stream to read from. /// Handle to a FreeImage bitmap. /// /// is null. /// /// is not capable of reading. 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); } /// /// Loads a FreeImage bitmap. /// The stream must be set to the correct position before calling LoadFromStream. /// /// The stream to read from. /// Flags to enable or disable plugin-features. /// Handle to a FreeImage bitmap. /// /// is null. /// /// is not capable of reading. 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); } /// /// Loads a FreeImage bitmap. /// In case the loading format is the /// bitmaps real format is being analysed. /// The stream must be set to the correct position before calling LoadFromStream. /// /// The stream to read from. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadFromStream it will be returned in format. /// Handle to a FreeImage bitmap. /// /// is null. /// /// is not capable of reading. public static FIBITMAP LoadFromStream(Stream stream, ref FREE_IMAGE_FORMAT format) { return LoadFromStream(stream, FREE_IMAGE_LOAD_FLAGS.DEFAULT, ref format); } /// /// Loads a FreeImage bitmap. /// In case the loading format is /// the bitmaps real format is being analysed. /// The stream must be set to the correct position before calling LoadFromStream. /// /// The stream to read from. /// Flags to enable or disable plugin-features. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadFromStream it will be returned in format. /// Handle to a FreeImage bitmap. /// /// is null. /// /// is not capable of reading. 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); } } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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); } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// When true the structure will be unloaded on success. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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); } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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); } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// Flags to enable or disable plugin-features. /// When true the structure will be unloaded on success. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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); } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// Flags to enable or disable plugin-features. /// The new color depth of the bitmap. /// Set to 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. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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); } /// /// Saves a previously loaded FreeImage bitmap to a stream. /// The stream must be set to the correct position before calling SaveToStream. /// /// Handle to a FreeImage bitmap. /// The stream to write to. /// Format of the image. /// Flags to enable or disable plugin-features. /// The new color depth of the bitmap. /// Set to 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. /// When true the structure will be unloaded on success. /// Returns true on success, false on failure. /// /// or is null. /// /// cannot write. 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 /// /// Checks if an extension is valid for a certain format. /// /// The desired format. /// The desired extension. /// True if the extension is valid for the given format, false otherwise. /// /// is null. public static bool IsExtensionValidForFIF(FREE_IMAGE_FORMAT fif, string extension) { return IsExtensionValidForFIF(fif, extension, StringComparison.CurrentCultureIgnoreCase); } /// /// Checks if an extension is valid for a certain format. /// /// The desired format. /// The desired extension. /// The string comparison type. /// True if the extension is valid for the given format, false otherwise. /// /// is null. 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; } /// /// Checks if a filename is valid for a certain format. /// /// The desired format. /// The desired filename. /// True if the filename is valid for the given format, false otherwise. /// /// is null. public static bool IsFilenameValidForFIF(FREE_IMAGE_FORMAT fif, string filename) { return IsFilenameValidForFIF(fif, filename, StringComparison.CurrentCultureIgnoreCase); } /// /// Checks if a filename is valid for a certain format. /// /// The desired format. /// The desired filename. /// The string comparison type. /// True if the filename is valid for the given format, false otherwise. /// /// is null. 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; } /// /// 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. /// The image format to obtain the primary extension for. /// The primary extension of the specified image format. 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 /// /// Loads a FreeImage multi-paged bitmap. /// /// The complete name of the file to load. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// /// The complete name of the file to load. /// When true performance is increased at the cost of memory. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// /// The complete name of the file to load. /// When true the bitmap will be loaded read only. /// When true performance is increased at the cost of memory. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// /// The complete name of the file to load. /// When true a new bitmap is created. /// When true the bitmap will be loaded read only. /// When true performance is increased at the cost of memory. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// In case the loading format is the files real /// format is being analysed. If no plugin can read the file, format remains /// and 0 is returned. /// /// The complete name of the file to load. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadEx it will be returned in format. /// When true a new bitmap is created. /// When true the bitmap will be loaded read only. /// When true performance is increased at the cost of memory. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// In case the loading format is the files /// real format is being analysed. If no plugin can read the file, format remains /// and 0 is returned. /// Load flags can be provided by the flags parameter. /// /// The complete name of the file to load. /// Format of the image. If the format is unknown use /// . /// In case a suitable format was found by LoadEx it will be returned in format. /// Flags to enable or disable plugin-features. /// When true a new bitmap is created. /// When true the bitmap will be loaded read only. /// When true performance is increased at the cost of memory. /// Handle to a FreeImage multi-paged bitmap. /// /// does not exists while opening. 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; } /// /// Loads a FreeImage multi-paged bitmap. /// /// The stream to load the bitmap from. /// Handle to a FreeImage multi-paged bitmap. 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); } /// /// Loads a FreeImage multi-paged bitmap. /// In case the loading format is the files /// real format is being analysed. If no plugin can read the file, format remains /// and 0 is returned. /// Load flags can be provided by the flags parameter. /// /// The stream to load the bitmap from. /// Format of the image. If the format is unknown use /// . /// Flags to enable or disable plugin-features. /// Handle to a FreeImage multi-paged bitmap. 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 { lock (streamHandles) { streamHandles.Add(mdib, handle); } } return mdib; } catch { if (!mdib.IsNull) CloseMultiBitmap(mdib, FREE_IMAGE_SAVE_FLAGS.DEFAULT); if (handle != null) handle.Dispose(); throw; } } /// /// Closes a previously opened multi-page bitmap and, when the bitmap was not opened read-only, applies any changes made to it. /// /// Handle to a FreeImage multi-paged bitmap. /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. public static bool CloseMultiBitmap(FIMULTIBITMAP bitmap, FREE_IMAGE_SAVE_FLAGS flags) { if (CloseMultiBitmap_(bitmap, flags)) { fi_handle handle; lock (streamHandles) { if (streamHandles.TryGetValue(bitmap, out handle)) { streamHandles.Remove(bitmap); handle.Dispose(); } } return true; } return false; } /// /// 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. /// /// Handle to a FreeImage multi-paged bitmap. /// Returns true on success, false on failure. public static bool CloseMultiBitmapEx(ref FIMULTIBITMAP bitmap) { return CloseMultiBitmapEx(ref bitmap, FREE_IMAGE_SAVE_FLAGS.DEFAULT); } /// /// 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. /// /// Handle to a FreeImage multi-paged bitmap. /// Flags to enable or disable plugin-features. /// Returns true on success, false on failure. 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; } /// /// Retrieves the number of pages that are locked in a multi-paged bitmap. /// /// Handle to a FreeImage multi-paged bitmap. /// Number of locked pages. /// /// is null. public static int GetLockedPageCount(FIMULTIBITMAP dib) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } int result = 0; GetLockedPageNumbers(dib, null, ref result); return result; } /// /// Retrieves a list locked pages of a multi-paged bitmap. /// /// Handle to a FreeImage multi-paged bitmap. /// List containing the indexes of the locked pages. /// /// is null. 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; } /// /// 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 /// , /// , /// or /// . /// /// The stream to read from. /// Format of the image. /// Flags to enable or disable plugin-features. /// The temporary memory buffer used to load the bitmap. /// Handle to a FreeImage multi-paged bitmap. /// /// is null. /// /// can not read. 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 /// /// 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. /// /// Name of the stream to analyze. /// Type of the bitmap. /// /// is null. /// /// can not read. 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 /// /// Retrieves an hBitmap for a FreeImage bitmap. /// Call FreeHbitmap(IntPtr) to free the handle. /// /// Handle to a FreeImage bitmap. /// A reference device context. /// Use IntPtr.Zero if no reference is available. /// When true dib will be unloaded if the function succeeded. /// The hBitmap for the FreeImage bitmap. /// /// is null. 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; } /// /// Returns an HBITMAP created by the CreateDIBitmap() function which in turn /// has always the same color depth as the reference DC, which may be provided /// through . The desktop DC will be used, /// if IntPtr.Zero DC is specified. /// Call to free the handle. /// /// Handle to a FreeImage bitmap. /// Handle to a device context. /// When true the structure will be unloaded on success. /// If the function failed and returned false, the bitmap was not unloaded. /// If the function succeeds, the return value is a handle to the /// compatible bitmap. If the function fails, the return value is . /// /// is null. 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; } /// /// Creates a FreeImage DIB from a Device Context/Compatible Bitmap. /// /// Handle to the bitmap. /// Handle to a device context. /// Handle to a FreeImage bitmap. /// /// is null. 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; } /// /// Frees a bitmap handle. /// /// Handle to a bitmap. /// True on success, false on failure. public static bool FreeHbitmap(IntPtr hbitmap) { return DeleteObject(hbitmap); } #endregion #region Bitmap information functions /// /// Retrieves a DIB's resolution in X-direction measured in 'dots per inch' (DPI) and not in /// 'dots per meter'. /// /// Handle to a FreeImage bitmap. /// The resolution in 'dots per inch'. /// /// is null. public static uint GetResolutionX(FIBITMAP dib) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } return (uint)(0.5d + 0.0254d * GetDotsPerMeterX(dib)); } /// /// Retrieves a DIB's resolution in Y-direction measured in 'dots per inch' (DPI) and not in /// 'dots per meter'. /// /// Handle to a FreeImage bitmap. /// The resolution in 'dots per inch'. /// /// is null. public static uint GetResolutionY(FIBITMAP dib) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } return (uint)(0.5d + 0.0254d * GetDotsPerMeterY(dib)); } /// /// Sets a DIB's resolution in X-direction measured in 'dots per inch' (DPI) and not in /// 'dots per meter'. /// /// Handle to a FreeImage bitmap. /// The new resolution in 'dots per inch'. /// /// is null. public static void SetResolutionX(FIBITMAP dib, uint res) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } SetDotsPerMeterX(dib, (uint)((double)res / 0.0254d + 0.5d)); } /// /// Sets a DIB's resolution in Y-direction measured in 'dots per inch' (DPI) and not in /// 'dots per meter'. /// /// Handle to a FreeImage bitmap. /// The new resolution in 'dots per inch'. /// /// is null. public static void SetResolutionY(FIBITMAP dib, uint res) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } SetDotsPerMeterY(dib, (uint)((double)res / 0.0254d + 0.5d)); } /// /// 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. /// /// Handle to a FreeImage bitmap. /// True if the image is a greyscale image, else false. /// /// is null. 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; } /// /// Returns a structure that represents the palette of a FreeImage bitmap. /// /// Handle to a FreeImage bitmap. /// A structure representing the bitmaps palette. /// /// is null. public static Palette GetPaletteEx(FIBITMAP dib) { return new Palette(dib); } /// /// Returns the structure of a FreeImage bitmap. /// The structure is a copy, so changes will have no effect on /// the bitmap itself. /// /// Handle to a FreeImage bitmap. /// structure of the bitmap. /// /// is null. public static unsafe BITMAPINFOHEADER GetInfoHeaderEx(FIBITMAP dib) { if (dib.IsNull) { throw new ArgumentNullException("dib"); } return *(BITMAPINFOHEADER*)GetInfoHeader(dib); } /// /// Returns the structure of a FreeImage bitmap. /// The structure is a copy, so changes will have no effect on /// the bitmap itself. /// /// Handle to a FreeImage bitmap. /// structure of the bitmap. /// /// is null. 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(ptr, (int)result.bmiHeader.biClrUsed).Data; } return result; } /// /// Returns the pixelformat of the bitmap. /// /// Handle to a FreeImage bitmap. /// of the bitmap. /// /// is null. 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; } /// /// Retrieves all parameters needed to create a new FreeImage bitmap from /// the format of a .NET . /// /// The /// of the .NET . /// Returns the type used for the new bitmap. /// Returns the color depth for the new bitmap. /// Returns the red_mask for the new bitmap. /// Returns the green_mask for the new bitmap. /// Returns the blue_mask for the new bitmap. /// True in case a matching conversion exists; else false. /// 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; } /// /// Returns the for the specified /// . /// /// The /// for which to return the corresponding . /// The for the specified /// 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; } /// /// Retrieves all parameters needed to create a new FreeImage bitmap from /// raw bits . /// /// The /// of the data in memory. /// The color depth for the data. /// Returns the red_mask for the data. /// Returns the green_mask for the data. /// Returns the blue_mask for the data. /// True in case a matching conversion exists; else false. /// 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; } /// /// Compares two FreeImage bitmaps. /// /// The first bitmap to compare. /// The second bitmap to compare. /// Determines which components of the bitmaps will be compared. /// True in case both bitmaps match the compare conditions, false otherwise. 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; } /// /// Returns the FreeImage bitmap's transparency table. /// The array is empty in case the bitmap has no transparency table. /// /// Handle to a FreeImage bitmap. /// The FreeImage bitmap's transparency table. /// /// is null. 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; } /// /// Set the FreeImage bitmap's transparency table. Only affects palletised bitmaps. /// /// Handle to a FreeImage bitmap. /// The FreeImage bitmap's new transparency table. /// /// or is null. 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); } /// /// 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 /// type images only. /// /// Handle to a FreeImage bitmap. /// Returns the number of unique colors used by the image specified or /// zero, if the image type cannot be handled. /// /// is null. 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; } /// /// Verifies whether the FreeImage bitmap is 16bit 555. /// /// The FreeImage bitmap to verify. /// true if the bitmap is RGB16-555; otherwise false. 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)); } /// /// Verifies whether the FreeImage bitmap is 16bit 565. /// /// The FreeImage bitmap to verify. /// true if the bitmap is RGB16-565; otherwise false. 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 /// /// Creates a new ICC-Profile for a FreeImage bitmap. /// /// Handle to a FreeImage bitmap. /// The data of the new ICC-Profile. /// The new ICC-Profile of the bitmap. /// /// is null. public static FIICCPROFILE CreateICCProfileEx(FIBITMAP dib, byte[] data) { return new FIICCPROFILE(dib, data); } /// /// Creates a new ICC-Profile for a FreeImage bitmap. /// /// Handle to a FreeImage bitmap. /// The data of the new ICC-Profile. /// The number of bytes of to use. /// The new ICC-Profile of the FreeImage bitmap. /// /// is null. public static FIICCPROFILE CreateICCProfileEx(FIBITMAP dib, byte[] data, int size) { return new FIICCPROFILE(dib, data, size); } #endregion #region Conversion functions /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// When true the structure will be unloaded on success. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Threshold value when converting with /// . /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Dither algorithm when converting /// with . /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// The quantization algorithm for conversion to 8-bit color depth. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Threshold value when converting with /// . /// When true the structure will be unloaded on success. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Dither algorithm when converting with /// . /// When true the structure will be unloaded on success. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// The quantization algorithm for conversion to 8-bit color depth. /// When true the structure will be unloaded on success. /// Handle to a FreeImage bitmap. /// /// is null. 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); } /// /// Converts a FreeImage bitmap from one color depth to another. /// If the conversion fails the original FreeImage bitmap is returned. /// /// Handle to a FreeImage bitmap. /// The desired output format. /// Threshold value when converting with /// . /// Dither algorithm when converting with /// . /// The quantization algorithm for conversion to 8-bit color depth. /// When true the structure will be unloaded on success. /// Handle to a FreeImage bitmap. /// /// is null. 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; } /// /// ColorQuantizeEx is an extension to the /// 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. /// /// Handle to a FreeImage bitmap. /// Specifies the color reduction algorithm to be used. /// Size of the desired output palette. /// The provided palette. /// true to create a bitmap with the smallest possible /// color depth for the specified . /// Handle to a FreeImage bitmap. 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; } /// /// ColorQuantizeEx is an extension to the /// 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. /// /// Handle to a FreeImage bitmap. /// Specifies the color reduction algorithm to be used. /// Size of the desired output palette. /// The provided palette. /// The desired color depth of the created image. /// Handle to a FreeImage bitmap. 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 srcScanline = new Scanline(temp, y); Scanline dstScanline = new Scanline(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 srcScanline = new Scanline(temp, y); Scanline dstScanline = new Scanline(result, y); for (int x = (int)GetWidth(temp) - 1; x >= 0; x--) { dstScanline[x] = srcScanline[x]; } } } } UnloadEx(ref temp); return result; } } #endregion #region Metadata /// /// Copies metadata from one FreeImage bitmap to another. /// /// Source FreeImage bitmap containing the metadata. /// FreeImage bitmap to copy the metadata to. /// Flags to switch different copy modes. /// Returns -1 on failure else the number of copied tags. /// /// or is null. 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; } /// /// Returns the comment of a JPEG, PNG or GIF image. /// /// Handle to a FreeImage bitmap. /// Comment of the FreeImage bitmp, or null in case no comment exists. /// /// is null. 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; } /// /// Sets the comment of a JPEG, PNG or GIF image. /// /// Handle to a FreeImage bitmap. /// New comment of the FreeImage bitmap. /// Use null to remove the comment. /// Returns true on success, false on failure. /// /// is null. 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; } /// /// Retrieve a metadata attached to a FreeImage bitmap. /// /// The metadata model to look for. /// Handle to a FreeImage bitmap. /// The metadata field name. /// A structure returned by the function. /// Returns true on success, false on failure. /// /// is null. 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; } /// /// Attach a new metadata tag to a FreeImage bitmap. /// /// The metadata model used to store the tag. /// Handle to a FreeImage bitmap. /// The tag field name. /// The to be attached. /// Returns true on success, false on failure. /// /// is null. 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); } /// /// Provides information about the first instance of a tag that matches the metadata model. /// /// The model to match. /// Handle to a FreeImage bitmap. /// Tag that matches the metadata model. /// Unique search handle that can be used to call FindNextMetadata or FindCloseMetadata. /// Null if the metadata model does not exist. /// /// is null. 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; } /// /// 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. /// /// Unique search handle provided by FindFirstMetadata. /// Tag that matches the metadata model. /// Returns true on success, false on failure. 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; } /// /// Closes the specified metadata search handle and releases associated resources. /// /// The handle to close. public static void FindCloseMetadata(FIMETADATA mdhandle) { if (metaDataSearchHandler.ContainsKey(mdhandle)) { metaDataSearchHandler.Remove(mdhandle); } FindCloseMetadata_(mdhandle); } /// /// This dictionary links FIMETADATA handles and FREE_IMAGE_MDMODEL models. /// private static Dictionary metaDataSearchHandler = new Dictionary(1); #endregion #region Rotation and Flipping /// /// 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°. /// null is returned for other values. /// /// Handle to a FreeImage bitmap. /// The angle of rotation. /// Handle to a FreeImage bitmap. public static FIBITMAP Rotate(FIBITMAP dib, double angle) { return Rotate(dib, angle, IntPtr.Zero); } /// /// 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°. /// null is returned for other values. /// /// The type of the color to use as background. /// Handle to a FreeImage bitmap. /// The angle of rotation. /// The color used used to fill the bitmap's background. /// Handle to a FreeImage bitmap. public static FIBITMAP Rotate(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); } } /// /// Rotates a 4-bit color FreeImage bitmap. /// Allowed values for are 90, 180 and 270. /// In case is 0 or 360 a clone is returned. /// 0 is returned for other values or in case the rotation fails. /// /// Handle to a FreeImage bitmap. /// The angle of rotation. /// Handle to a FreeImage bitmap. /// /// This function is kind of temporary due to FreeImage's lack of /// rotating 4-bit images. It's particularly used by 's /// method RotateFlip. This function will be removed as soon as FreeImage /// supports rotating 4-bit images. /// /// /// is null. 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[] 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 /// /// Enlarges or shrinks the FreeImage bitmap selectively per side and fills newly added areas /// with the specified background color. See remarks for further details. /// /// The type of the specified color. /// Handle to a FreeImage bitmap. /// The number of pixels, the image should be enlarged on its left side. /// Negative values shrink the image on its left side. /// The number of pixels, the image should be enlarged on its top side. /// Negative values shrink the image on its top side. /// The number of pixels, the image should be enlarged on its right side. /// Negative values shrink the image on its right side. /// The number of pixels, the image should be enlarged on its bottom side. /// Negative values shrink the image on its bottom side. /// The color, the enlarged sides of the image should be filled with. /// Options that affect the color search process for palletized images. /// Handle to a FreeImage bitmap. /// /// 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 , , /// or . This value represents the border's /// width in pixels. Newly created parts of the image (the border areas) are filled with the /// specified . /// 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. /// /// So, calling this function with all parameters , , /// and set to zero, is /// effectively the same as calling function ; setting all parameters /// , , and /// to value equal to or smaller than zero, my easily be substituted /// by a call to function . Both these cases produce a new image, which is /// guaranteed not to be larger than the input image. Thus, since the specified /// is not needed in these cases, /// may be null. /// /// Both parameters and work according to /// function . So, please refer to the documentation of /// to learn more about parameters /// and . 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. /// /// /// // create a white color
/// RGBQUAD c;
/// c.rgbRed = 0xFF;
/// c.rgbGreen = 0xFF;
/// c.rgbBlue = 0xFF;
/// c.rgbReserved = 0x00;
///
/// // add a white, symmetric 10 pixel wide border to the image
/// dib2 = FreeImage_EnlargeCanvas(dib, 10, 10, 10, 10, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);
///
/// // add white, 20 pixel wide stripes to the top and bottom side of the image
/// dib3 = FreeImage_EnlargeCanvas(dib, 0, 20, 0, 20, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);
///
/// // add white, 30 pixel wide stripes to the right side of the image and
/// // cut off the 40 leftmost pixel columns
/// dib3 = FreeImage_EnlargeCanvas(dib, -40, 0, 30, 0, c, FREE_IMAGE_COLOR_OPTIONS.FICO_RGB);
///
public static FIBITMAP EnlargeCanvas(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 (color.HasValue) { if (!CheckColorType(GetImageType(dib), color.Value)) return FIBITMAP.Zero; 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 /// /// Sets all pixels of the specified image to the color provided through the /// parameter. See remarks for further details. /// /// The type of the specified color. /// Handle to a FreeImage bitmap. /// The color to fill the bitmap with. See remarks for further details. /// Options that affect the color search process for palletized images. /// true on success, false on failure. /// /// This function sets all pixels of an image to the color provided through /// the parameter. is used for standard type images. /// For non standard type images the underlaying structure is used. /// /// So, must be of type , if the image to be filled is of type /// and must be a structure if the /// image is of type and so on. /// /// However, the fill color is always specified through a structure /// for all images of type . /// So, for 32- and 24-bit images, the red, green and blue members of the /// structure are directly used for the image's red, green and blue channel respectively. /// Although alpha transparent 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 field is only taken into account, /// if option has been specified. /// /// 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. /// /// 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: /// /// /// Value /// Meaning /// /// /// /// /// 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. /// /// /// /// /// /// 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 /// 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. /// /// /// /// /// /// 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. /// /// /// /// public static bool FillBackground(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 /// /// Returns the next higher possible color depth. /// /// Color depth to increase. /// The next higher color depth or 0 if there is no valid color depth. 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; } /// /// Returns the next lower possible color depth. /// /// Color depth to decrease. /// The next lower color depth or 0 if there is no valid color depth. 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; } /// /// Reads a null-terminated c-string. /// /// Pointer to the first char of the string. /// The converted string. 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 newPalette = new List(size); List lut = new List(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[] Get04BitScanlines(FIBITMAP dib) { int height = (int)GetHeight(dib); Scanline[] array = new Scanline[height]; for (int i = 0; i < height; i++) { array[i] = new Scanline(dib, i); } return array; } /// /// Changes a bitmaps color depth. /// Used by SaveEx and SaveToStream. /// 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; } /// /// Compares blocks of memory. /// /// A pointer to a block of memory to compare. /// A pointer to a block of memory to compare. /// Specifies the number of bytes to be compared. /// true, if all bytes compare as equal, false otherwise. public static unsafe bool CompareMemory(void* buf1, void* buf2, uint length) { return (length == RtlCompareMemory(buf1, buf2, length)); } /// /// Compares blocks of memory. /// /// A pointer to a block of memory to compare. /// A pointer to a block of memory to compare. /// Specifies the number of bytes to be compared. /// true, if all bytes compare as equal, false otherwise. public static unsafe bool CompareMemory(void* buf1, void* buf2, long length) { return (length == RtlCompareMemory(buf1, buf2, checked((uint)length))); } /// /// Compares blocks of memory. /// /// A pointer to a block of memory to compare. /// A pointer to a block of memory to compare. /// Specifies the number of bytes to be compared. /// true, if all bytes compare as equal, false otherwise. public static unsafe bool CompareMemory(IntPtr buf1, IntPtr buf2, uint length) { return (length == RtlCompareMemory(buf1.ToPointer(), buf2.ToPointer(), length)); } /// /// Compares blocks of memory. /// /// A pointer to a block of memory to compare. /// A pointer to a block of memory to compare. /// Specifies the number of bytes to be compared. /// true, if all bytes compare as equal, false otherwise. public static unsafe bool CompareMemory(IntPtr buf1, IntPtr buf2, long length) { return (length == RtlCompareMemory(buf1.ToPointer(), buf2.ToPointer(), checked((uint)length))); } /// /// Moves a block of memory from one location to another. /// /// A pointer to the starting address of the move destination. /// A pointer to the starting address of the block of memory to be moved. /// The size of the block of memory to move, in bytes. public static unsafe void MoveMemory(void* dst, void* src, long size) { MoveMemory(dst, src, checked((uint)size)); } /// /// Moves a block of memory from one location to another. /// /// A pointer to the starting address of the move destination. /// A pointer to the starting address of the block of memory to be moved. /// The size of the block of memory to move, in bytes. public static unsafe void MoveMemory(IntPtr dst, IntPtr src, uint size) { MoveMemory(dst.ToPointer(), src.ToPointer(), size); } /// /// Moves a block of memory from one location to another. /// /// A pointer to the starting address of the move destination. /// A pointer to the starting address of the block of memory to be moved. /// The size of the block of memory to move, in bytes. public static unsafe void MoveMemory(IntPtr dst, IntPtr src, long size) { MoveMemory(dst.ToPointer(), src.ToPointer(), checked((uint)size)); } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// 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; } } } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// public static unsafe void CopyMemory(byte* dest, byte* src, long len) { CopyMemory(dest, src, checked((int)len)); } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// public static unsafe void CopyMemory(void* dest, void* src, long len) { CopyMemory((byte*)dest, (byte*)src, checked((int)len)); } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// public static unsafe void CopyMemory(void* dest, void* src, int len) { CopyMemory((byte*)dest, (byte*)src, len); } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// public static unsafe void CopyMemory(IntPtr dest, IntPtr src, int len) { CopyMemory((byte*)dest, (byte*)src, len); } /// /// Copies a block of memory from one location to another. /// /// A pointer to the starting address of the copied block's destination. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. /// /// CopyMemory runs faster than . /// However, if both blocks overlap the result is undefined. /// public static unsafe void CopyMemory(IntPtr dest, IntPtr src, long len) { CopyMemory((byte*)dest, (byte*)src, checked((int)len)); } /// /// Copies a block of memory into an array. /// /// An array used as the destination of the copy process. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. 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(); } } /// /// Copies a block of memory into an array. /// /// An array used as the destination of the copy process. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(Array dest, void* src, long len) { CopyMemory(dest, (byte*)src, checked((int)len)); } /// /// Copies a block of memory into an array. /// /// An array used as the destination of the copy process. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(Array dest, IntPtr src, int len) { CopyMemory(dest, (byte*)src, len); } /// /// Copies a block of memory into an array. /// /// An array used as the destination of the copy process. /// A pointer to the starting address of the block of memory to copy. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(Array dest, IntPtr src, long len) { CopyMemory(dest, (byte*)src, checked((int)len)); } /// /// Copies the content of an array to a memory location. /// /// A pointer to the starting address of the copied block's destination. /// An array used as the source of the copy process. /// The size of the block of memory to copy, in bytes. 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(); } } /// /// Copies the content of an array to a memory location. /// /// A pointer to the starting address of the copied block's destination. /// An array used as the source of the copy process. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(void* dest, Array src, long len) { CopyMemory((byte*)dest, src, checked((int)len)); } /// /// Copies the content of an array to a memory location. /// /// A pointer to the starting address of the copied block's destination. /// An array used as the source of the copy process. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(IntPtr dest, Array src, int len) { CopyMemory((byte*)dest, src, len); } /// /// Copies the content of an array to a memory location. /// /// A pointer to the starting address of the copied block's destination. /// An array used as the source of the copy process. /// The size of the block of memory to copy, in bytes. public static unsafe void CopyMemory(IntPtr dest, Array src, long len) { CopyMemory((byte*)dest, src, checked((int)len)); } /// /// Copies the content of one array into another array. /// /// An array used as the destination of the copy process. /// An array used as the source of the copy process. /// The size of the content to copy, in bytes. 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(); } } /// /// Copies the content of one array into another array. /// /// An array used as the destination of the copy process. /// An array used as the source of the copy process. /// The size of the content to copy, in bytes. 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(ref T[] array, int length) { if ((array != null) && (length >= 0) && (array.Length != length)) { Array.Resize(ref array, length); } } internal static void Resize(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(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 /// /// 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. /// /// Handle to the window whose DC is to be retrieved. /// If this value is IntPtr.Zero, GetDC retrieves the DC for the entire screen. /// 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. [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hWnd); /// /// 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. /// /// Handle to the window whose DC is to be released. /// Handle to the DC to be released. /// Returns true on success, false on failure. [DllImport("user32.dll")] private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); /// /// 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. /// /// Handle to a device context. /// Pointer to a BITMAPINFO structure that specifies various attributes of the DIB, /// including the bitmap dimensions and colors. /// 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). /// Pointer to a variable that receives a pointer to the location of the DIB bit values. /// Handle to a file-mapping object that the function will use to create the DIB. /// This parameter can be NULL. /// 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. /// 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. [DllImport("gdi32.dll")] private static extern IntPtr CreateDIBSection( IntPtr hdc, [In] IntPtr pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset); /// /// 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. /// /// Handle to a logical pen, brush, font, bitmap, region, or palette. /// Returns true on success, false on failure. [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); /// /// Creates a compatible bitmap (DDB) from a DIB and, optionally, sets the bitmap bits. /// /// Handle to a device context. /// Pointer to a bitmap information header structure. /// Specifies how the system initializes the bitmap bits - (use 4). /// Pointer to an array of bytes containing the initial bitmap data. /// Pointer to a BITMAPINFO structure that describes the dimensions /// and color format of the array pointed to by the lpbInit parameter. /// Specifies whether the bmiColors member of the BITMAPINFO structure /// was initialized - (use 0). /// Handle to a DIB or null on failure. [DllImport("gdi32.dll")] private static extern IntPtr CreateDIBitmap( IntPtr hdc, IntPtr lpbmih, uint fdwInit, IntPtr lpbInit, IntPtr lpbmi, uint fuUsage); /// /// Retrieves information for the specified graphics object. /// /// Handle to the graphics object of interest. /// Specifies the number of bytes of information to /// be written to the buffer. /// Pointer to a buffer that receives the information /// about the specified graphics object. /// 0 on failure. [DllImport("gdi32.dll")] private static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject); /// /// Retrieves the bits of the specified compatible bitmap and copies them into a buffer /// as a DIB using the specified format. /// /// Handle to the device context. /// Handle to the bitmap. This must be a compatible bitmap (DDB). /// Specifies the first scan line to retrieve. /// Specifies the number of scan lines to retrieve. /// Pointer to a buffer to receive the bitmap data. /// Pointer to a BITMAPINFO structure that specifies the desired /// format for the DIB data. /// Specifies the format of the bmiColors member of the /// BITMAPINFO structure - (use 0). /// 0 on failure. [DllImport("gdi32.dll")] private static extern unsafe int GetDIBits( IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines, IntPtr lpvBits, IntPtr lpbmi, uint uUsage); /// /// Moves a block of memory from one location to another. /// /// Pointer to the starting address of the move destination. /// Pointer to the starting address of the block of memory to be moved. /// Size of the block of memory to move, in bytes. [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] public static unsafe extern void MoveMemory(void* dst, void* src, uint size); /// /// The RtlCompareMemory routine compares blocks of memory /// and returns the number of bytes that are equivalent. /// /// A pointer to a block of memory to compare. /// A pointer to a block of memory to compare. /// Specifies the number of bytes to be compared. /// RtlCompareMemory returns the number of bytes that compare as equal. /// If all bytes compare as equal, the input Length is returned. [DllImport("ntdll.dll", EntryPoint = "RtlCompareMemory", SetLastError = false)] internal static unsafe extern uint RtlCompareMemory(void* buf1, void* buf2, uint count); #endregion } }