File: System\Windows\Media\PixelFormat.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;
using System.ComponentModel;
using MS.Internal;
 
using UnsafeNativeMethods = MS.Win32.PresentationCore.UnsafeNativeMethods;
 
namespace System.Windows.Media
{
    [Flags]
    internal enum PixelFormatFlags
    {
        BitsPerPixelMask        = 0x00FF,
        BitsPerPixelUndefined   = 0,
        BitsPerPixel1           = 1,
        BitsPerPixel2           = 2,
        BitsPerPixel4           = 4,
        BitsPerPixel8           = 8,
        BitsPerPixel16          = 16,
        BitsPerPixel24          = 24,
        BitsPerPixel32          = 32,
        BitsPerPixel48          = 48,
        BitsPerPixel64          = 64,
        BitsPerPixel96          = 96,
        BitsPerPixel128         = 128,
        IsGray                  = 0x00000100,   // Grayscale only
        IsCMYK                  = 0x00000200,   // CMYK, not ARGB
        IsSRGB                  = 0x00000400,   // Gamma is approximately 2.2
        IsScRGB                 = 0x00000800,   // Gamma is 1.0
        Premultiplied           = 0x00001000,   // Premultiplied Alpha
        ChannelOrderMask        = 0x0001E000,
        ChannelOrderRGB         = 0x00002000,
        ChannelOrderBGR         = 0x00004000,
        ChannelOrderARGB        = 0x00008000,
        ChannelOrderABGR        = 0x00010000,
        Palettized              = 0x00020000,   // Pixels are indexes into a palette
        NChannelAlpha           = 0x00040000,   // N-Channel format with alpha
        IsNChannel              = 0x00080000,   // N-Channel format
    }
 
    #region PixelFormat
 
    /// <summary>
    /// Describes the bit mask and shift for a specific pixelformat
    /// </summary>
    public struct PixelFormatChannelMask
    {
        internal PixelFormatChannelMask(byte[] mask)
        {
            Debug.Assert(mask != null);
            _mask = mask;
        }
 
        /// <summary>
        /// The bitmask for a color channel
        /// It will never be greater then 0xffffffff
        /// </summary>
        public IList<byte> Mask
        {
            get
            {
                return _mask != null ? new PartialList<byte>((byte[])_mask.Clone()) : null;
            }
        }
 
        /// <summary>
        /// op_equality - returns whether or not the two pixel format channel masks are equal
        /// </summary>
        public static bool operator == (PixelFormatChannelMask left, PixelFormatChannelMask right)
        {
            return Equals(left, right);
        }
 
        /// <summary>
        /// Equals - Returns whether or not the two pixel format channel masks are equal
        /// </summary>
        public static bool Equals(PixelFormatChannelMask left, PixelFormatChannelMask right)
        {
            int leftNumChannels  =  left._mask != null ?  left._mask.Length : 0;
            int rightNumChannels = right._mask != null ? right._mask.Length : 0;
 
            if (leftNumChannels != rightNumChannels)
            {
                return false;
            }
 
            for (int i = 0; i < leftNumChannels; ++i)
            {
                if (left._mask[i] != right._mask[i])
                {
                    return false;
                }
            }
 
            return true;
        }
 
        /// <summary>
        /// op_inequality - returns whether or not the two pixel format channel masks are not equal
        /// </summary>
        public static bool operator != (PixelFormatChannelMask left, PixelFormatChannelMask right)
        {
            return !(left == right);
        }
 
        /// <summary>
        /// Equals - Returns whether or not this is equal to the Object
        /// </summary>
        public override bool Equals(Object obj)
        {
            // Can't use "as" since we're looking for a value type
            return obj is PixelFormatChannelMask ? this == (PixelFormatChannelMask)obj : false;
        }
 
        /// <summary>
        /// GetHashCode - Returns a hash code
        /// </summary>
        public override int GetHashCode()
        {
            int hash = 0;
 
            if (_mask != null)
            {
                for (int i = 0, count = _mask.Length; i < count; ++i)
                {
                    hash += _mask[i] * 256 * i;
                }
            }
 
            return hash;
        }
 
        private byte[] _mask;
    }
 
    /// <summary>
    /// Pixel Format Definition for images and pixel-based surfaces
    /// </summary>
    [TypeConverter(typeof(PixelFormatConverter))]
    [Serializable]
    public struct PixelFormat : IEquatable<PixelFormat>
    {
        internal PixelFormat(Guid guidPixelFormat)
        {
            unsafe
            {
                Debug.Assert(Unsafe.SizeOf<Guid>() == 16);
 
                // Compare only the first 15 bytes of the GUID. If the first
                // 15 bytes match the WIC pixel formats, then the 16th byte
                // will be the format enum value.
                Guid guidWicPixelFormat = WICPixelFormatGUIDs.WICPixelFormatDontCare;
                ReadOnlySpan<byte> pGuidPixelFormat = new(&guidPixelFormat, 15);
                ReadOnlySpan<byte> pGuidBuiltIn = new(&guidWicPixelFormat, 15);
 
                // If it looks like a built-in WIC pixel format, verify that the format enum value is known to us.
                if (pGuidPixelFormat.SequenceEqual(pGuidBuiltIn) && ((byte*)&guidPixelFormat)[15] <= (byte)PixelFormatEnum.Cmyk32)
                {
                    _format = (PixelFormatEnum)((byte*)&guidPixelFormat)[15];
                }
                else
                {
                    _format = PixelFormatEnum.Extended;
                }
            }
 
            _flags = GetPixelFormatFlagsFromEnum(_format) | GetPixelFormatFlagsFromGuid(guidPixelFormat);
            _bitsPerPixel = GetBitsPerPixelFromEnum(_format);
            _guidFormat = guidPixelFormat;
        }
 
        internal PixelFormat(PixelFormatEnum format)
        {
            _format = format;
 
            _flags = GetPixelFormatFlagsFromEnum(format);
            _bitsPerPixel = GetBitsPerPixelFromEnum(format);
            _guidFormat = GetGuidFromFormat(format);
        }
 
        /// <summary>
        /// Construct a pixel format from a string that represents the format.
        /// The purpose of this method is only for deserialization of PixelFormat.
        /// The preferred way to construct a PixelFormat is with the PixelFormats class.
        /// </summary>
        /// <param name="pixelFormatString"></param>
        internal PixelFormat(string pixelFormatString)
        {
            ArgumentNullException.ThrowIfNull(pixelFormatString);
 
            _format = pixelFormatString switch
            {
                _ when pixelFormatString.Equals("Default", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Default,
                _ when pixelFormatString.Equals("Extended", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Extended,
                _ when pixelFormatString.Equals("Indexed1", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Indexed1,
                _ when pixelFormatString.Equals("Indexed2", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Indexed2,
                _ when pixelFormatString.Equals("Indexed4", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Indexed4,
                _ when pixelFormatString.Equals("Indexed8", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Indexed8,
                _ when pixelFormatString.Equals("BlackWhite", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.BlackWhite,
                _ when pixelFormatString.Equals("Gray2", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Gray2,
                _ when pixelFormatString.Equals("Gray4", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Gray4,
                _ when pixelFormatString.Equals("Gray8", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Gray8,
                _ when pixelFormatString.Equals("Bgr555", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgr555,
                _ when pixelFormatString.Equals("Bgr565", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgr565,
                _ when pixelFormatString.Equals("Bgr24", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgr24,
                _ when pixelFormatString.Equals("Rgb24", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Rgb24,
                _ when pixelFormatString.Equals("Bgr101010", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgr101010,
                _ when pixelFormatString.Equals("Bgr32", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgr32,
                _ when pixelFormatString.Equals("Bgra32", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Bgra32,
                _ when pixelFormatString.Equals("Pbgra32", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Pbgra32,
                _ when pixelFormatString.Equals("Rgb48", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Rgb48,
                _ when pixelFormatString.Equals("Rgba64", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Rgba64,
                _ when pixelFormatString.Equals("Prgba64", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Prgba64,
                _ when pixelFormatString.Equals("Gray16", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Gray16,
                _ when pixelFormatString.Equals("Gray32Float", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Gray32Float,
                _ when pixelFormatString.Equals("Rgb128Float", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Rgb128Float,
                _ when pixelFormatString.Equals("Rgba128Float", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Rgba128Float,
                _ when pixelFormatString.Equals("Prgba128Float", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Prgba128Float,
                _ when pixelFormatString.Equals("Cmyk32", StringComparison.OrdinalIgnoreCase) => PixelFormatEnum.Cmyk32,
                _ => throw new ArgumentException(SR.Format(SR.Image_BadPixelFormat, pixelFormatString), nameof(pixelFormatString)),
            };
 
            _flags = GetPixelFormatFlagsFromEnum(_format);
            _bitsPerPixel = GetBitsPerPixelFromEnum(_format);
            _guidFormat = GetGuidFromFormat(_format);
        }
 
        private static Guid GetGuidFromFormat(PixelFormatEnum format) => format switch
        {
            PixelFormatEnum.Default => WICPixelFormatGUIDs.WICPixelFormatDontCare,
            PixelFormatEnum.Indexed1 => WICPixelFormatGUIDs.WICPixelFormat1bppIndexed,
            PixelFormatEnum.Indexed2 => WICPixelFormatGUIDs.WICPixelFormat2bppIndexed,
            PixelFormatEnum.Indexed4 => WICPixelFormatGUIDs.WICPixelFormat4bppIndexed,
            PixelFormatEnum.Indexed8 => WICPixelFormatGUIDs.WICPixelFormat8bppIndexed,
            PixelFormatEnum.BlackWhite => WICPixelFormatGUIDs.WICPixelFormatBlackWhite,
            PixelFormatEnum.Gray2 => WICPixelFormatGUIDs.WICPixelFormat2bppGray,
            PixelFormatEnum.Gray4 => WICPixelFormatGUIDs.WICPixelFormat4bppGray,
            PixelFormatEnum.Gray8 => WICPixelFormatGUIDs.WICPixelFormat8bppGray,
            PixelFormatEnum.Bgr555 => WICPixelFormatGUIDs.WICPixelFormat16bppBGR555,
            PixelFormatEnum.Bgr565 => WICPixelFormatGUIDs.WICPixelFormat16bppBGR565,
            PixelFormatEnum.Bgr24 => WICPixelFormatGUIDs.WICPixelFormat24bppBGR,
            PixelFormatEnum.Rgb24 => WICPixelFormatGUIDs.WICPixelFormat24bppRGB,
            PixelFormatEnum.Bgr101010 => WICPixelFormatGUIDs.WICPixelFormat32bppBGR101010,
            PixelFormatEnum.Bgr32 => WICPixelFormatGUIDs.WICPixelFormat32bppBGR,
            PixelFormatEnum.Bgra32 => WICPixelFormatGUIDs.WICPixelFormat32bppBGRA,
            PixelFormatEnum.Pbgra32 => WICPixelFormatGUIDs.WICPixelFormat32bppPBGRA,
            PixelFormatEnum.Rgb48 => WICPixelFormatGUIDs.WICPixelFormat48bppRGB,
            PixelFormatEnum.Rgba64 => WICPixelFormatGUIDs.WICPixelFormat64bppRGBA,
            PixelFormatEnum.Prgba64 => WICPixelFormatGUIDs.WICPixelFormat64bppPRGBA,
            PixelFormatEnum.Gray16 => WICPixelFormatGUIDs.WICPixelFormat16bppGray,
            PixelFormatEnum.Gray32Float => WICPixelFormatGUIDs.WICPixelFormat32bppGrayFloat,
            PixelFormatEnum.Rgb128Float => WICPixelFormatGUIDs.WICPixelFormat128bppRGBFloat,
            PixelFormatEnum.Rgba128Float => WICPixelFormatGUIDs.WICPixelFormat128bppRGBAFloat,
            PixelFormatEnum.Prgba128Float => WICPixelFormatGUIDs.WICPixelFormat128bppPRGBAFloat,
            PixelFormatEnum.Cmyk32 => WICPixelFormatGUIDs.WICPixelFormat32bppCMYK,
            _ => throw new ArgumentException(SR.Format(SR.Image_BadPixelFormat, format), nameof(format))
        };
 
        private readonly PixelFormatFlags FormatFlags
        {
            get
            {
                return _flags;
            }
        }
 
        /// <summary>
        /// op_equality - returns whether or not the two pixel formats are equal
        /// </summary>
        public static bool operator ==(PixelFormat left, PixelFormat right)
        {
            return left.Guid == right.Guid;
        }
 
        /// <summary>
        /// op_inequality - returns whether or not the two pixel formats are not equal
        /// </summary>
        public static bool operator !=(PixelFormat left, PixelFormat right)
        {
            return left.Guid != right.Guid;
        }
 
        /// <summary>
        /// Equals - Returns whether or not the two pixel formats are equal
        /// </summary>
        public static bool Equals(PixelFormat left, PixelFormat right)
        {
            return left.Guid == right.Guid;
        }
 
        /// <summary>
        /// Equals - Returns whether or not this is equal to the PixelFormat
        /// </summary>
        public readonly bool Equals(PixelFormat pixelFormat)
        {
            return this == pixelFormat;
        }
 
        /// <summary>
        /// Equals - Returns whether or not this is equal to the Object
        /// </summary>
        public override readonly bool Equals(object obj)
        {
            return obj is PixelFormat pixelFormat && Equals(pixelFormat);
        }
 
        /// <summary>
        /// GetHashCode - Returns a hash code
        /// </summary>
        public override readonly int GetHashCode()
        {
            return Guid.GetHashCode();
        }
 
        /// <summary>
        /// The number of bits per pixel for this format.
        /// </summary>
        public int BitsPerPixel
        {
            get
            {
                return InternalBitsPerPixel;
            }
        }
 
        /// <summary>
        /// The pixel format mask information for each channel.
        /// </summary>
        public readonly IList<PixelFormatChannelMask> Masks
        {
            get
            {
                IntPtr pixelFormatInfo = CreatePixelFormatInfo();
                Debug.Assert(pixelFormatInfo != IntPtr.Zero);
 
                PixelFormatChannelMask[] masks;
 
                try
                {
                    HRESULT.Check(UnsafeNativeMethods.WICPixelFormatInfo.GetChannelCount(
                        pixelFormatInfo,
                        out UInt32 channelCount
                        ));
 
                    Debug.Assert(channelCount >= 1);
 
                    masks = new PixelFormatChannelMask[channelCount];
 
                    unsafe
                    {
                        for (uint i = 0; i < channelCount; i++)
                        {
                            HRESULT.Check(UnsafeNativeMethods.WICPixelFormatInfo.GetChannelMask(
                                pixelFormatInfo, i, 0, null, out UInt32 cbBytes));
 
                            Debug.Assert(cbBytes > 0);
 
                            byte[] channelMask = new byte[cbBytes];
 
                            fixed (byte* pbChannelMask = channelMask)
                            {
                                HRESULT.Check(UnsafeNativeMethods.WICPixelFormatInfo.GetChannelMask(
                                    pixelFormatInfo, i, cbBytes, pbChannelMask, out cbBytes));
 
                                Debug.Assert(cbBytes == channelMask.Length);
                            }
 
                            masks[i] = new PixelFormatChannelMask(channelMask);
                        }
                    }
                }
                finally
                {
                    if (pixelFormatInfo != IntPtr.Zero)
                    {
                        UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref pixelFormatInfo);
                    }
                }
 
                return new ReadOnlyCollection<PixelFormatChannelMask>(masks);
            }
        }
 
        internal readonly IntPtr CreatePixelFormatInfo()
        {
            IntPtr componentInfo = IntPtr.Zero;
            IntPtr pixelFormatInfo = IntPtr.Zero;
 
            using (FactoryMaker myFactory = new FactoryMaker())
            {
                try
                {
                    Guid guidPixelFormat = _guidFormat;
 
                    int hr = UnsafeNativeMethods.WICImagingFactory.CreateComponentInfo(
                        myFactory.ImagingFactoryPtr,
                        ref guidPixelFormat,
                        out componentInfo);
                    if (hr == (int)WinCodecErrors.WINCODEC_ERR_COMPONENTINITIALIZEFAILURE ||
                        hr == (int)WinCodecErrors.WINCODEC_ERR_COMPONENTNOTFOUND)
                    {
                        throw new NotSupportedException(SR.Image_NoPixelFormatFound);
                    }
                    HRESULT.Check(hr);
 
                    Guid guidPixelFormatInfo = MILGuidData.IID_IWICPixelFormatInfo;
                    HRESULT.Check(UnsafeNativeMethods.MILUnknown.QueryInterface(
                        componentInfo,
                        ref guidPixelFormatInfo,
                        out pixelFormatInfo));
                }
                finally
                {
                    if (componentInfo != IntPtr.Zero)
                    {
                        UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref componentInfo);
                    }
                }
            }
 
            return pixelFormatInfo;
        }
 
        internal int InternalBitsPerPixel
        {
            get
            {
                if (_bitsPerPixel == 0)
                {
                    UInt32 bpp = 0;
 
                    IntPtr pixelFormatInfo = CreatePixelFormatInfo();
                    Debug.Assert(pixelFormatInfo != IntPtr.Zero);
 
                    try
                    {
                        HRESULT.Check(UnsafeNativeMethods.WICPixelFormatInfo.GetBitsPerPixel(
                            pixelFormatInfo,
                            out bpp
                            ));
                    }
                    finally
                    {
                        if (pixelFormatInfo != IntPtr.Zero)
                        {
                            UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref pixelFormatInfo);
                        }
                    }
 
                    _bitsPerPixel = bpp;
                }
 
                return (int)_bitsPerPixel;
            }
        }
 
        internal readonly bool HasAlpha
        {
            get
            {
                return (FormatFlags & PixelFormatFlags.ChannelOrderABGR) != 0 ||
                       (FormatFlags & PixelFormatFlags.ChannelOrderARGB) != 0 ||
                       (FormatFlags & PixelFormatFlags.NChannelAlpha) != 0;
            }
        }
 
        internal readonly bool Palettized
        {
            get
            {
                return (FormatFlags & PixelFormatFlags.Palettized) != 0;
            }
        }
 
        internal readonly PixelFormatEnum Format
        {
            get
            {
                return _format;
            }
        }
 
        internal readonly Guid Guid
        {
            get
            {
                return _guidFormat;
            }
        }
 
        /// <summary>
        /// Convert a PixelFormat to a string that represents it.
        /// </summary>
        /// <returns></returns>
        public override readonly string ToString()
        {
            return _format.ToString();
        }
 
        internal static PixelFormat GetPixelFormat(SafeMILHandle /* IWICBitmapSource */ bitmapSource)
        {
            Guid guidPixelFormat = WICPixelFormatGUIDs.WICPixelFormatDontCare;
 
            HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.GetPixelFormat(bitmapSource, out guidPixelFormat));
 
            return new PixelFormat(guidPixelFormat);
        }
 
        /// <summary>
        /// Convert from the internal guid to the actual PixelFormat value.
        /// </summary>
        /// <param name="pixelFormatGuid"></param>
        /// <returns></returns>
        internal static PixelFormat GetPixelFormat(Guid pixelFormatGuid)
        {
            Span<byte> guidBytes = stackalloc byte[16];
            pixelFormatGuid.TryWriteBytes(guidBytes);
 
            return GetPixelFormat((PixelFormatEnum)guidBytes[15]);
        }
 
        /// <summary>
        /// Convert from the internal enum to the actual PixelFormat value.
        /// </summary>
        /// <param name="pixelFormatEnum"></param>
        /// <returns></returns>
        internal static PixelFormat GetPixelFormat(PixelFormatEnum pixelFormatEnum) => pixelFormatEnum switch
        {
            PixelFormatEnum.Indexed1 => PixelFormats.Indexed1,
            PixelFormatEnum.Indexed2 => PixelFormats.Indexed2,
            PixelFormatEnum.Indexed4 => PixelFormats.Indexed4,
            PixelFormatEnum.Indexed8 => PixelFormats.Indexed8,
            PixelFormatEnum.BlackWhite => PixelFormats.BlackWhite,
            PixelFormatEnum.Gray2 => PixelFormats.Gray2,
            PixelFormatEnum.Gray4 => PixelFormats.Gray4,
            PixelFormatEnum.Gray8 => PixelFormats.Gray8,
            PixelFormatEnum.Bgr555 => PixelFormats.Bgr555,
            PixelFormatEnum.Bgr565 => PixelFormats.Bgr565,
            PixelFormatEnum.Bgr101010 => PixelFormats.Bgr101010,
            PixelFormatEnum.Bgr24 => PixelFormats.Bgr24,
            PixelFormatEnum.Rgb24 => PixelFormats.Rgb24,
            PixelFormatEnum.Bgr32 => PixelFormats.Bgr32,
            PixelFormatEnum.Bgra32 => PixelFormats.Bgra32,
            PixelFormatEnum.Pbgra32 => PixelFormats.Pbgra32,
            PixelFormatEnum.Rgb48 => PixelFormats.Rgb48,
            PixelFormatEnum.Rgba64 => PixelFormats.Rgba64,
            PixelFormatEnum.Prgba64 => PixelFormats.Prgba64,
            PixelFormatEnum.Gray16 => PixelFormats.Gray16,
            PixelFormatEnum.Gray32Float => PixelFormats.Gray32Float,
            PixelFormatEnum.Rgb128Float => PixelFormats.Rgb128Float,
            PixelFormatEnum.Rgba128Float => PixelFormats.Rgba128Float,
            PixelFormatEnum.Prgba128Float => PixelFormats.Prgba128Float,
            PixelFormatEnum.Cmyk32 => PixelFormats.Cmyk32,
            _ => PixelFormats.Default,
        };
 
        private static PixelFormatFlags GetPixelFormatFlagsFromGuid(Guid pixelFormatGuid)
        {
            PixelFormatFlags result = PixelFormatFlags.BitsPerPixelUndefined;
 
            if (pixelFormatGuid.CompareTo(WICPixelFormatPhotonFirst) >= 0 && pixelFormatGuid.CompareTo(WICPixelFormatPhotonLast) <= 0)
            {
                Span<byte> guidBytes = stackalloc byte[16];
                pixelFormatGuid.TryWriteBytes(guidBytes);
 
                result = guidBytes[15] switch
                {
                    // GUID_WICPixelFormat64bppRGBAFixedPoint
                    0x1D => PixelFormatFlags.ChannelOrderARGB | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat128bppRGBAFixedPoint
                    0x1E => PixelFormatFlags.ChannelOrderARGB | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat64bppCMYK
                    0x1F => PixelFormatFlags.IsCMYK,
                    // GUID_WICPixelFormat24bpp3Channels
                    0x20 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat32bpp4Channels
                    0x21 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat40bpp5Channels
                    0x22 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat48bpp6Channels
                    0x23 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat56bpp7Channels
                    0x24 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat64bpp8Channels
                    0x25 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat48bpp3Channels
                    0x26 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat64bpp4Channels
                    0x27 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat80bpp5Channels
                    0x28 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat96bpp6Channels
                    0x29 => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat112bpp7Channels
                    0x2A => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat128bpp8Channels
                    0x2B => PixelFormatFlags.IsNChannel,
                    // GUID_WICPixelFormat40bppCMYKAlpha
                    0x2C => PixelFormatFlags.IsCMYK | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat80bppCMYKAlpha
                    0x2D => PixelFormatFlags.IsCMYK | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat32bpp3ChannelsAlpha
                    0x2E => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat40bpp4ChannelsAlpha
                    0x2F => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat48bpp5ChannelsAlpha
                    0x30 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat56bpp6ChannelsAlpha
                    0x31 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat64bpp7ChannelsAlpha
                    0x32 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat72bpp8ChannelsAlpha
                    0x33 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat64bpp3ChannelsAlpha
                    0x34 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat80bpp4ChannelsAlpha
                    0x35 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat96bpp5ChannelsAlpha
                    0x36 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat112bpp6ChannelsAlpha
                    0x37 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat128bpp7ChannelsAlpha
                    0x38 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat144bpp8ChannelsAlpha
                    0x39 => PixelFormatFlags.IsNChannel | PixelFormatFlags.NChannelAlpha,
                    // GUID_WICPixelFormat64bppRGBAHalf
                    0x3A => PixelFormatFlags.ChannelOrderARGB | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat48bppRGBHalf
                    0x3B => PixelFormatFlags.ChannelOrderRGB | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat32bppRGBE
                    0x3D => PixelFormatFlags.ChannelOrderRGB | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat16bppGrayHalf
                    0x3E => PixelFormatFlags.IsGray | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat32bppGrayFixedPoint
                    0x3F => PixelFormatFlags.IsGray | PixelFormatFlags.IsScRGB,
                    // GUID_WICPixelFormat64bppRGBFixedPoint
                    0x40 => PixelFormatFlags.IsScRGB | PixelFormatFlags.ChannelOrderRGB,
                    // GUID_WICPixelFormat128bppRGBFixedPoint
                    0x41 => PixelFormatFlags.IsScRGB | PixelFormatFlags.ChannelOrderRGB,
                    // GUID_WICPixelFormat64bppRGBHalf
                    0x42 => PixelFormatFlags.IsScRGB | PixelFormatFlags.ChannelOrderRGB,
                    _ => PixelFormatFlags.BitsPerPixelUndefined
                };
            }
 
            return result;
        }
 
        private static PixelFormatFlags GetPixelFormatFlagsFromEnum(PixelFormatEnum pixelFormatEnum) => pixelFormatEnum switch
        {
            PixelFormatEnum.Default => PixelFormatFlags.BitsPerPixelUndefined,
            PixelFormatEnum.Indexed1 => PixelFormatFlags.BitsPerPixel1 | PixelFormatFlags.Palettized,
            PixelFormatEnum.Indexed2 => PixelFormatFlags.BitsPerPixel2 | PixelFormatFlags.Palettized,
            PixelFormatEnum.Indexed4 => PixelFormatFlags.BitsPerPixel4 | PixelFormatFlags.Palettized,
            PixelFormatEnum.Indexed8 => PixelFormatFlags.BitsPerPixel8 | PixelFormatFlags.Palettized,
            PixelFormatEnum.BlackWhite => PixelFormatFlags.BitsPerPixel1 | PixelFormatFlags.IsGray,
            PixelFormatEnum.Gray2 => PixelFormatFlags.BitsPerPixel2 | PixelFormatFlags.IsGray,
            PixelFormatEnum.Gray4 => PixelFormatFlags.BitsPerPixel4 | PixelFormatFlags.IsGray,
            PixelFormatEnum.Gray8 => PixelFormatFlags.BitsPerPixel8 | PixelFormatFlags.IsGray,
            PixelFormatEnum.Bgr555 => PixelFormatFlags.BitsPerPixel16 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderBGR,
            PixelFormatEnum.Bgr565 => PixelFormatFlags.BitsPerPixel16 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderBGR,
            PixelFormatEnum.Bgr101010 => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderBGR,
            PixelFormatEnum.Bgr24 => PixelFormatFlags.BitsPerPixel24 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderBGR,
            PixelFormatEnum.Rgb24 => PixelFormatFlags.BitsPerPixel24 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderRGB,
            PixelFormatEnum.Bgr32 => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderBGR,
            PixelFormatEnum.Bgra32 => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderABGR,
            PixelFormatEnum.Pbgra32 => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsSRGB | PixelFormatFlags.Premultiplied | PixelFormatFlags.ChannelOrderABGR,
            PixelFormatEnum.Rgb48 => PixelFormatFlags.BitsPerPixel48 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderRGB,
            PixelFormatEnum.Rgba64 => PixelFormatFlags.BitsPerPixel64 | PixelFormatFlags.IsSRGB | PixelFormatFlags.ChannelOrderARGB,
            PixelFormatEnum.Prgba64 => PixelFormatFlags.BitsPerPixel64 | PixelFormatFlags.IsSRGB | PixelFormatFlags.Premultiplied | PixelFormatFlags.ChannelOrderARGB,
            PixelFormatEnum.Gray16 => PixelFormatFlags.BitsPerPixel16 | PixelFormatFlags.IsSRGB | PixelFormatFlags.IsGray,
            PixelFormatEnum.Gray32Float => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsScRGB | PixelFormatFlags.IsGray,
            PixelFormatEnum.Rgb128Float => PixelFormatFlags.BitsPerPixel128 | PixelFormatFlags.IsScRGB | PixelFormatFlags.ChannelOrderRGB,
            PixelFormatEnum.Rgba128Float => PixelFormatFlags.BitsPerPixel128 | PixelFormatFlags.IsScRGB | PixelFormatFlags.ChannelOrderARGB,
            PixelFormatEnum.Prgba128Float => PixelFormatFlags.BitsPerPixel128 | PixelFormatFlags.IsScRGB | PixelFormatFlags.Premultiplied | PixelFormatFlags.ChannelOrderARGB,
            PixelFormatEnum.Cmyk32 => PixelFormatFlags.BitsPerPixel32 | PixelFormatFlags.IsCMYK,
            // 3rd party pixel format -- we don't expose anything about it.
            _ => PixelFormatFlags.BitsPerPixelUndefined,
        };
 
        private static UInt32 GetBitsPerPixelFromEnum(PixelFormatEnum pixelFormatEnum) => pixelFormatEnum switch
        {
            PixelFormatEnum.Default => 0,
            PixelFormatEnum.Indexed1 => 1,
            PixelFormatEnum.Indexed2 => 2,
            PixelFormatEnum.Indexed4 => 4,
            PixelFormatEnum.Indexed8 => 8,
            PixelFormatEnum.BlackWhite => 1,
            PixelFormatEnum.Gray2 => 2,
            PixelFormatEnum.Gray4 => 4,
            PixelFormatEnum.Gray8 => 8,
            PixelFormatEnum.Bgr555 or PixelFormatEnum.Bgr565 => 16,
            PixelFormatEnum.Bgr101010 => 32,
            PixelFormatEnum.Bgr24 or PixelFormatEnum.Rgb24 => 24,
            PixelFormatEnum.Bgr32 or PixelFormatEnum.Bgra32 or PixelFormatEnum.Pbgra32 => 32,
            PixelFormatEnum.Rgb48 => 48,
            PixelFormatEnum.Rgba64 or PixelFormatEnum.Prgba64 => 64,
            PixelFormatEnum.Gray16 => 16,
            PixelFormatEnum.Gray32Float => 32,
            PixelFormatEnum.Rgb128Float or PixelFormatEnum.Rgba128Float or PixelFormatEnum.Prgba128Float => 128,
            PixelFormatEnum.Cmyk32 => 32,
            // 3rd party pixel format -- we don't expose anything about it.
            _ => 0,
        };
 
        [NonSerialized]
        private readonly PixelFormatFlags _flags;
 
        [NonSerialized]
        private readonly PixelFormatEnum _format;
 
        [NonSerialized]
        private UInt32 _bitsPerPixel;
 
        [NonSerialized]
        private readonly Guid _guidFormat;
 
        [NonSerialized]
        private static readonly Guid WICPixelFormatPhotonFirst = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x1d);
 
        [NonSerialized]
        private static readonly Guid WICPixelFormatPhotonLast  = new Guid(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x42);
    }
    #endregion // PixelFormat
}