internal static Color InvertLightness(Color color) { // We unfortunately still need janky tuning of lightness and desaturation for good visibility, but // Oklab does give us a beautiful perceptual lightness scale (dark blue goes to light blue!) unlike // HSL, so awesome! Lab lab = ColorToOklab(color); #region Invert and global lightness boost float newL = lab.L < 0.5f ? ((1.0f - lab.L) + 0.10f).Clamp(0.0f, 1.0f) : (lab.L + 0.025f).Clamp(0.5f, 1.0f); lab = new Lab(newL, lab.a, lab.b); #endregion LCh lch = OklabToLCh(lab); #region Tweaks for specific hue ranges bool redDesaturated = false; switch (lch.h) { // Blue range case >= -1.901099f and <= -1.448842f: // Green range case >= 2.382358f and <= 2.880215f: lch = new LCh(lch.L.Clamp(0.7f, 1.0f), lch.C, lch.h); break; // Red range case >= -0.1025453f and <= 0.8673203f: lch = new LCh(lch.L.Clamp(0.6f, 1.0f), lch.C, lch.h); if (lch.L is >= 0.5f and <= 0.7f) { lch = new LCh(lch.L, (lch.C - 0.025f).Clamp(0, 1.0f), lch.h); redDesaturated = true; } break; } #endregion // Slight global desaturation lab = LChToOklab(new LCh(lch.L, (lch.C - (redDesaturated ? 0.015f : 0.04f)).Clamp(0, 1.0f), lch.h)); Color retColor = OklabToColor(lab); // For some reason RTF doesn't accept a \cfN if the color is 255 all around, it has to be 254 or // less... don't ask me if (retColor.R == 255 && retColor.G == 255 && retColor.B == 255) { retColor = Color.FromArgb(254, 254, 254); } return(retColor); }
// Converts Lab to LCh (lightness, chroma, hue) private static Lab LChToOklab(LCh lch) { float a = (float)(lch.C * Math.Cos(lch.h)); float b = (float)(lch.C * Math.Sin(lch.h)); return(new Lab(lch.L, a, b)); }
public Checador() { InitializeComponent(); _lCh = new LCh(); _eCh = new ECh(); _isEnablebinding = true; BindingCheck(); }
public Checador(ECh check) { _lCh = new LCh(); _eCh = new ECh(); _eCh = check; BindingCheckReload(); _isEnablebinding = true; BindingCheck(); }
private static LCh LabToLCh(Lab lab) { LCh lch = new LCh(); lch.l = lab.L; lch.c = Mathf.Sqrt(lab.A * lab.A + lab.B * lab.B); float h = Mathf.Rad2Deg * Mathf.Atan2(lab.B, lab.A); if (h < 0) { h += 360f; } else if (h >= 360) { h -= 360f; } lch.h = h; lch.alpha = lab.Alpha; return(lch); }
/// <summary> /// Tips: 0.5f (really high contrast shapes, maybe 10+ bit monitors), 1f (high-quality monitors, indoors) 2.0f (else). /// </summary> public static float DeltaEmpfindung00(Lab lab1, Lab lab2, DeltaEWeightType weight = DeltaEWeightType.Graphic) { // Lab to LCh LCh lch1 = LabToLCh(lab1); LCh lch2 = LabToLCh(lab2); // Weight Factors DeltaEWeight weightFactors = Convert.ToBoolean((int)weight) ? weightGraphic : weightTextile; float CX = (lch1.c + lch2.c) / 2f; float GX = 0.5f * (1f - Mathf.Sqrt(Mathf.Pow(CX, 7f) / (Mathf.Pow(CX, 7f) + Mathf.Pow(25f, 7f)))); float NN = (1f + GX) * lab1.A; lch1.c = Mathf.Sqrt(NN * NN + lab1.B * lab1.B); float hue1 = LabToHue(NN, lab1.B); NN = (1f + GX) * lab2.A; lch2.c = Mathf.Sqrt(NN * NN + lab2.B * lab2.B); float hue2 = LabToHue(NN, lab2.B); float deltaLightness = lch2.l - lch1.l; float deltaChroma = lch2.c - lch1.c; float deltaHue; if ((lch1.c * lch2.c) == 0f) { deltaHue = 0f; } else { NN = Mathf.Round((hue2 - hue1) * 1000f) / 1000f; if (Mathf.Abs(NN) <= 180f) { deltaHue = hue2 - hue1; } else { if (NN > 180f) { deltaHue = hue2 - hue1 - 360f; } else { deltaHue = hue2 - hue1 + 360f; } } } deltaHue = 2f * Mathf.Sqrt(lch1.c * lch2.c) * Mathf.Sin((deltaHue / 2f) * Mathf.Deg2Rad); float lightnessAverage = (lch1.l + lch2.l) / 2f; float chromaAverage = (lch1.c + lch2.c) / 2f; float hueNeutralsCompensation; if ((lch1.c * lch2.c) == 0f) { hueNeutralsCompensation = hue1 + hue2; } else { NN = Mathf.Abs(Mathf.Round((hue1 - hue2) * 1000f) / 1000f); if (NN > 180f) { if ((hue2 + hue1) < 360f) { hueNeutralsCompensation = hue1 + hue2 + 360f; } else { hueNeutralsCompensation = hue1 + hue2 - 360f; } } else { hueNeutralsCompensation = hue1 + hue2; } hueNeutralsCompensation /= 2f; } float T = 1f - 0.17f * Mathf.Cos((hueNeutralsCompensation - 30f) * Mathf.Deg2Rad) + 0.24f * Mathf.Cos((2f * hueNeutralsCompensation) * Mathf.Deg2Rad) + 0.32f * Mathf.Cos((3f * hueNeutralsCompensation + 6f) * Mathf.Deg2Rad) - 0.20f * Mathf.Cos((4f * hueNeutralsCompensation - 63f) * Mathf.Deg2Rad); // Compensations float lightnessCompensation = 1f + ((0.015f * ((lightnessAverage - 50f) * (lightnessAverage - 50f))) / Mathf.Sqrt(20f + ((lightnessAverage - 50f) * (lightnessAverage - 50f)))); float chromaCompensation = 1f + weightFactors.kC * chromaAverage; float hueCompensation = 1f + weightFactors.kH * chromaAverage * T; // Rotation float PH = 30f * Mathf.Exp(-((hueNeutralsCompensation - 275f) / 25f) * ((hueNeutralsCompensation - 275f) / 25f)); float RC = 2f * Mathf.Sqrt(Mathf.Pow(chromaAverage, 7f) / (Mathf.Pow(chromaAverage, 7f) + Mathf.Pow(25f, 7f))); float rotationTerm = -Mathf.Sin((2f * PH) * Mathf.Deg2Rad) * RC; // Weights Constants float weightFactorChroma = 1f; float weightFactorHue = 1f; // Corrections deltaLightness = deltaLightness / (weightFactors.kL * lightnessCompensation); deltaChroma = deltaChroma / (weightFactorChroma * chromaCompensation); deltaHue = deltaHue / (weightFactorHue * hueCompensation); float distance = Mathf.Sqrt(Mathf.Pow(deltaLightness, 2f) + Mathf.Pow(deltaChroma, 2f) + Mathf.Pow(deltaHue, 2f) + rotationTerm * deltaChroma * deltaHue); return(distance); }