using ColorMine.ColorSpaces; using Colourful; using Colourful.Conversion; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media; using Tango.Core.Commands; using Tango.SharedUI; namespace Tango.PPC.Jobs.Models { public class VisualOffsetModel : ViewModel { public enum TypeColorCorrection { Lightness = 1, Chroma = 2, Hue = 3, } public class VisualLCHOffset : ViewModel { public TypeColorCorrection ColorType { get; set; } public int Offset { get; set; } public double L { get; set; } public double C { get; set; } private double _h; public double H { get { return _h; } set { _h = value; RaisePropertyChangedAuto(); } } public SolidColorBrush ColorBrush { get { Lch lch = new Lch() { L = L, C = C, H = H }; IRgb RGB = lch.ToRgb(); return new SolidColorBrush() { Color = Color.FromRgb(System.Convert.ToByte(RGB.R), System.Convert.ToByte(RGB.G), System.Convert.ToByte(RGB.B)) }; } } private bool _isVisible; public bool IsVisible { get { return _isVisible; } set { _isVisible = value; RaisePropertyChangedAuto(); } } public VisualLCHOffset(TypeColorCorrection colorType, int offset) { ColorType = colorType; Offset = offset; } public void Init(Lch lch) { L = lch.L; C = lch.C; H = lch.H; RaisePropertyChanged( nameof(ColorBrush)); } } #region Property private double _l; public double L { get { return _l; } set { if (_l != value) { _l = value; RaisePropertyChangedAuto(); } } } private double _a; public double A { get { return _a; } set { if (_a != value) { _a = value; RaisePropertyChangedAuto(); } } } private double _b; public double B { get { return _b; } set { if (_b != value) { _b = value; RaisePropertyChangedAuto(); } } } public double C { get; set; } public double H { get; set; } private double _sourcel; public double SourceL { get { return _sourcel; } set { if(_sourcel != value) { _sourcel = value; RaisePropertyChangedAuto(); //ManualCorrection(LightnessOffset, ChromaOffset, HueOffset); } } } private double _sourcea; public double SourceA { get { return _sourcea; } set { if (_sourcea != value) { _sourcea = value; RaisePropertyChangedAuto(); //ManualCorrection(LightnessOffset, ChromaOffset, HueOffset); } } } private double _sourceb; public double SourceB { get { return _sourceb; } set { if (_sourceb != value) { _sourceb = value; RaisePropertyChangedAuto(); //ManualCorrection(LightnessOffset, ChromaOffset, HueOffset); } } } private double _sourceh; public double SourceH { get { return _sourceh; } set { if (_sourceh != value) { _sourceh = value; RaisePropertyChangedAuto(); } } } private double _sourcec; public double SourceC { get { return _sourcec; } set { if (_sourcec != value) { _sourcec = value; RaisePropertyChangedAuto(); } } } public double LightnessOffset { get; set; } public double ChromaOffset { get; set; } public double HueOffset { get; set; } public VisualLCHOffset MinLightness { get; set; } public VisualLCHOffset MaxLightness { get; set; } public VisualLCHOffset MinChroma { get; set; } public VisualLCHOffset MaxChroma { get; set; } public VisualLCHOffset MinHue { get; set; } public VisualLCHOffset MaxHue { get; set; } public SolidColorBrush SourceColorBrush { get { //Lch lch = new Lch() { L = L, C = C, H = H }; //IRgb RGB = lch.ToRgb(); Lab lab = new Lab(SourceL, SourceA, SourceB); Rgb rgb = new Rgb(lab.ToRgb()); return new SolidColorBrush() { Color = Color.FromRgb((byte)rgb.R, (byte)rgb.G, (byte)rgb.B) }; } } public SolidColorBrush ManualColorBrush { get { //Lch lch = new Lch() { L = L, C = C, H = H }; //IRgb rgb = lch.ToRgb(); if(LightnessOffset == 0 && ChromaOffset == 0 && HueOffset == 0) { return SourceColorBrush; } Lab lab = new Lab(L, A, B); Rgb rgb = new Rgb(lab.ToRgb()); return new SolidColorBrush() { Color = Color.FromRgb((byte)rgb.R, (byte)rgb.G, (byte)rgb.B) }; } } private bool _isOutOfGamut; /// /// Gets or sets a value indicating whether this instance is out of gamut. /// public bool IsOutOfGamut { get { return _isOutOfGamut; } set { if (_isOutOfGamut != value) { _isOutOfGamut = value; RaisePropertyChangedAuto(); } } } #endregion public VisualOffsetModel() { MinLightness = new VisualLCHOffset(TypeColorCorrection.Lightness, -1); MaxLightness = new VisualLCHOffset(TypeColorCorrection.Lightness, 1); MinChroma = new VisualLCHOffset(TypeColorCorrection.Chroma, -1); MaxChroma = new VisualLCHOffset(TypeColorCorrection.Chroma, 1); MinHue = new VisualLCHOffset(TypeColorCorrection.Hue, -1); MaxHue = new VisualLCHOffset(TypeColorCorrection.Hue, 1); IsOutOfGamut = false; } public void InitLAB(double l, double a, double b, double c, double h) { L = l; A = a; B = b; C = c; H = h; LightnessOffset = ChromaOffset = HueOffset = 0; _sourcel = l; RaisePropertyChanged(nameof(SourceL)); _sourcea = a; RaisePropertyChanged(nameof(SourceA)); _sourceb = b; RaisePropertyChanged(nameof(SourceB)); _sourcec = c; RaisePropertyChanged(nameof(SourceC)); _sourceh = h; RaisePropertyChanged(nameof(SourceH)); UpdateManualColors(); } public void ClearAll(double l, double a, double b) { LightnessOffset = 0; ChromaOffset = 0; HueOffset = 0; LabColor labColor = new LabColor(l, a, b); var converter = new ColourfulConverter { WhitePoint = Illuminants.D65 }; var last_LCH = converter.ToLChab(labColor); SourceL = l; SourceA = a; SourceB = b; SourceC = last_LCH.C; SourceH = last_LCH.h; L = l; A = a; B = b; C = last_LCH.C; H = last_LCH.h; IsOutOfGamut = false; } public void UpdateSourceLAB(double l, double a, double b) { LabColor labColor = new LabColor(l, a, b); var converter = new ColourfulConverter { WhitePoint = Illuminants.D65 }; var last_LCH = converter.ToLChab(labColor); SourceL = l; SourceA = a; SourceB = b; SourceC = last_LCH.C; SourceH = last_LCH.h; } public void UpdateLAB(double l, double a, double b) { LabColor labColor = new LabColor(l, a, b); var converter = new ColourfulConverter { WhitePoint = Illuminants.D65 }; var last_LCH = converter.ToLChab(labColor); L = l; A = a; B = b; C = last_LCH.C; H = last_LCH.h; } public void UpdateManualColors() { Lch lch = Correction(SourceL, SourceC, SourceH,-6, 0, 0); MinLightness.Init(lch); lch = Correction(SourceL, SourceC, SourceH, 6, 0, 0); MaxLightness.Init(lch); lch = Correction(SourceL, SourceC, SourceH, 0, -6, 0); MinChroma.Init(lch); lch = Correction(SourceL, SourceC, SourceH, 0, 6, 0); MaxChroma.Init(lch); lch = Correction(SourceL, SourceC, SourceH, 0, 0, -6); MinHue.Init(lch); lch = Correction(SourceL, SourceC, SourceH, 0, 0, 6); MaxHue.Init(lch); RaisePropertyChanged(nameof(SourceColorBrush)); RaisePropertyChanged(nameof(ManualColorBrush)); } /// /// Manuals the correction. During change lightnessOffset, chromaOffset or hueOffset in app caused to change LAB of visual correction. /// /// The lightness offset. /// The chroma offset. /// The hue offset. public void ManualCorrection( double lightnessOffset, double chromaOffset, double hueOffset) { Lch lch = Correction(SourceL, SourceC, SourceH, lightnessOffset, chromaOffset, hueOffset); L = lch.L; A = lch.C * Math.Cos(lch.H * (Math.PI / 180)); B = lch.C * Math.Sin(lch.H * (Math.PI / 180)); RaisePropertyChanged(nameof(SourceColorBrush)); RaisePropertyChanged(nameof(ManualColorBrush)); } /// /// Calculate Lch on the specified lightness offset. /// /// The lightness offset. /// The chroma offset. /// The hue offset. /// public Lch Correction(double lch_l, double lch_c, double lch_h, double lightnessOffset, double chromaOffset, double hueOffset) { double L1 = lch_l; double C1 = lch_c; double H1 = lch_h; if (H1 < 0) H1 += 360; double refX_C = C1; ///lightness double SL = 0; if (L1 < 16) SL = 0.511; else SL = L1 * 0.040975 / (1 + 0.01765 * L1); L1 += lightnessOffset * 2 * SL; if (L1 > 100) L1 = 100; if (L1 < 0) L1 = 0; double SC = (0.638 + 0.0638 * refX_C / (1 + 0.0131 * refX_C)); C1 = C1 + (chromaOffset * SC); // if (C1 > 128) // C1 = 128; if (C1 < 0) C1 = 0; //double h1 = H1; //if (h1 < 0) // h1 = h1 + 360; double refX_CQ = Math.Pow(refX_C, 4); double refX_H = H1; double refX_F = Math.Sqrt(refX_CQ / (refX_CQ + 1900)); double refX_T = 0; if ((refX_H > 164) & (refX_H < 345)) refX_T = 0.56 + Math.Abs(0.2 * Math.Cos(Math.PI * (refX_H + 168) / 180)); else if ((refX_H >= 345) | (refX_H <= 164)) refX_T = 0.36 + Math.Abs(0.4 * Math.Cos(Math.PI * (refX_H + 35) / 180)); //double refX_SC = (0.638 + 0.0638 * refX_C / (1 + 0.0131 * refX_C)); double refX_SH = SC * (refX_T * refX_F + 1 - refX_F); if (C1 != 0) { var argum = 1 - (refX_SH * refX_SH * hueOffset * hueOffset / (2 * C1 * C1)); if (argum < -1) argum = -1; if (argum > 1) argum = 1; double tmp = (180 / Math.PI) * Math.Acos(argum); //degrees if (hueOffset < 0)//? tmp *= -1; H1 += tmp; } Lch lch = new Lch() { L = L1, C = C1, H = H1 }; return lch; } #region Converter #endregion } }