private void UpdatePropertiesIfNeeded(PropertyBasedEffectConfigToken newToken)
        {
            var whiteBalanceMethod = (WhiteBalanceMethod)newToken.GetProperty <StaticListChoiceProperty>(PropertyNames.WhiteBalanceMethod).Value;

            if (_lastSetWhiteBalanceMethod == whiteBalanceMethod)
            {
                return;
            }

            UpdateProperties(newToken, whiteBalanceMethod);
            _lastSetWhiteBalanceMethod = whiteBalanceMethod;
        }
        private void UpdateProperties(PropertyBasedEffectConfigToken newToken, WhiteBalanceMethod whiteBalanceMethod)
        {
            var whiteBalanceFunction = GetWhiteBalanceFunction(whiteBalanceMethod);

            if (whiteBalanceFunction == null)
            {
                return;
            }

            var(redGain, greenGain, blueGain) = whiteBalanceFunction(SrcArgs.Surface);
            (redGain, greenGain, blueGain)    = Normalize(redGain, greenGain, blueGain);

            newToken.SetPropertyValue(PropertyNames.RedGain, redGain);
            newToken.SetPropertyValue(PropertyNames.GreenGain, greenGain);
            newToken.SetPropertyValue(PropertyNames.BlueGain, blueGain);

            // XXX : How to update controls with new values ?
            // This cannot be done with PropertyBasedEffect effects (The so called IndirectUI).
            // The solution is to inherit from Effect, and develop the Winform UI manually. (See ScribbleEffect source code)
        }
        private Func <Surface, (double redGain, double greenGain, double blueGain)> GetWhiteBalanceFunction(WhiteBalanceMethod whiteBalanceMethod)
        {
            Func <Surface, (double redGain, double greenGain, double blueGain)> function = null;

            switch (whiteBalanceMethod)
            {
            case WhiteBalanceMethod.GrayWorld:
                function = GrayWorld;
                break;

            case WhiteBalanceMethod.Retinex:
                function = Retinex;
                break;

            case WhiteBalanceMethod.YCbCr:
                function = YCbCr;
                break;

            case WhiteBalanceMethod.None:
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(function);
        }