File: System\Windows\Media\ColorTransform.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.
 
namespace System.Windows.Media
{
    internal class ColorTransform
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
 
        private ColorTransform()
        {
        }
 
        internal ColorTransform(ColorContext srcContext, ColorContext dstContext)
        {
            InitializeICM();
 
            if (srcContext == null)
            {
                srcContext = new ColorContext(PixelFormats.Bgra32);
            }
 
            if (dstContext == null)
            {
                dstContext = new ColorContext(PixelFormats.Bgra32);
            }
 
            _inputColorType = srcContext.ColorType;
            _outputColorType = dstContext.ColorType;
 
            _colorTransformHelper.CreateTransform(srcContext.ProfileHandle, dstContext.ProfileHandle);
        }
 
        internal ColorTransform(SafeMILHandle bitmapSource, ColorContext srcContext, ColorContext dstContext, System.Windows.Media.PixelFormat pixelFormat)
        {
            InitializeICM();
 
            if (srcContext == null)
            {
                srcContext = new ColorContext(pixelFormat);
            }
            if (dstContext == null)
            {
                dstContext = new ColorContext(pixelFormat);
            }
 
            _inputColorType = srcContext.ColorType;
            _outputColorType = dstContext.ColorType;
 
            //if this failed or the handle is invalid, we can't continue
            if (srcContext.ProfileHandle != null && !srcContext.ProfileHandle.IsInvalid)
            {
                //if this failed or the handle is invalid, we can't continue
                if (dstContext.ProfileHandle != null && !dstContext.ProfileHandle.IsInvalid)
                {
                    _colorTransformHelper.CreateTransform(srcContext.ProfileHandle, dstContext.ProfileHandle);
                }
            }
        }
        #endregion
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        #region Public Methods
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        internal unsafe void Translate(Span<float> srcValue, Span<float> dstValue)
        {
            // Transform colors using TranslateColors
            const UInt32 NumColors = 1;
 
            // There's no SkipLocalsInit, will be all zeroes
            long* paInputColors = stackalloc long[64 / sizeof(long)];
            long* paOutputColors = stackalloc long[64 / sizeof(long)];
 
            long inputColor = ICM2Color(srcValue);
            paInputColors[0] = inputColor;
 
            _colorTransformHelper.TranslateColors((nint)paInputColors, NumColors, _inputColorType, (nint)paOutputColors, _outputColorType);
 
            long outputColor = paOutputColors[0];
            for (int i = 0; i < dstValue.Length; i++)
            {
                UInt32 result = 0x0000ffff & (UInt32)(outputColor >> (16 * i));
                float a = (result & 0x7fffffff) / (float)0x10000;
 
                if (result < 0)
                    dstValue[i] = -a;
                else
                    dstValue[i] = a;
            }
        }
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        private void InitializeICM()
        {
            _colorTransformHelper = new ColorTransformHelper();
        }
 
        private long ICM2Color(Span<float> srcValue)
        {
            long colorValue;
 
            if (srcValue.Length < 3 || srcValue.Length > 8)
            {
                throw new NotSupportedException(); // Only support color spaces with 3,4,5,6,7,8 channels
            }
 
            if (srcValue.Length <= 4)
            {
                Span<UInt16> channel = stackalloc UInt16[4];
 
                for (int i = 0; i < srcValue.Length; i++)
                {
                    if (srcValue[i] >= 1.0)// this fails for values above 1.0 and below 0.0
                    {
                        channel[i] = 0xffff;
                    }
                    else if (srcValue[i] <= 0.0)
                    {
                        channel[i] = 0x0;
                    }
                    else
                    {
                        channel[i] = (UInt16)(srcValue[i] * (float)0xFFFF);
                    }
                }
 
                colorValue = (long)(((UInt64)channel[3] << 48) + ((UInt64)channel[2] << 32) + ((UInt64)channel[1] << 16) + (UInt64)channel[0]);
            }
            else
            {
                Span<byte> channel = stackalloc byte[8];
 
                for (int i = 0; i < srcValue.Length; i++)
                {
                    if (srcValue[i] >= 1.0)// this fails for values above 1.0 and below 0.0
                    {
                        channel[i] = 0xff;
                    }
                    else if (srcValue[i] <= 0.0)
                    {
                        channel[i] = 0x0;
                    }
                    else
                    {
                        channel[i] = (byte)(srcValue[i] * (float)0xFF);
                    }
                }
 
                colorValue = (long)(((UInt64)channel[7] << 56) + ((UInt64)channel[6] << 48) +
                                    ((UInt64)channel[5] << 40) + ((UInt64)channel[4] << 32) +
                                    ((UInt64)channel[3] << 24) + ((UInt64)channel[2] << 16) +
                                    ((UInt64)channel[1] << 8) + ((UInt64)channel[0] << 0));
            }
            return colorValue;
        }
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        private ColorTransformHelper _colorTransformHelper;
 
        private readonly UInt32 _inputColorType;
 
        private readonly UInt32 _outputColorType;
 
        #endregion
    }
}