【Openxml】颜色变化属性计算

  • 【Openxml】颜色变化属性计算已关闭评论
  • 217 次浏览
  • A+
所属分类:.NET技术
摘要

目前Openxml存在颜色变化属性如下:其中有关RGB和Hsl的相互转换的公式如下:RGB转Hsl公式如下:


Openxml的颜色变化属性

目前Openxml存在颜色变化属性如下:

参数 说明
Hue 色调(色相)
HueModulate 色调调制,百分比
HueOffset 色调偏移量,角度值
Saturation 饱和度
SaturationModulation 饱和度调制,百分比
SaturationOffset 饱和度偏移量
Luminance 亮度
LuminanceModulation 亮度调制,百分比
LuminanceOffset 亮度偏移量
Alpha Alpha
AlphaModulation Alpha 调制,百分比
AlphaOffset Alpha 偏移量
Red 红色
RedModulation 红色调制,百分比
RedOffset 红色偏移量
Blue 蓝色
BlueModification 蓝色调制
BlueOffse 蓝色偏移量,百分比
Green 绿色
GreenModification 绿色调制,百分比
GreenOffset 绿色偏移量
Complement 补充
Gamma 伽玛
Gray 灰色
Inverse 反函数
Inverse Gamma 反函数伽玛
Shade 底纹,百分比
Tint 底纹,百分比

RGB和Hsl的相互转换

其中有关RGB和Hsl的相互转换的公式如下:

RGB转Hsl公式如下:

【Openxml】颜色变化属性计算

Hsl转RGB公式如下:

【Openxml】颜色变化属性计算

其中涉及到有Hsl计算的有以下九个属性:

  • Hue、HueModulate、HueOffset
  • Saturation、SaturationModulation、SaturationOffset
  • Luminance、LuminanceModulation、LuminanceOffset

那么我们开始写代码:

定义RGB的类:

    /// <summary>     /// 用 A R G B 表示的颜色     /// </summary>     public class ARgbColor     {         /// <summary>         /// 创建 A R G B 颜色         /// </summary>         public ARgbColor()         {         }          /// <summary>         /// 创建 A R G B 颜色         /// </summary>         /// <param name="a"></param>         /// <param name="r"></param>         /// <param name="g"></param>         /// <param name="b"></param>         public ARgbColor(byte a, byte r, byte g, byte b)         {             A = a;             R = r;             G = g;             B = b;         }          /// <summary>         /// 表示透明色         /// </summary>         public byte A { set; get; }          /// <summary>         /// 表示红色         /// </summary>         public byte R { set; get; }          /// <summary>         /// 表示绿色         /// </summary>         public byte G { set; get; }          /// <summary>         /// 表示蓝色         /// </summary>         public byte B { set; get; }      }  

定义颜色变化相关类ColorTransform,并且定义RGB和Hsl的相互转换逻辑方法:

    /// <summary>     ///     处理颜色之间的变换,调整,格式转换     /// </summary>     public static class ColorTransform     {          /// <summary>         ///  将<see cref="Color" />的数据转换为Hsl         /// </summary>         /// <param name="color"></param>         /// <returns></returns>         public static (Degree hue, Percentage sat, Percentage lum, byte alpha) ColorToHsl(Color color)         {             var max = System.Math.Max(color.R, System.Math.Max(color.G, color.B));             var min = System.Math.Min(color.R, System.Math.Min(color.G, color.B));             var delta = max - min;             var l = Percentage.FromDouble((max + min) / 2.0 / 255.0);             var h = Degree.FromDouble(0);             var s = Percentage.Zero;              if (delta > 0)             {                 s = l < Percentage.FromDouble(0.5)                     ? Percentage.FromDouble((max - min) * 1.0 / (max + min))                     : Percentage.FromDouble((max - min) * 1.0 / (2 * 255 - max - min));                  if (max == color.R)                 {                     h = Degree.FromDouble((0 + (color.G - color.B) * 1.0 / delta) * 60);                 }                 else if (max == color.G)                 {                     h = Degree.FromDouble((2 + (color.B - color.R) * 1.0 / delta) * 60);                 }                 else                 {                     h = Degree.FromDouble((4 + (color.R - color.G) * 1.0 / delta) * 60);                 }             }              return (h, s, l, color.A);         }      }              /// <summary>         ///     将Hsl的数据转换为<see cref="Color" />         /// </summary>         /// <param name="hue">色相</param>         /// <param name="saturation">饱和度</param>         /// <param name="lightness">亮度</param>         /// <param name="a">透明度</param>         /// <returns></returns>         public static Color HslToColor(Degree hue, Percentage saturation, Percentage lightness, byte a = 0xFF)         {             var color = new Color { A = a };              var hueValue = hue.DoubleValue;             var saturationValue = saturation.DoubleValue;             var lightnessValue = lightness.DoubleValue;              var c = (1 - System.Math.Abs(2 * lightnessValue - 1)) * saturationValue;             var x = c * (1 - System.Math.Abs((hueValue / 60) % 2 - 1));             var m = lightnessValue - c / 2;              var r = 0d;             var g = 0d;             var b = 0d;              if (hueValue is >= 0 and < 60)             {                 r = c;                 g = x;                 b = 0;             }              if (hueValue is >= 60 and < 120)             {                 r = x;                 g = c;                 b = 0;             }              if (hueValue is >= 120 and < 180)             {                 r = 0;                 g = c;                 b = x;             }              if (hueValue is >= 180 and < 240)             {                 r = 0;                 g = x;                 b = c;             }              if (hueValue is >= 240 and < 300)             {                 r = x;                 g = 0;                 b = c;             }              if (hueValue is >= 300 and < 360)             {                 r = c;                 g = 0;                 b = x;             }              color.R = (byte) ((r + m) * 255);             color.G = (byte) ((g + m) * 255);             color.B = (byte) ((b + m) * 255);              return color;         }   

然后我们来写真正处理Openxml的Hsl相关属性逻辑:

       /// <summary>         ///     将<see cref="RgbColorModelHex" />转换为<see cref="Color" />         /// </summary>         /// <param name="color"></param>         /// <returns></returns>         public static Color? ToColor(this RgbColorModelHex color)         {             if (color.Val is not null)             {                 if (uint.TryParse(color.Val.Value, NumberStyles.HexNumber, null, out var result))                 {                     var solidColor = result.HexToColor();                     var modifiedColor = ColorTransform.AppendColorModify(solidColor, color.ChildElements);                     return modifiedColor;                 }             }              return null;         }           private static Color HexToColor(this uint rgb)         {             var color = new Color();             const int maxByte = 0xff;              color.B = (byte) (rgb & maxByte);             color.G = (byte) ((rgb >> 8) & maxByte);             color.R = (byte) ((rgb >> 16) & maxByte);             color.A = 0xFF;              return color;         }          /// <summary>         /// 给颜色叠加转换         /// </summary>         /// <param name="color"></param>         /// <param name="list"></param>         /// <returns></returns>         public static Color AppendColorModify(ARgbColor color, OpenXmlElementList list)         {             var updatedColor = color;             foreach (var element in list)             {                 if (element is Hue hue)                 {                     updatedColor = HandleHue(updatedColor, hue, null, null);                     continue;                 }                  if (element is HueModulation hueModulation)                 {                     updatedColor = HandleHue(updatedColor, null, hueModulation, null);                     continue;                 }                  if (element is HueOffset hueOffset)                 {                     updatedColor = HandleHue(updatedColor, null, null, hueOffset);                     continue;                 }                  if (element is Saturation saturation)                 {                     updatedColor = HandleSaturation(updatedColor, saturation, null, null);                     continue;                 }                  if (element is SaturationModulation saturationModulation)                 {                     updatedColor = HandleSaturation(updatedColor, null, saturationModulation, null);                     continue;                 }                  if (element is SaturationOffset saturationOffset)                 {                     updatedColor = HandleSaturation(updatedColor, null, null, saturationOffset);                     continue;                 }                  if (element is Luminance luminance)                 {                     updatedColor = HandleLuminance(updatedColor, luminance, null, null);                     continue;                 }                  if (element is LuminanceModulation luminanceModulation)                 {                     updatedColor = HandleLuminance(updatedColor, null, luminanceModulation, null);                     continue;                 }                  if (element is LuminanceOffset luminanceOffset)                 {                     updatedColor = HandleLuminance(updatedColor, null, null, luminanceOffset);                     continue;                 }             }           private static Color HandleHue(Color color, Hue? hueElement, HueModulation? hueModElement,             HueOffset? hueOffsetElement)         {             if (hueElement is null && hueModElement is null && hueOffsetElement is null)             {                 return color;             }              var updatedColor = HandleHslCore(color, hueElement: hueElement, hueModElement: hueModElement, hueOffsetElement: hueOffsetElement);              return updatedColor;         }          private static Color HandleSaturation(Color color, Saturation? satElement, SaturationModulation? satModElement,             SaturationOffset? satOffsetElement)         {             if (satElement is null && satModElement is null && satOffsetElement is null)             {                 return color;             }              var updatedColor = HandleHslCore(color, satElement: satElement, satModElement: satModElement, satOffsetElement: satOffsetElement);              return updatedColor;         }          private static Color HandleLuminance(Color color, Luminance? lumElement, LuminanceModulation? lumModElement,             LuminanceOffset? lumOffsetElement)         {             if (lumElement is null && lumModElement is null && lumOffsetElement is null)             {                 return color;             }              var updatedColor = HandleHslCore(color, lumElement: lumElement, lumModElement: lumModElement, lumOffsetElement: lumOffsetElement);              return updatedColor;         }           private static Color HandleHslCore(Color color,             Hue? hueElement = null, HueModulation? hueModElement = null, HueOffset? hueOffsetElement = null,             Saturation? satElement = null, SaturationModulation? satModElement = null, SaturationOffset? satOffsetElement = null,             Luminance? lumElement = null, LuminanceModulation? lumModElement = null, LuminanceOffset? lumOffsetElement = null)         {             if (hueElement is null && hueModElement is null && hueOffsetElement is null                 && satElement is null && satModElement is null && satOffsetElement is null                 && lumElement is null && lumModElement is null && lumOffsetElement is null)             {                 return color;             }              var (hue, sat, lum, alpha) = ColorToHsl(color);              var hueElementVal = hueElement?.Val;             var hueValue = hueElementVal is not null ? new Angle(hueElementVal).ToDegreeValue() : hue.DoubleValue;             var satElementVal = satElement?.Val;             var satValue = satElementVal is not null ? new Percentage(satElementVal).DoubleValue : sat.DoubleValue;             var lumElementVal = lumElement?.Val;             var lumValue = lumElementVal is not null ? new Percentage(lumElementVal).DoubleValue : lum.DoubleValue;              var hueModElementVal = hueModElement?.Val;             var hueModValue = hueModElementVal is not null && hueModElementVal.HasValue                 ? new Percentage(hueModElementVal)                 : Percentage.FromDouble(1);             var satModElementVal = satModElement?.Val;             var satModValue = satModElementVal is not null && satModElementVal.HasValue                 ? new Percentage(satModElementVal)                 : Percentage.FromDouble(1);             var lumModElementVal = lumModElement?.Val;             var lumModValue = lumModElementVal is not null && lumModElementVal.HasValue                 ? new Percentage(lumModElementVal)                 : Percentage.FromDouble(1);              var hueOffsetVal = hueOffsetElement?.Val;             var hueOffset = hueOffsetVal is not null && hueOffsetVal.HasValue                 ? new Angle(hueOffsetVal).ToDegreeValue()                 : new Angle(0).ToDegreeValue();             var saturationOffsetVal = satOffsetElement?.Val;             var saturationOffset = saturationOffsetVal is not null && saturationOffsetVal.HasValue                 ? new Percentage(saturationOffsetVal)                 : Percentage.Zero;             var lumOffsetElementVal = lumOffsetElement?.Val;             var lumOffset = lumOffsetElementVal is not null && lumOffsetElementVal.HasValue                 ? new Percentage(lumOffsetElementVal)                 : Percentage.Zero;              var hueResult = hueValue * hueModValue.DoubleValue + hueOffset;             hue = Degree.FromDouble(hueResult);              var satResult = satValue * satModValue.DoubleValue + saturationOffset.DoubleValue;             sat = Percentage.FromDouble(satResult);             sat = sat > Percentage.FromDouble(1) ? Percentage.FromDouble(1) : sat;             sat = sat < Percentage.Zero ? Percentage.Zero : sat;              var lumResult = lumValue * lumModValue.DoubleValue + lumOffset.DoubleValue;             lum = Percentage.FromDouble(lumResult);             lum = lum > Percentage.FromDouble(1) ? Percentage.FromDouble(1) : lum;             lum = lum < Percentage.Zero ? Percentage.Zero : lum;              return HslToColor(hue, sat, lum, alpha);          }    

处理RGB相关属性

涉及到RGB相关的Openxml属性如下:

  • 透明度:Alpha、AlphaModulation、AlphaOffset
  • RGB的红色:Red、RedModulation、RedOffset
  • RGB的蓝色:Blue、BlueModulation、BlueOffset
  • RGB的绿色:Green、GreenModulation、GreenOffset
  • RGB的反函数:Inverse
  • RGB的补码: Complement
  • RGB的伽玛校正和反伽玛矫正: Gamma、InverseGamma
  • RGB的灰阶(灰度):Gray

处理透明度

以下为处理透明度的逻辑代码:

           private static Color HandleAlphaModify(Color color, Alpha? alphaElement, AlphaModulation? alphaModulation, AlphaOffset? alphaOffset)         {             if (alphaElement is null && alphaModulation is null && alphaOffset is null)             {                 return color;             }              var alphaValue = alphaElement?.Val;             var modulationVal = alphaModulation?.Val;             var offsetVal = alphaOffset?.Val;              var alpha = alphaValue is not null && alphaValue.HasValue                 ? new Percentage(alphaValue)                 : Percentage.FromDouble(1);              var mod = modulationVal is not null && modulationVal.HasValue                 ? new Percentage(modulationVal)                 : Percentage.FromDouble(1);              var off = offsetVal is not null && offsetVal.HasValue                 ? new Percentage(offsetVal)                 : Percentage.Zero;               var alphaResult = alpha.DoubleValue * mod.DoubleValue + off.DoubleValue;             color.A = (byte) (color.A * alphaResult);               return color;         }          

处理RGB的红色、蓝色、绿色

以下为处理RGB的红色、蓝色、绿色的逻辑代码:

         private static Color HandleRgb(Color color, Red? redElement, Green? greenElement, Blue? blueElement)         {             if (redElement is null && greenElement is null && blueElement is null)             {                 return color;             }              var updatedColor = HandleRgbCore(color, redElement: redElement, greenElement: greenElement,                 blueElement: blueElement);               return updatedColor;         }         private static Color HandleRgbModulation(Color color, RedModulation? redModulationElement, GreenModulation? greenModulationElement, BlueModulation? blueModulationElement)         {             if (redModulationElement is null && greenModulationElement is null && blueModulationElement is null)             {                 return color;             }              var updatedColor = HandleRgbCore(color, redModulationElement: redModulationElement,                 greenModulationElement: greenModulationElement, blueModulationElement: blueModulationElement);               return updatedColor;         }           private static Color HandleRgbOffset(Color color, RedOffset? redOffsetElement, GreenOffset? greenOffsetElement, BlueOffset? blueOffsetElement)         {             if (redOffsetElement is null && blueOffsetElement is null && greenOffsetElement is null)             {                 return color;             }              var updatedColor = HandleRgbCore(color, redOffsetElement: redOffsetElement,                 greenOffsetElement: greenOffsetElement, blueOffsetElement: blueOffsetElement);               return updatedColor;         }            private static Color HandleRgbCore(Color color,             Red? redElement = null, Green? greenElement = null, Blue? blueElement = null,             RedModulation? redModulationElement = null, GreenModulation? greenModulationElement = null, BlueModulation? blueModulationElement = null,             RedOffset? redOffsetElement = null, GreenOffset? greenOffsetElement = null, BlueOffset? blueOffsetElement = null)         {             if (redElement is null && greenElement is null && blueElement is null                 && redModulationElement is null && greenModulationElement is null && blueModulationElement is null                 && redOffsetElement is null && greenOffsetElement is null && blueOffsetElement is null)             {                 return color;             }              var updatedColor = color;              var redModulationValue = redModulationElement?.Val;             var redMod = redModulationValue is not null ? new Percentage(redModulationValue) : Percentage.FromDouble(1);              var greenModulationValue = greenModulationElement?.Val;             var greenMod = greenModulationValue is not null ? new Percentage(greenModulationValue) : Percentage.FromDouble(1);              var blueModulationValue = blueModulationElement?.Val;             var blueMod = blueModulationValue is not null ? new Percentage(blueModulationValue) : Percentage.FromDouble(1);              var redOffsetValue = redOffsetElement?.Val;             var redOffset = redOffsetValue is not null ? new Percentage(redOffsetValue) : Percentage.FromDouble(0);              var greenOffsetValue = greenOffsetElement?.Val;             var greenOffset = greenOffsetValue is not null ? new Percentage(greenOffsetValue) : Percentage.FromDouble(0);              var blueOffsetValue = blueOffsetElement?.Val;             var blueOffset = blueOffsetValue is not null ? new Percentage(blueOffsetValue) : Percentage.FromDouble(0);               var linearR = SRgbToLinearRgb(updatedColor.R / 255.0);             var linearG = SRgbToLinearRgb(updatedColor.G / 255.0);             var linearB = SRgbToLinearRgb(updatedColor.B / 255.0);              var redValue = redElement?.Val;             var red = redValue is not null ? new Percentage(redValue).DoubleValue : linearR;              var greenValue = greenElement?.Val;             var green = greenValue is not null ? new Percentage(greenValue).DoubleValue : linearG;              var blueValue = blueElement?.Val;             var blue = blueValue is not null ? new Percentage(blueValue).DoubleValue : linearB;              var redResult = red * redMod.DoubleValue + redOffset.DoubleValue;             var greenResult = green * greenMod.DoubleValue + greenOffset.DoubleValue;             var blueResult = blue * blueMod.DoubleValue + blueOffset.DoubleValue;               var r = redResult < 0 ? 0 : redResult > 1 ? 1 : redResult;             var g = greenResult < 0 ? 0 : greenResult > 1 ? 1 : greenResult;             var b = blueResult < 0 ? 0 : blueResult > 1 ? 1 : blueResult;             updatedColor.R = (byte) System.Math.Round(255 * LinearRgbToSRgb(r));             updatedColor.G = (byte) System.Math.Round(255 * LinearRgbToSRgb(g));             updatedColor.B = (byte) System.Math.Round(255 * LinearRgbToSRgb(b));              return updatedColor;         }          /// <summary>         /// https://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29         /// </summary>         /// <param name="sRgb"></param>         /// <returns></returns>         private static double SRgbToLinearRgb(double sRgb)         {             if (sRgb <= 0.04045) return sRgb / 12.92;              return System.Math.Pow((sRgb + 0.055) / 1.055, 2.4);         }   

RGB的反函数

以下为处理RGB的反函数的逻辑代码:

        private static Color HandleInverse(Color color, Inverse? inverseElement)         {             var updatedColor = color;             if (inverseElement != null)             {                 var linearR = SRgbToLinearRgb(updatedColor.R / 255.0);                 var linearG = SRgbToLinearRgb(updatedColor.G / 255.0);                 var linearB = SRgbToLinearRgb(updatedColor.B / 255.0);                 var r = System.Math.Abs(1.0 - linearR);                 var g = System.Math.Abs(1.0 - linearG);                 var b = System.Math.Abs(1.0 - linearB);                 updatedColor.R = (byte) System.Math.Round(255 * LinearRgbToSRgb(r));                 updatedColor.G = (byte) System.Math.Round(255 * LinearRgbToSRgb(g));                 updatedColor.B = (byte) System.Math.Round(255 * LinearRgbToSRgb(b));             }              return updatedColor;         }  

RGB的补码

以下为处理RGB的补码的逻辑代码:

         private static Color HandleComplement(Color color, Complement? complementElement)         {             var updatedColor = color;             if (complementElement != null)             {                 var r = updatedColor.B;                 var g = updatedColor.R + updatedColor.B - updatedColor.G;                 var b = updatedColor.R;                 updatedColor.R = r;                 updatedColor.G = (byte) g;                 updatedColor.B = b;             }              return updatedColor;         }  

RGB的伽玛校正和反伽玛矫正

伽玛校正

 实际上就是显示器的非线性特性让亮度在我们眼中看起来更好, 但是在渲染时反而会因此导致问题. 我们的渲染计算都是在伽马值为 1 的理想线性空间进行的,而显示器的非线性则是伽马值为 2.2计算的即为输入值的pow 2.2,伽马校正的思路就是在颜色被输送到显示器之前, 我们先对其进行 pow 1/2.2 的逆运算以抵消显示器的作用
【Openxml】颜色变化属性计算

因此计算伽玛校正的逻辑代码如下:

         /// <summary>         /// 对于sRGB的伽玛校正,也就是 1/2.2的幂运算         /// </summary>         /// <param name="color"></param>         /// <param name="gammaElement"></param>         /// <returns></returns>         private static Color HandleGamma(Color color, Gamma? gammaElement)         {             var updatedColor = color;             if (gammaElement != null)             {                 var r = System.Math.Pow(updatedColor.R / 255.0, 1 / 2.2);                 var g = System.Math.Pow(updatedColor.G / 255.0, 1 / 2.2);                 var b = System.Math.Pow(updatedColor.B / 255.0, 1 / 2.2);                 updatedColor.R = (byte) System.Math.Round(255 * r);                 updatedColor.G = (byte) System.Math.Round(255 * g);                 updatedColor.B = (byte) System.Math.Round(255 * b);             }              return updatedColor;         }  

而对于反伽玛校正,则其指数为2.2,代码如下:

         /// <summary>         /// 对于sRGB的反伽玛校正,也就是2.2的幂运算         /// </summary>         /// <param name="color"></param>         /// <param name="inverseGammaElement"></param>         /// <returns></returns>         private static Color HandleInverseGamma(Color color, InverseGamma? inverseGammaElement)         {             var updatedColor = color;             if (inverseGammaElement != null)             {                 var r = System.Math.Pow(updatedColor.R / 255.0, 2.2);                 var g = System.Math.Pow(updatedColor.G / 255.0, 2.2);                 var b = System.Math.Pow(updatedColor.B / 255.0, 2.2);                 updatedColor.R = (byte) System.Math.Round(255 * r);                 updatedColor.G = (byte) System.Math.Round(255 * g);                 updatedColor.B = (byte) System.Math.Round(255 * b);             }              return updatedColor;         }  

RGB的灰阶

不同的RGB空间,灰阶的计算公式有所不同,常见的几种RGB空间的计算灰阶的公式如下:

//简化 sRGB IEC61966-2.1 [gamma=2.20] Gray = (R^2.2 * 0.2126  + G^2.2  * 0.7152  + B^2.2  * 0.0722)^(1/2.2)  //Adobe RGB (1998) [gamma=2.20] Gray = (R^2.2 * 0.2973  + G^2.2  * 0.6274  + B^2.2  * 0.0753)^(1/2.2)  //Apple RGB [gamma=1.80] Gray = (R^1.8 * 0.2446  + G^1.8  * 0.6720  + B^1.8  * 0.0833)^(1/1.8)  //ColorMatch RGB [gamma=1.8] Gray = (R^1.8 * 0.2750  + G^1.8  * 0.6581  + B^1.8  * 0.0670)^(1/1.8)  //简化 KODAK DC Series Digital Camera [gamma=2.2] Gray = (R^2.2 * 0.2229  + G^2.2  * 0.7175  + B^2.2  * 0.0595)^(1/2.2)  

而我们选择了灰度系数2.2,即伽马值为2.2的sRGB的计算公式,那么逻辑代码如下:

        /// <summary>         /// 对于sRGB的灰阶计算         /// </summary>         /// <param name="color"></param>         /// <param name="grayElement"></param>         /// <returns></returns>         /// sRGB IEC61966-2.1 [gamma=2.20]:sRGB计算灰阶:Gray = (R^2.2 * 0.2126  + G^2.2  * 0.7152  + B^2.2  * 0.0722)^(1/2.2)         private static Color HandleGray(Color color, Gray? grayElement)         {             var updatedColor = color;             if (grayElement != null)             {                 var gray = System.Math.Pow(                           System.Math.Pow(updatedColor.R, 2.2) * 0.2126 +                           System.Math.Pow(updatedColor.G, 2.2) * 0.7152 +                           System.Math.Pow(updatedColor.B, 2.2) * 0.0722,                           1 / 2.2);                  var grayResult = (byte) System.Math.Round(gray);                  updatedColor.R = grayResult;                 updatedColor.G = grayResult;                 updatedColor.B = grayResult;             }              return updatedColor;         }  

参考