// Converts an RGB color to an HSV color. public static HsvColor ConvertRgbToHsv(double r, double b, double g) { double delta, min; double h = 0, s, v; min = Math.Min(Math.Min(r, g), b); v = Math.Max(Math.Max(r, g), b); delta = v - min; if (v == 0.0) { s = 0; } else s = delta / v; if (s == 0) h = 0.0f; else { if (r == v) h = (g - b) / delta; else if (g == v) h = 2 + (b - r) / delta; else if (b == v) h = 4 + (r - g) / delta; h *= 60; if (h < 0.0) h = h + 360; } HsvColor hsvColor = new HsvColor(); hsvColor.H = h; hsvColor.S = s; hsvColor.V = v / 255; return hsvColor; }
/*************************************************************************************** * * Methods * ***************************************************************************************/ /// <summary> /// Update the slider's Foreground and Background brushes based on the current slider state and color. /// </summary> /// <remarks> /// Manually refreshes the background gradient of the slider. /// This is callable separately for performance reasons. /// </remarks> public void UpdateColors() { HsvColor hsvColor = this.HsvColor; // Calculate and set the background this.UpdateBackground(hsvColor); // Calculate and set the foreground ensuring contrast with the background Color rgbColor = Uwp.Helpers.ColorHelper.FromHsv(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A); Color selectedRgbColor; double sliderPercent = this.Value / (this.Maximum - this.Minimum); if (this.ColorRepresentation == ColorRepresentation.Hsva) { if (this.IsAlphaMaxForced && this.ColorChannel != ColorChannel.Alpha) { hsvColor = new HsvColor() { H = hsvColor.H, S = hsvColor.S, V = hsvColor.V, A = 1.0 }; } switch (this.ColorChannel) { case ColorChannel.Channel1: { var channelValue = Math.Clamp(sliderPercent * 360.0, 0.0, 360.0); hsvColor = new HsvColor() { H = channelValue, S = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.S, V = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.V, A = hsvColor.A }; break; } case ColorChannel.Channel2: { var channelValue = Math.Clamp(sliderPercent * 1.0, 0.0, 1.0); hsvColor = new HsvColor() { H = hsvColor.H, S = channelValue, V = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.V, A = hsvColor.A }; break; } case ColorChannel.Channel3: { var channelValue = Math.Clamp(sliderPercent * 1.0, 0.0, 1.0); hsvColor = new HsvColor() { H = hsvColor.H, S = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.S, V = channelValue, A = hsvColor.A }; break; } } selectedRgbColor = Uwp.Helpers.ColorHelper.FromHsv( hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A); } else { if (this.IsAlphaMaxForced && this.ColorChannel != ColorChannel.Alpha) { rgbColor = new Color() { R = rgbColor.R, G = rgbColor.G, B = rgbColor.B, A = 255 }; } byte channelValue = Convert.ToByte(Math.Clamp(sliderPercent * 255.0, 0.0, 255.0)); switch (this.ColorChannel) { case ColorChannel.Channel1: rgbColor = new Color() { R = channelValue, G = rgbColor.G, B = rgbColor.B, A = rgbColor.A }; break; case ColorChannel.Channel2: rgbColor = new Color() { R = rgbColor.R, G = channelValue, B = rgbColor.B, A = rgbColor.A }; break; case ColorChannel.Channel3: rgbColor = new Color() { R = rgbColor.R, G = rgbColor.G, B = channelValue, A = rgbColor.A }; break; } selectedRgbColor = rgbColor; } var converter = new ContrastBrushConverter(); this.Foreground = converter.Convert(selectedRgbColor, typeof(Brush), this.DefaultForeground, null) as Brush; return; }
public static Color ToArgbColor(this HsvColor hsvColor) { return(FromHsvEx(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A)); }
public static Color Lighter(Color c) { var hsv = RgbToHsv(c); return(HsvToRgb(HsvColor.FromHsv(hsv.H, hsv.S, Math.Min(1.0, hsv.V * 1.2)))); }
public static ColorBgra ToColorBgra(this HsvColor color) { // HsvColor contains values scaled as in the color wheel: double h; double s; double v; double r = 0; double g = 0; double b = 0; // Scale Hue to be between 0 and 360. Saturation // and value scale to be between 0 and 1. h = (double)color.Hue % 360; s = (double)color.Saturation / 100; v = (double)color.Value / 100; if (s == 0) { // If s is 0, all colors are the same. // This is some flavor of gray. r = v; g = v; b = v; } else { double p; double q; double t; double fractionalSector; int sectorNumber; double sectorPos; // The color wheel consists of 6 sectors. // Figure out which sector you're in. sectorPos = h / 60; sectorNumber = (int)(Math.Floor(sectorPos)); // get the fractional part of the sector. // That is, how many degrees into the sector // are you? fractionalSector = sectorPos - sectorNumber; // Calculate values for the three axes // of the color. p = v * (1 - s); q = v * (1 - (s * fractionalSector)); t = v * (1 - (s * (1 - fractionalSector))); // Assign the fractional colors to r, g, and b // based on the sector the angle is in. switch (sectorNumber) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } } // return an RgbColor structure, with values scaled // to be between 0 and 255. return(ColorBgra.FromBgr((byte)(b * 255), (byte)(g * 255), (byte)(r * 255))); }
public int Compare(Color x, Color y) => HsvColor.FromColor(x).CompareTo(HsvColor.FromColor(y));
protected virtual void OnColorChanged(ColorPoint colorPoint, HsvColor oldValue, HsvColor newValue) { }
public override HsvColor GetColorByChannelLevelValue(HsvColor c, double value) { Color rgbColor = c.ToRgb(); return(HsvColor.FromRgb(Color.FromScRgb(rgbColor.ScA, rgbColor.ScR, rgbColor.ScG, ColorUtilities.sRgbToScRgb((float)value)))); }
public override Point GetColorDetailsMarkerCoord(HsvColor hsvColor) { return(new Point(hsvColor.S, hsvColor.V)); }
public override double GetChanellLevelValue(HsvColor c) { return(ColorUtilities.ScRgbTosRgb(c.ToRgb().ScB)); }
public override HsvColor GetColorByColorDetailsMarkerCoord(HsvColor c, Point coord) { Color rgbColor = c.ToRgb(); return(HsvColor.FromRgb(Color.FromScRgb(rgbColor.ScA, ColorUtilities.sRgbToScRgb((float)coord.X), ColorUtilities.sRgbToScRgb((float)coord.Y), rgbColor.ScB))); }
public override Point GetColorDetailsMarkerCoord(HsvColor c) { Color rgbColor = c.ToRgb(); return(new Point(ColorUtilities.ScRgbTosRgb(rgbColor.ScR), ColorUtilities.ScRgbTosRgb(rgbColor.ScG))); }
public abstract HsvColor GetColorByChannelLevelValue(HsvColor selectedColor, double value);
public abstract HsvColor GetColorByColorDetailsMarkerCoord(HsvColor selectedColor, Point coord);
private ESRI.ArcGIS.Display.IColor GetCombinedColor(IColor pColor1, IColor pColor2, EColorCombinationType eCombinationMethod, IColor pOriginColor) { // combines the input colors based on m_eColorCombinationMethod // (11/08/04) -- RGB and enuLabLChColorRamp aren't used by GUI IColor pOutColor = null; long MyOLE_COLOR = 0; // As OLE_COLOR in VB6 IRgbColor pMainRGBColor = null; IRgbColor pVariationRGBColor = null; IRgbColor pMergedRGBColor = null; bool bOK = false; IAlgorithmicColorRamp pAlgorithmicCR = null; // if either of the colors are null, then don't run the color through any algorithm, // instead, just return the other color. if both are null, then return a null color if (pColor1.NullColor) { pOutColor = pColor2; } else if (pColor2.NullColor) { pOutColor = pColor1; } else if (eCombinationMethod == EColorCombinationType.enuComponents) { // HSV components // create a new HSV color IHsvColor pHSVDrawColor = null; pHSVDrawColor = new HsvColor(); // get HSV values from Color1 and Color2 and assign to pHSVDrawColor IHsvColor pHSVColor1 = null; IHsvColor pHSVColor2 = null; // (new 4/27/04) didn't think I had to do this... //pHSVColor1 = pColor1 //pHSVColor2 = pColor2 pHSVColor1 = new HsvColor(); pHSVColor1.RGB = pColor1.RGB; pHSVColor2 = new HsvColor(); pHSVColor2.RGB = pColor2.RGB; pHSVDrawColor.Hue = pHSVColor1.Hue; pHSVDrawColor.Saturation = pHSVColor2.Saturation; pHSVDrawColor.Value = pHSVColor2.Value; pOutColor = pHSVDrawColor; } else if (eCombinationMethod == EColorCombinationType.enuRGBAverage) { // use additive color model to merge the two colors MyOLE_COLOR = pColor1.RGB; pMainRGBColor = new RgbColor(); pMainRGBColor.RGB = (int)MyOLE_COLOR; MyOLE_COLOR = pColor2.RGB; pVariationRGBColor = new RgbColor(); pVariationRGBColor.RGB = (int)MyOLE_COLOR; // merged color = RGB average of the two colors pMergedRGBColor = new RgbColor(); pMergedRGBColor.Red = (pMainRGBColor.Red + pVariationRGBColor.Red) / 2; pMergedRGBColor.Green = (pMainRGBColor.Green + pVariationRGBColor.Green) / 2; pMergedRGBColor.Blue = (pMainRGBColor.Blue + pVariationRGBColor.Blue) / 2; pOutColor = pMergedRGBColor; } else if ((eCombinationMethod == EColorCombinationType.enuCIELabColorRamp) | (eCombinationMethod == EColorCombinationType.enuLabLChColorRamp)) { // use color ramp and take central color between the two colors pAlgorithmicCR = new AlgorithmicColorRamp(); if (m_eColorCombinationMethod == EColorCombinationType.enuCIELabColorRamp) pAlgorithmicCR.Algorithm = esriColorRampAlgorithm.esriCIELabAlgorithm; else pAlgorithmicCR.Algorithm = esriColorRampAlgorithm.esriLabLChAlgorithm; pAlgorithmicCR.Size = 3; pAlgorithmicCR.FromColor = pColor1; pAlgorithmicCR.ToColor = pColor2; pAlgorithmicCR.CreateRamp(out bOK); pOutColor = pAlgorithmicCR.get_Color(1); // middle color in ramp } else // EColorCombinationType.enuCIELabMatrix { double[] iLab1 = new double[4]; // L, a, b values for Color1 double[] iLab2 = new double[4]; // L, a, b values for Color2 double[] iLabOrig = new double[4]; // L, a, b values for pOriginColor pColor1.GetCIELAB(out iLab1[0], out iLab1[1], out iLab1[2]); pColor2.GetCIELAB(out iLab2[0], out iLab2[1], out iLab2[2]); pOriginColor.GetCIELAB(out iLabOrig[0], out iLabOrig[1], out iLabOrig[2]); double[] iLabOut = new double[4]; // add color1 vector and color2 vector, then subtract the origin color vector iLabOut[0] = iLab1[0] + iLab2[0] - iLabOrig[0]; iLabOut[1] = iLab1[1] + iLab2[1] - iLabOrig[1]; iLabOut[2] = iLab1[2] + iLab2[2] - iLabOrig[2]; CorrectLabOutofRange(ref iLabOut[0], ref iLabOut[1], ref iLabOut[2]); IHsvColor pHSVColor = null; pHSVColor = new HsvColor(); pHSVColor.SetCIELAB(iLabOut[0], iLabOut[1], iLabOut[2]); pOutColor = pHSVColor; } return pOutColor; }
public override double GetChanellLevelValue(HsvColor hsvColor) { return(hsvColor.H / HsvColor.MaxHueValue); }
public abstract Brush GetChanellLevelBrush(HsvColor selectedColor);
public override Brush GetChanellLevelBrush(HsvColor hsvColor) { return(_chanellLevelBrush); }
private unsafe void DrawWheel(Surface sfc) { huerad = hsvcolor.Hue / radtodeg; for (int y = 0; y < sfc.Height; y++) { ColorBgra *ptr = sfc.GetRowAddress(y); float cy = radius - y; float cy2 = cy * cy; for (int x = 0; x < sfc.Width; x++) { float cx = x - radius; double r = Math.Sqrt(cy2 + cx * cx); double theta = Math.Atan2(cy, cx); double tx = r * Math.Cos(theta - huerad) + innerradius / 2; double tu = r * Math.Sin(theta - thirtydeg - huerad) + innerradius / 2; double tv = r * Math.Sin(theta + thirtydeg - huerad) + innerradius; if (theta < 0) { theta += 2 * Math.PI; } if (r <= radius && r >= innerradius) { //draw the hue ring *ptr = new HsvColor((int)(theta * radtodeg), 100, 100).ToColorBgra(); //antialias if (radius - r <= 1 && radius - r >= 0) { ptr->A = (byte)(255 * (radius - r)); } else if (r - innerradius <= 1 && r - innerradius >= 0) { ptr->A = (byte)(255 * (r - innerradius)); } } else if (hasmouse && (mouser <= radius || trackingSV || trackingH)) { if (tu >= 0 && tv <= theight && tx >= 0) { //draw the triangle float s, v; GetSatVal(cx, cy, out s, out v); int sat = (int)(100 * s); int val = (int)(100 * v); * ptr = new HsvColor(hsvcolor.Hue, sat, val).ToColorBgra(); //aa if (tx <= 1 && tx >= 0) { ptr->A = (byte)(255 * tx); } else if (tu <= 1 && tu >= 0) { ptr->A = (byte)(255 * tu); } else if (theight - tv <= 1 && theight - tv >= 0) { ptr->A = (byte)(255 * (theight - tv)); } } else if (r <= innerradius - padding && (tu <= -padding || tx <= -padding || tv >= theight + padding)) { //fills the free space between the ring and triangle with the selected color byte v = (byte)((((x ^ y) & 8) * 8) + 191); ptr->Bgra = (uint)v | (uint)(v << 8) | (uint)(v << 16) | 0xff000000; *ptr = UserBlendOps.NormalBlendOp.ApplyStatic(*ptr, color); //aa if (padding + tu >= -1 && padding + tu <= 0) { ptr->A = (byte)(255 * (-padding - tu)); } else if (padding + tx >= -1 && padding + tx <= 0) { ptr->A = (byte)(255 * (-padding - tx)); } else if (tv - theight - padding <= 1 && tv - theight - padding >= 0) { ptr->A = (byte)(255 * (tv - theight - padding)); } if (innerradius - padding - r <= 1 && innerradius - padding - r >= 0) { ptr->A = Math.Min(ptr->A, (byte)(255 * (innerradius - padding - r))); } } else { *ptr = ColorBgra.Transparent; } } else if (r <= innerradius - padding) { //fills the free space inside the ring with the selected color byte v = (byte)((((x ^ y) & 8) * 8) + 191); ptr->Bgra = (uint)v | (uint)(v << 8) | (uint)(v << 16) | 0xff000000; *ptr = UserBlendOps.NormalBlendOp.ApplyStatic(*ptr, color); //aa if (innerradius - padding - r <= 1 && innerradius - padding - r >= 0) { ptr->A = (byte)(255 * (innerradius - padding - r)); } } else { *ptr = ColorBgra.Transparent; } ++ptr; } } }
public override HsvColor GetColorByColorDetailsMarkerCoord(HsvColor hsvColor, Point coord) { return(new HsvColor(hsvColor.A, hsvColor.H, coord.X, coord.Y)); }
public static Color Darker(Color c) { var hsv = RgbToHsv(c); return(HsvToRgb(HsvColor.FromHsv(hsv.H, hsv.S, hsv.V * 0.8))); }
public override HsvColor GetColorByChannelLevelValue(HsvColor hsvColor, double value) { return(new HsvColor(hsvColor.A, value * HsvColor.MaxHueValue, hsvColor.S, hsvColor.V)); }
internal static Color ToXamlColor(this HsvColor hsvColor) { return(hsvColor.ToRgbColor().ToXamlColor()); }
public override Point GetColorDetailsMarkerCoord(HsvColor hsvColor) { return(new Point(hsvColor.H / HsvColor.MaxHueValue, hsvColor.S)); }
/// <summary> /// Generates a new background image for the color channel slider and applies it. /// </summary> private async void UpdateBackground(HsvColor color) { /* Updates may be requested when sliders are not in the visual tree. * For first-time load this is handled by the Loaded event. * However, after that problems may arise, consider the following case: * * (1) Backgrounds are drawn normally the first time on Loaded. * Actual height/width are available. * (2) The palette tab is selected which has no sliders * (3) The picker flyout is closed * (4) Externally the color is changed * The color change will trigger slider background updates but * with the flyout closed, actual height/width are zero. * No zero size bitmap can be generated. * (5) The picker flyout is re-opened by the user and the default * last-opened tab will be viewed: palette. * No loaded events will be fired for sliders. The color change * event was already handled in (4). The sliders will never * be updated. * * In this case the sliders become out of sync with the Color because there is no way * to tell when they actually come into view. To work around this, force a re-render of * the background with the last size of the slider. This last size will be when it was * last loaded or updated. * * In the future additional consideration may be required for SizeChanged of the control. * This work-around will also cause issues if display scaling changes in the special * case where cached sizes are required. */ var width = Convert.ToInt32(this.ActualWidth); var height = Convert.ToInt32(this.ActualHeight); if (width == 0 || height == 0) { // Attempt to use the last size if it was available if (this.cachedSize.IsEmpty == false) { width = Convert.ToInt32(this.cachedSize.Width); height = Convert.ToInt32(this.cachedSize.Height); } } else { this.cachedSize = new Size(width, height); } var bitmap = await ColorPickerRenderingHelpers.CreateChannelBitmapAsync( width, height, this.Orientation, this.ColorRepresentation, this.ColorChannel, color, this.CheckerBackgroundColor, this.IsAlphaMaxForced, this.IsSaturationValueMaxForced); if (bitmap != null) { this.Background = await ColorPickerRenderingHelpers.BitmapToBrushAsync(bitmap, width, height); } return; }
public override double GetChanellLevelValue(HsvColor hsvColor) { return(hsvColor.V); }
private ESRI.ArcGIS.Display.IColor GetCombinedColor(IColor pColor1, IColor pColor2, EColorCombinationType eCombinationMethod, IColor pOriginColor) { // combines the input colors based on m_eColorCombinationMethod // (11/08/04) -- RGB and enuLabLChColorRamp aren't used by GUI IColor pOutColor = null; long MyOLE_COLOR = 0; // As OLE_COLOR in VB6 IRgbColor pMainRGBColor = null; IRgbColor pVariationRGBColor = null; IRgbColor pMergedRGBColor = null; bool bOK = false; IAlgorithmicColorRamp pAlgorithmicCR = null; // if either of the colors are null, then don't run the color through any algorithm, // instead, just return the other color. if both are null, then return a null color if (pColor1.NullColor) { pOutColor = pColor2; } else if (pColor2.NullColor) { pOutColor = pColor1; } else if (eCombinationMethod == EColorCombinationType.enuComponents) { // HSV components // create a new HSV color IHsvColor pHSVDrawColor = null; pHSVDrawColor = new HsvColor(); // get HSV values from Color1 and Color2 and assign to pHSVDrawColor IHsvColor pHSVColor1 = null; IHsvColor pHSVColor2 = null; // (new 4/27/04) didn't think I had to do this... //pHSVColor1 = pColor1 //pHSVColor2 = pColor2 pHSVColor1 = new HsvColor(); pHSVColor1.RGB = pColor1.RGB; pHSVColor2 = new HsvColor(); pHSVColor2.RGB = pColor2.RGB; pHSVDrawColor.Hue = pHSVColor1.Hue; pHSVDrawColor.Saturation = pHSVColor2.Saturation; pHSVDrawColor.Value = pHSVColor2.Value; pOutColor = pHSVDrawColor; } else if (eCombinationMethod == EColorCombinationType.enuRGBAverage) { // use additive color model to merge the two colors MyOLE_COLOR = pColor1.RGB; pMainRGBColor = new RgbColor(); pMainRGBColor.RGB = (int)MyOLE_COLOR; MyOLE_COLOR = pColor2.RGB; pVariationRGBColor = new RgbColor(); pVariationRGBColor.RGB = (int)MyOLE_COLOR; // merged color = RGB average of the two colors pMergedRGBColor = new RgbColor(); pMergedRGBColor.Red = (pMainRGBColor.Red + pVariationRGBColor.Red) / 2; pMergedRGBColor.Green = (pMainRGBColor.Green + pVariationRGBColor.Green) / 2; pMergedRGBColor.Blue = (pMainRGBColor.Blue + pVariationRGBColor.Blue) / 2; pOutColor = pMergedRGBColor; } else if ((eCombinationMethod == EColorCombinationType.enuCIELabColorRamp) | (eCombinationMethod == EColorCombinationType.enuLabLChColorRamp)) { // use color ramp and take central color between the two colors pAlgorithmicCR = new AlgorithmicColorRamp(); if (m_eColorCombinationMethod == EColorCombinationType.enuCIELabColorRamp) pAlgorithmicCR.Algorithm = esriColorRampAlgorithm.esriCIELabAlgorithm; else pAlgorithmicCR.Algorithm = esriColorRampAlgorithm.esriLabLChAlgorithm; pAlgorithmicCR.Size = 3; pAlgorithmicCR.FromColor = pColor1; pAlgorithmicCR.ToColor = pColor2; pAlgorithmicCR.CreateRamp(out bOK); pOutColor = pAlgorithmicCR.get_Color(1); // middle color in ramp } else // EColorCombinationType.enuCIELabMatrix { double[] iLab1 = new double[4]; // L, a, b values for Color1 double[] iLab2 = new double[4]; // L, a, b values for Color2 double[] iLabOrig = new double[4]; // L, a, b values for pOriginColor pColor1.GetCIELAB(out iLab1[0], out iLab1[1], out iLab1[2]); pColor2.GetCIELAB(out iLab2[0], out iLab2[1], out iLab2[2]); pOriginColor.GetCIELAB(out iLabOrig[0], out iLabOrig[1], out iLabOrig[2]); double[] iLabOut = new double[4]; // add color1 vector and color2 vector, then subtract the origin color vector iLabOut[0] = iLab1[0] + iLab2[0] - iLabOrig[0]; iLabOut[1] = iLab1[1] + iLab2[1] - iLabOrig[1]; iLabOut[2] = iLab1[2] + iLab2[2] - iLabOrig[2]; CorrectLabOutofRange(ref iLabOut[0], ref iLabOut[1], ref iLabOut[2]); IHsvColor pHSVColor = null; pHSVColor = new HsvColor(); pHSVColor.SetCIELAB(iLabOut[0], iLabOut[1], iLabOut[2]); pOutColor = pHSVColor; } return pOutColor; }
public override HsvColor GetColorByColorDetailsMarkerCoord(HsvColor hsvColor, Point coord) { return(new HsvColor(hsvColor.A, coord.X * HsvColor.MaxHueValue, coord.Y, hsvColor.V)); }
public static Color ToRgba(HsvColor c) { return(ToRgba(c.h, c.s, c.v, c.a)); }
public override HsvColor GetColorByChannelLevelValue(HsvColor hsvColor, double value) { return(new HsvColor(hsvColor.A, hsvColor.H, hsvColor.S, value)); }
public static Color ConvertHsvToRgb(HsvColor color) { return ConvertHsvToRgb(color.H, color.S, color.V); }
/// <summary> /// Generates a new bitmap of the specified size by changing a specific color channel. /// This will produce a gradient representing all possible differences of that color channel. /// </summary> /// <param name="width">The pixel width (X, horizontal) of the resulting bitmap.</param> /// <param name="height">The pixel height (Y, vertical) of the resulting bitmap.</param> /// <param name="orientation">The orientation of the resulting bitmap (gradient direction).</param> /// <param name="colorRepresentation">The color representation being used: RGBA or HSVA.</param> /// <param name="channel">The specific color channel to vary.</param> /// <param name="baseHsvColor">The base HSV color used for channels not being changed.</param> /// <param name="checkerColor">The color of the checker background square.</param> /// <param name="isAlphaMaxForced">Fix the alpha channel value to maximum during calculation. /// This will remove any alpha/transparency from the other channel backgrounds.</param> /// <param name="isSaturationValueMaxForced">Fix the saturation and value channels to maximum /// during calculation in HSVA color representation. /// This will ensure colors are always discernible regardless of saturation/value.</param> /// <returns>A new bitmap representing a gradient of color channel values.</returns> public static async Task <byte[]> CreateChannelBitmapAsync( int width, int height, Orientation orientation, ColorRepresentation colorRepresentation, ColorChannel channel, HsvColor baseHsvColor, Color?checkerColor, bool isAlphaMaxForced, bool isSaturationValueMaxForced) { if (width == 0 || height == 0) { return(null); } var bitmap = await Task.Run <byte[]>(async() => { int pixelDataIndex = 0; double channelStep; byte[] bgraPixelData; byte[] bgraCheckeredPixelData = null; Color baseRgbColor = Colors.White; Color rgbColor; int bgraPixelDataHeight; int bgraPixelDataWidth; // Allocate the buffer // BGRA formatted color channels 1 byte each (4 bytes in a pixel) bgraPixelData = new byte[width * height * 4]; bgraPixelDataHeight = height * 4; bgraPixelDataWidth = width * 4; // Maximize alpha channel value if (isAlphaMaxForced && channel != ColorChannel.Alpha) { baseHsvColor = new HsvColor() { H = baseHsvColor.H, S = baseHsvColor.S, V = baseHsvColor.V, A = 1.0 }; } // Convert HSV to RGB once if (colorRepresentation == ColorRepresentation.Rgba) { baseRgbColor = Uwp.Helpers.ColorHelper.FromHsv( baseHsvColor.H, baseHsvColor.S, baseHsvColor.V, baseHsvColor.A); } // Maximize Saturation and Value channels when in HSVA mode if (isSaturationValueMaxForced && colorRepresentation == ColorRepresentation.Hsva && channel != ColorChannel.Alpha) { switch (channel) { case ColorChannel.Channel1: baseHsvColor = new HsvColor() { H = baseHsvColor.H, S = 1.0, V = 1.0, A = baseHsvColor.A }; break; case ColorChannel.Channel2: baseHsvColor = new HsvColor() { H = baseHsvColor.H, S = baseHsvColor.S, V = 1.0, A = baseHsvColor.A }; break; case ColorChannel.Channel3: baseHsvColor = new HsvColor() { H = baseHsvColor.H, S = 1.0, V = baseHsvColor.V, A = baseHsvColor.A }; break; } } // Create a checkered background if (checkerColor != null) { bgraCheckeredPixelData = await CreateCheckeredBitmapAsync( width, height, checkerColor.Value); } // Create the color channel gradient if (orientation == Orientation.Horizontal) { // Determine the numerical increment of the color steps within the channel if (colorRepresentation == ColorRepresentation.Hsva) { if (channel == ColorChannel.Channel1) { channelStep = 360.0 / width; } else { channelStep = 1.0 / width; } } else { channelStep = 255.0 / width; } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (y == 0) { rgbColor = GetColor(x *channelStep); // Get a new color bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 3] = rgbColor.A; } else { // Use the color in the row above // Remember the pixel data is 1 dimensional instead of 2 bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex + 0 - bgraPixelDataWidth]; bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex + 1 - bgraPixelDataWidth]; bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex + 2 - bgraPixelDataWidth]; bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex + 3 - bgraPixelDataWidth]; } pixelDataIndex += 4; } } } else { // Determine the numerical increment of the color steps within the channel if (colorRepresentation == ColorRepresentation.Hsva) { if (channel == ColorChannel.Channel1) { channelStep = 360.0 / height; } else { channelStep = 1.0 / height; } } else { channelStep = 255.0 / height; } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (x == 0) { // The lowest channel value should be at the 'bottom' of the bitmap rgbColor = GetColor((height - 1 - y) * channelStep); // Get a new color bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R *rgbColor.A / 255); bgraPixelData[pixelDataIndex + 3] = rgbColor.A; } else { // Use the color in the column to the left // Remember the pixel data is 1 dimensional instead of 2 bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex - 4]; bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex - 3]; bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex - 2]; bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex - 1]; } pixelDataIndex += 4; } } } // Composite the checkered background with color channel gradient for final result // The height/width are not checked as both bitmaps were built with the same values if ((checkerColor != null) && (bgraCheckeredPixelData != null)) { pixelDataIndex = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { /* The following algorithm is used to blend the two bitmaps creating the final composite. * In this formula, pixel data is normalized 0..1, actual pixel data is in the range 0..255. * The color channel gradient should apply OVER the checkered background. * * R = R0 * A0 * (1 - A1) + R1 * A1 = RA0 * (1 - A1) + RA1 * G = G0 * A0 * (1 - A1) + G1 * A1 = GA0 * (1 - A1) + GA1 * B = B0 * A0 * (1 - A1) + B1 * A1 = BA0 * (1 - A1) + BA1 * A = A0 * (1 - A1) + A1 = A0 * (1 - A1) + A1 * * Considering only the red channel, some algebraic transformation is applied to * make the math quicker to solve. * * => ((RA0 / 255.0) * (1.0 - A1 / 255.0) + (RA1 / 255.0)) * 255.0 * => ((RA0 * 255) - (RA0 * A1) + (RA1 * 255)) / 255 */ // Bottom layer byte rXa0 = bgraCheckeredPixelData[pixelDataIndex + 2]; byte gXa0 = bgraCheckeredPixelData[pixelDataIndex + 1]; byte bXa0 = bgraCheckeredPixelData[pixelDataIndex + 0]; byte a0 = bgraCheckeredPixelData[pixelDataIndex + 3]; // Top layer byte rXa1 = bgraPixelData[pixelDataIndex + 2]; byte gXa1 = bgraPixelData[pixelDataIndex + 1]; byte bXa1 = bgraPixelData[pixelDataIndex + 0]; byte a1 = bgraPixelData[pixelDataIndex + 3]; bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(((bXa0 * 255) - (bXa0 * a1) + (bXa1 * 255)) / 255); bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(((gXa0 * 255) - (gXa0 * a1) + (gXa1 * 255)) / 255); bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(((rXa0 * 255) - (rXa0 * a1) + (rXa1 * 255)) / 255); bgraPixelData[pixelDataIndex + 3] = Convert.ToByte(((a0 * 255) - (a0 * a1) + (a1 * 255)) / 255); pixelDataIndex += 4; } } } Color GetColor(double channelValue) { Color newRgbColor = Colors.White; switch (channel) { case ColorChannel.Channel1: { if (colorRepresentation == ColorRepresentation.Hsva) { // Sweep hue newRgbColor = Uwp.Helpers.ColorHelper.FromHsv( Math.Clamp(channelValue, 0.0, 360.0), baseHsvColor.S, baseHsvColor.V, baseHsvColor.A); } else { // Sweep red newRgbColor = new Color { R = Convert.ToByte(Math.Clamp(channelValue, 0.0, 255.0)), G = baseRgbColor.G, B = baseRgbColor.B, A = baseRgbColor.A }; } break; } case ColorChannel.Channel2: { if (colorRepresentation == ColorRepresentation.Hsva) { // Sweep saturation newRgbColor = Uwp.Helpers.ColorHelper.FromHsv( baseHsvColor.H, Math.Clamp(channelValue, 0.0, 1.0), baseHsvColor.V, baseHsvColor.A); } else { // Sweep green newRgbColor = new Color { R = baseRgbColor.R, G = Convert.ToByte(Math.Clamp(channelValue, 0.0, 255.0)), B = baseRgbColor.B, A = baseRgbColor.A }; } break; } case ColorChannel.Channel3: { if (colorRepresentation == ColorRepresentation.Hsva) { // Sweep value newRgbColor = Uwp.Helpers.ColorHelper.FromHsv( baseHsvColor.H, baseHsvColor.S, Math.Clamp(channelValue, 0.0, 1.0), baseHsvColor.A); } else { // Sweep blue newRgbColor = new Color { R = baseRgbColor.R, G = baseRgbColor.G, B = Convert.ToByte(Math.Clamp(channelValue, 0.0, 255.0)), A = baseRgbColor.A }; } break; } case ColorChannel.Alpha: { if (colorRepresentation == ColorRepresentation.Hsva) { // Sweep alpha newRgbColor = Uwp.Helpers.ColorHelper.FromHsv( baseHsvColor.H, baseHsvColor.S, baseHsvColor.V, Math.Clamp(channelValue, 0.0, 1.0)); } else { // Sweep alpha newRgbColor = new Color { R = baseRgbColor.R, G = baseRgbColor.G, B = baseRgbColor.B, A = Convert.ToByte(Math.Clamp(channelValue, 0.0, 255.0)) }; } break; } } return(newRgbColor); } return(bgraPixelData); }); return(bitmap); }
private void CalculateColor( Point p ) { HsvColor hsv = new HsvColor( 360 - _spectrumSlider.Value, 1, 1 ) { S = p.X, V = 1 - p.Y }; var currentColor = ColorUtilities.ConvertHsvToRgb( hsv.H, hsv.S, hsv.V ); currentColor.A = A; SelectedColor = currentColor; SetHexadecimalStringProperty( GetFormatedColorString( SelectedColor ), false ); }
public abstract double GetChanellLevelValue(HsvColor selectedColor);