/// <summary>
 /// Converts a FreeImage bitmap from one color depth to another.
 /// If the conversion fails the original FreeImage bitmap is returned.
 /// </summary>
 /// <param name="dib">Handle to a FreeImage bitmap.</param>
 /// <param name="conversion">The desired output format.</param>
 /// <param name="ditherMethod">Dither algorithm when converting with
 /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER"/>.</param>
 /// <param name="unloadSource">When true the structure will be unloaded on success.</param>
 /// <returns>Handle to a FreeImage bitmap.</returns>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="dib"/> is null.</exception>
 public static FIBITMAP ConvertColorDepth(
     FIBITMAP dib,
     FREE_IMAGE_COLOR_DEPTH conversion,
     FREE_IMAGE_DITHER ditherMethod,
     bool unloadSource)
 {
     return ConvertColorDepth(
         dib,
         conversion,
         128,
         ditherMethod,
         FREE_IMAGE_QUANTIZE.FIQ_WUQUANT,
         unloadSource);
 }
 /// <summary>
 /// Converts a FreeImage bitmap from one color depth to another.
 /// If the conversion fails the original FreeImage bitmap is returned.
 /// </summary>
 /// <param name="dib">Handle to a FreeImage bitmap.</param>
 /// <param name="conversion">The desired output format.</param>
 /// <param name="quantizationMethod">The quantization algorithm for conversion to 8-bit color depth.</param>
 /// <param name="unloadSource">When true the structure will be unloaded on success.</param>
 /// <returns>Handle to a FreeImage bitmap.</returns>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="dib"/> is null.</exception>
 public static FIBITMAP ConvertColorDepth(
     FIBITMAP dib,
     FREE_IMAGE_COLOR_DEPTH conversion,
     FREE_IMAGE_QUANTIZE quantizationMethod,
     bool unloadSource)
 {
     return ConvertColorDepth(
         dib,
         conversion,
         128,
         FREE_IMAGE_DITHER.FID_FS,
         quantizationMethod,
         unloadSource);
 }
        /// <summary>
        /// Changes a bitmaps color depth.
        /// Used by SaveEx and SaveToStream
        /// </summary>
        private static FIBITMAP PrepareBitmapColorDepth(FIBITMAP dibToSave, FREE_IMAGE_FORMAT format, FREE_IMAGE_COLOR_DEPTH colorDepth)
        {
            int bpp = (int)GetBPP(dibToSave);
            int targetBpp = (int)(colorDepth & FREE_IMAGE_COLOR_DEPTH.FICD_COLOR_MASK);

            if (colorDepth != FREE_IMAGE_COLOR_DEPTH.FICD_AUTO)
            {
                // A fix colordepth was chosen
                if (FIFSupportsExportBPP(format, targetBpp))
                {
                    dibToSave = ConvertColorDepth(dibToSave, colorDepth, false);
                }
                else
                {
                    throw new ArgumentException("FreeImage\n\nFreeImage Library plugin " +
                        GetFormatFromFIF(format) + " is unable to write images with a color depth of " +
                        targetBpp + " bpp.");
                }
            }
            else
            {
                // Auto selection was chosen
                if (!FIFSupportsExportBPP(format, bpp))
                {
                    // The color depth is not supported
                    int bppUpper = bpp;
                    int bppLower = bpp;
                    // Check from the bitmaps current color depth in both directions
                    do
                    {
                        bppUpper = GetNextColorDepth(bppUpper);
                        if (FIFSupportsExportBPP(format, bppUpper))
                        {
                            dibToSave = ConvertColorDepth(dibToSave, (FREE_IMAGE_COLOR_DEPTH)bppUpper, false);
                            break;
                        }
                        bppLower = GetPrevousColorDepth(bppLower);
                        if (FIFSupportsExportBPP(format, bppLower))
                        {
                            dibToSave = ConvertColorDepth(dibToSave, (FREE_IMAGE_COLOR_DEPTH)bppLower, false);
                            break;
                        }
                    } while (!((bppLower == 0) && (bppUpper == 0)));
                }
            }
            return dibToSave;
        }
 /// <summary>
 /// Converts a FreeImage bitmap from one color depth to another.
 /// If the conversion fails the original FreeImage bitmap is returned.
 /// </summary>
 /// <param name="dib">Handle to a FreeImage bitmap.</param>
 /// <param name="conversion">The desired output format.</param>
 /// <param name="threshold">Threshold value when converting with
 /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD"/>.</param>
 /// <param name="unloadSource">When true the structure will be unloaded on success.</param>
 /// <returns>Handle to a FreeImage bitmap.</returns>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="dib"/> is null.</exception>
 public static FIBITMAP ConvertColorDepth(
     FIBITMAP dib,
     FREE_IMAGE_COLOR_DEPTH conversion,
     byte threshold,
     bool unloadSource)
 {
     return ConvertColorDepth(
         dib,
         conversion,
         threshold,
         FREE_IMAGE_DITHER.FID_FS,
         FREE_IMAGE_QUANTIZE.FIQ_WUQUANT,
         unloadSource);
 }
 /// <summary>
 /// Converts a FreeImage bitmap from one color depth to another.
 /// If the conversion fails the original FreeImage bitmap is returned.
 /// </summary>
 /// <param name="dib">Handle to a FreeImage bitmap.</param>
 /// <param name="conversion">The desired output format.</param>
 /// <returns>Handle to a FreeImage bitmap.</returns>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="dib"/> is null.</exception>
 public static FIBITMAP ConvertColorDepth(
     FIBITMAP dib,
     FREE_IMAGE_COLOR_DEPTH conversion)
 {
     return ConvertColorDepth(
         dib,
         conversion,
         128,
         FREE_IMAGE_DITHER.FID_FS,
         FREE_IMAGE_QUANTIZE.FIQ_WUQUANT,
         false);
 }
        /// <summary>
        /// Converts a FreeImage bitmap from one color depth to another.
        /// If the conversion fails the original FreeImage bitmap is returned.
        /// </summary>
        /// <param name="dib">Handle to a FreeImage bitmap.</param>
        /// <param name="conversion">The desired output format.</param>
        /// <param name="threshold">Threshold value when converting with
        /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD"/>.</param>
        /// <param name="ditherMethod">Dither algorithm when converting with
        /// <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_DITHER"/>.</param>
        /// <param name="quantizationMethod">The quantization algorithm for conversion to 8-bit color depth.</param>
        /// <param name="unloadSource">When true the structure will be unloaded on success.</param>
        /// <returns>Handle to a FreeImage bitmap.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="dib"/> is null.</exception>
        internal static FIBITMAP ConvertColorDepth(
            FIBITMAP dib,
            FREE_IMAGE_COLOR_DEPTH conversion,
            byte threshold,
            FREE_IMAGE_DITHER ditherMethod,
            FREE_IMAGE_QUANTIZE quantizationMethod,
            bool unloadSource)
        {
            if (dib.IsNull)
            {
                throw new ArgumentNullException("dib");
            }

            FIBITMAP result = 0;
            FIBITMAP dibTemp = 0;
            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);

            switch (conversion & (FREE_IMAGE_COLOR_DEPTH)0xFF)
            {
                case FREE_IMAGE_COLOR_DEPTH.FICD_01_BPP_THRESHOLD:

                    if (bpp != 1)
                    {
                        result = Threshold(dib, threshold);
                    }
                    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)
                    {
                        result = Dither(dib, ditherMethod);
                    }
                    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);
                        }
                        // All other cases are converted directly
                        else
                        {
                            result = ConvertTo4Bits(dib);
                        }
                    }
                    else
                    {
                        bool isGreyscale = IsGreyscaleImage(dib);
                        if ((forceGreyscale && (!isGreyscale)) ||
                            (reorderPalette && isGreyscale))
                        {
                            dibTemp = ConvertToGreyscale(dib);
                            result = ConvertTo4Bits(dibTemp);
                            Unload(dibTemp);
                        }
                    }

                    break;

                case FREE_IMAGE_COLOR_DEPTH.FICD_08_BPP:

                    if (bpp != 8)
                    {
                        if (forceGreyscale)
                        {
                            result = ConvertToGreyscale(dib);
                        }
                        else
                        {
                            dibTemp = ConvertTo24Bits(dib);
                            result = ColorQuantize(dibTemp, quantizationMethod);
                            Unload(dibTemp);
                        }
                    }
                    else
                    {
                        bool isGreyscale = IsGreyscaleImage(dib);
                        if ((forceGreyscale && (!isGreyscale)) || (reorderPalette && isGreyscale))
                        {
                            result = ConvertToGreyscale(dib);
                        }
                    }
                    break;

                case FREE_IMAGE_COLOR_DEPTH.FICD_16_BPP_555:

                    if (forceGreyscale)
                    {
                        dibTemp = ConvertToGreyscale(dib);
                        result = ConvertTo16Bits555(dibTemp);
                        Unload(dibTemp);
                    }
                    else if (bpp != 16 || GetRedMask(dib) != FI16_555_RED_MASK || GetGreenMask(dib) != FI16_555_GREEN_MASK || GetBlueMask(dib) != FI16_555_BLUE_MASK)
                    {
                        result = ConvertTo16Bits555(dib);
                    }
                    break;

                case FREE_IMAGE_COLOR_DEPTH.FICD_16_BPP:

                    if (forceGreyscale)
                    {
                        dibTemp = ConvertToGreyscale(dib);
                        result = ConvertTo16Bits565(dibTemp);
                        Unload(dibTemp);
                    }
                    else if (bpp != 16 || GetRedMask(dib) != FI16_565_RED_MASK || GetGreenMask(dib) != FI16_565_GREEN_MASK || GetBlueMask(dib) != FI16_565_BLUE_MASK)
                    {
                        result = ConvertTo16Bits565(dib);
                    }
                    break;

                case FREE_IMAGE_COLOR_DEPTH.FICD_24_BPP:

                    if (forceGreyscale)
                    {
                        dibTemp = ConvertToGreyscale(dib);
                        result = ConvertTo24Bits(dibTemp);
                        Unload(dibTemp);
                    }
                    else if (bpp != 24)
                    {
                        result = ConvertTo24Bits(dib);
                    }
                    break;

                case FREE_IMAGE_COLOR_DEPTH.FICD_32_BPP:

                    if (forceGreyscale)
                    {
                        dibTemp = ConvertToGreyscale(dib);
                        result = ConvertTo32Bits(dibTemp);
                        Unload(dibTemp);
                    }
                    else if (bpp != 32)
                    {
                        result = ConvertTo32Bits(dib);
                    }
                    break;
            }

            if (result.IsNull)
            {
                return dib;
            }
            if (unloadSource)
            {
                Unload(dib);
            }

            return result;
        }
        /// <summary>
        /// Saves a previously loaded FreeImage bitmap to a stream.
        /// The stream must be set to the correct position before calling SaveToStream.
        /// </summary>
        /// <param name="dib">Handle to a FreeImage bitmap.</param>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="format">Format of the image.</param>
        /// <param name="flags">Flags to enable or disable plugin-features.</param>
        /// <param name="colorDepth">The new color depth of the bitmap.
        /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if SaveToStream should
        /// take the best suitable color depth.
        /// If a color depth is selected that the provided format cannot write an
        /// error-message will be thrown.</param>
        /// <param name="unloadSource">When true the structure will be unloaded on success.</param>
        /// <returns>Returns true on success, false on failure.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="stream"/> cannot write.</exception>
        public static bool SaveToStream(
            ref FIBITMAP dib,
            Stream stream,
            FREE_IMAGE_FORMAT format,
            FREE_IMAGE_SAVE_FLAGS flags,
            FREE_IMAGE_COLOR_DEPTH colorDepth,
            bool unloadSource)
        {
            if (dib.IsNull)
            {
                throw new ArgumentNullException("dib");
            }
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (!stream.CanWrite)
            {
                throw new ArgumentException("stream is not capable of writing.");
            }
            if ((!FIFSupportsWriting(format)) || (!FIFSupportsExportType(format, FREE_IMAGE_TYPE.FIT_BITMAP)))
            {
                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);
                }
            }
            catch
            {
                // Always unload a temporary created bitmap.
                if (dibToSave != dib)
                {
                    UnloadEx(ref dibToSave);
                }
                // On success unload the bitmap
                if (result && unloadSource)
                {
                    UnloadEx(ref dib);
                }
            }

            return result;
        }
 /// <summary>
 /// Saves a previously loaded FreeImage bitmap to a stream.
 /// The stream must be set to the correct position before calling SaveToStream.
 /// </summary>
 /// <param name="dib">Handle to a FreeImage bitmap.</param>
 /// <param name="stream">The stream to write to.</param>
 /// <param name="format">Format of the image.</param>
 /// <param name="flags">Flags to enable or disable plugin-features.</param>
 /// <param name="colorDepth">The new color depth of the bitmap.
 /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if SaveToStream should
 /// take the best suitable color depth.
 /// If a color depth is selected that the provided format cannot write an
 /// error-message will be thrown.</param>
 /// <returns>Returns true on success, false on failure.</returns>
 /// <exception cref="ArgumentNullException">
 /// <paramref name="dib"/> or <paramref name="stream"/> is null.</exception>
 /// <exception cref="ArgumentException">
 /// <paramref name="stream"/> cannot write.</exception>
 public static bool SaveToStream(
     FIBITMAP dib,
     Stream stream,
     FREE_IMAGE_FORMAT format,
     FREE_IMAGE_SAVE_FLAGS flags,
     FREE_IMAGE_COLOR_DEPTH colorDepth)
 {
     return SaveToStream(
         ref dib,
         stream,
         format,
         flags,
         colorDepth,
         false);
 }
        /// <summary>
        /// Saves a previously loaded FreeImage bitmap to a file.
        /// In case the loading format is <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>
        /// the format is taken off the filename.
        /// If no suitable format was found false will be returned.
        /// Save flags can be provided by the flags parameter.
        /// The bitmaps color depth can be set by 'colorDepth'.
        /// If set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> a suitable color depth
        /// will be taken if available.
        /// </summary>
        /// <param name="dib">Handle to a FreeImage bitmap.</param>
        /// <param name="filename">The complete name of the file to save to.
        /// The extension will be corrected if it is no valid extension for the
        /// selected format or if no extension was specified.</param>
        /// <param name="format">Format of the image. If the format should be taken from the
        /// filename use <see cref="FREE_IMAGE_FORMAT.FIF_UNKNOWN"/>.</param>
        /// <param name="flags">Flags to enable or disable plugin-features.</param>
        /// <param name="colorDepth">The new color depth of the bitmap.
        /// Set to <see cref="FREE_IMAGE_COLOR_DEPTH.FICD_AUTO"/> if Save should take the
        /// best suitable color depth.
        /// If a color depth is selected that the provided format cannot write an
        /// error-message will be thrown.</param>
        /// <param name="unloadSource">When true the structure will be unloaded on success.
        /// If the function failed and returned false, the bitmap was not unloaded.</param>
        /// <returns>Returns true on success, false on failure.</returns>
        /// <exception cref="ArgumentException">
        /// A direct color conversion failed.</exception>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="dib"/> or <paramref name="filename"/> is null.</exception>
        public static bool SaveEx(
            ref FIBITMAP dib,
            string filename,
            FREE_IMAGE_FORMAT format,
            FREE_IMAGE_SAVE_FLAGS flags,
            FREE_IMAGE_COLOR_DEPTH colorDepth,
            bool unloadSource)
        {
            if (dib.IsNull)
            {
                throw new ArgumentNullException("dib");
            }
            if (filename == null)
            {
                throw new ArgumentNullException("filename");
            }
            bool result = false;
            // Gets format from filename if the format is unknown
            if (format == FREE_IMAGE_FORMAT.FIF_UNKNOWN)
            {
                format = GetFIFFromFilename(filename);
            }
            if (format != FREE_IMAGE_FORMAT.FIF_UNKNOWN)
            {
                // Checks writing support
                if (FIFSupportsWriting(format) && FIFSupportsExportType(format, GetImageType(dib)))
                {
                    // Check valid filename and correct it if needed
                    if (!IsFilenameValidForFIF(format, filename))
                    {
                        int index = filename.LastIndexOf('.');
                        string extension = GetPrimaryExtensionFromFIF(format);

                        if (index == -1)
                        {
                            // We have no '.' (dot) so just add the extension
                            filename += "." + extension;
                        }
                        else
                        {
                            // Overwrite the old extension
                            filename = filename.Substring(0, filename.LastIndexOf('.')) + extension;
                        }
                    }

                    FIBITMAP dibToSave = PrepareBitmapColorDepth(dib, format, colorDepth);
                    result = Save(format, dibToSave, filename, flags);

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