From f70cfcb8ac25024b34562dfeedf2ed8c2f4eafeb Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Tue, 15 Apr 2025 14:32:18 +0800 Subject: [PATCH 1/6] add framework 9.0 --- .github/workflows/package.yml | 4 ++++ .github/workflows/pr-checks.yml | 4 ++++ .github/workflows/release.yml | 4 ++++ README.md | 4 ++++ benchmark/CssInCSharp.Benchmarks/Benchmark.cs | 1 + .../CssInCSharp.Benchmarks/CssInCSharp.Benchmarks.csproj | 2 +- src/CssInCSharp.csproj | 6 +++++- 7 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 7f91f87..2b4d353 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -37,6 +37,10 @@ jobs: with: dotnet-version: 8.0.100 + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: 9.0.200 + - name: Package Nightly Nuget 📦 run: | SUFFIX=`date "+%y%m%d%H%M%S"` diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 29c5c04..6a43487 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -29,6 +29,10 @@ jobs: with: dotnet-version: 8.0.100 + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: 9.0.200 + - name: Check Building ⚙ run: | dotnet build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 831ad1f..3ad7c5c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,10 @@ jobs: with: dotnet-version: 8.0.100 + - uses: actions/setup-dotnet@v1 + with: + dotnet-version: 9.0.200 + - name: Package and publish to Nuget📦 run: | VERSION=`git describe --tags` diff --git a/README.md b/README.md index f7852b1..42a7210 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,11 @@ AMD Ryzen 7 5700G with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores ## Special Thanks [cssinjs](https://github.com/ant-design/cssinjs): Component level cssinjs solution used in ant.design. + [stylis](https://github.com/thysultan/stylis): A Light–weight CSS Preprocessor. + [csstype](https://github.com/frenic/csstype): TypeScript and Flow definitions for CSS. + [tinycolor](https://github.com/scttcper/tinycolor): A small library for color manipulation and conversion. + [TypeScriptAST](https://github.com/ToCSharp/TypeScriptAST): .NET port of TypeScript parser. \ No newline at end of file diff --git a/benchmark/CssInCSharp.Benchmarks/Benchmark.cs b/benchmark/CssInCSharp.Benchmarks/Benchmark.cs index 9ac7fe8..8085f69 100644 --- a/benchmark/CssInCSharp.Benchmarks/Benchmark.cs +++ b/benchmark/CssInCSharp.Benchmarks/Benchmark.cs @@ -10,6 +10,7 @@ namespace CssInCSharp.Benchmarks [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net70)] [SimpleJob(RuntimeMoniker.Net80)] + [SimpleJob(RuntimeMoniker.Net90)] [Config(typeof(Config))] public class Benchmark { diff --git a/benchmark/CssInCSharp.Benchmarks/CssInCSharp.Benchmarks.csproj b/benchmark/CssInCSharp.Benchmarks/CssInCSharp.Benchmarks.csproj index 2961270..49169b0 100644 --- a/benchmark/CssInCSharp.Benchmarks/CssInCSharp.Benchmarks.csproj +++ b/benchmark/CssInCSharp.Benchmarks/CssInCSharp.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net5;net6;net7;net8 + net5;net6;net7;net8;net9.0 enable enable latest diff --git a/src/CssInCSharp.csproj b/src/CssInCSharp.csproj index 72cfa36..5467348 100644 --- a/src/CssInCSharp.csproj +++ b/src/CssInCSharp.csproj @@ -1,7 +1,7 @@  - netstandard2.1;net5;net6;net7;net8 + netstandard2.1;net5;net6;net7;net8;net9.0 latest Library true @@ -56,6 +56,10 @@ + + + + From 9ba38c5a5fd5581ba40d5782e387768eb323f5c1 Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Tue, 15 Apr 2025 14:34:59 +0800 Subject: [PATCH 2/6] update benchmark result --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 42a7210..757ccc7 100644 --- a/README.md +++ b/README.md @@ -65,23 +65,26 @@ The CssInCSharp is similar to less or sass. You can simply convert you style fil ## Benchmark ``` -BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3593/23H2/2023Update/SunValley3) +BenchmarkDotNet v0.13.12, Windows 11 (10.0.26100.3775) AMD Ryzen 7 5700G with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores -.NET SDK 8.0.300 - [Host] : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 +.NET SDK 9.0.200 + [Host] : .NET 9.0.2 (9.0.225.6610), X64 RyuJIT AVX2 .NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2 .NET 6.0 : .NET 6.0.28 (6.0.2824.12007), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.17 (7.0.1724.11508), X64 RyuJIT AVX2 - .NET 8.0 : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 + .NET 8.0 : .NET 8.0.13 (8.0.1325.6609), X64 RyuJIT AVX2 + .NET 9.0 : .NET 9.0.2 (9.0.225.6610), X64 RyuJIT AVX2 ``` -| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | -|---------- |--------- |--------- |---------:|---------:|---------:|------:|--------:|--------:|-------:|----------:|------------:| -| CreateCss | .NET 5.0 | .NET 5.0 | 58.99 μs | 1.179 μs | 2.001 μs | 1.00 | 0.00 | 18.4326 | 0.9766 | 150.64 KB | 1.00 | -| CreateCss | .NET 6.0 | .NET 6.0 | 55.16 μs | 1.076 μs | 1.797 μs | 0.94 | 0.04 | 17.6392 | 1.0376 | 144.36 KB | 0.96 | -| CreateCss | .NET 7.0 | .NET 7.0 | 50.61 μs | 0.999 μs | 2.062 μs | 0.87 | 0.05 | 17.6392 | 1.0376 | 144.42 KB | 0.96 | -| CreateCss | .NET 8.0 | .NET 8.0 | 37.73 μs | 0.748 μs | 1.642 μs | 0.65 | 0.03 | 17.6392 | 0.9155 | 144.3 KB | 0.96 | +| Method | Job | Runtime | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | +|---------- |--------- |--------- |---------:|---------:|---------:|---------:|------:|--------:|--------:|-------:|----------:|------------:| +| CreateCss | .NET 5.0 | .NET 5.0 | 55.68 μs | 1.112 μs | 1.795 μs | 55.56 μs | 1.00 | 0.00 | 20.0806 | 1.0986 | 164.32 KB | 1.00 | +| CreateCss | .NET 6.0 | .NET 6.0 | 51.66 μs | 1.024 μs | 2.374 μs | 52.28 μs | 0.91 | 0.05 | 19.3481 | 1.0986 | 158.04 KB | 0.96 | +| CreateCss | .NET 7.0 | .NET 7.0 | 48.51 μs | 0.969 μs | 1.450 μs | 48.49 μs | 0.87 | 0.03 | 19.3481 | 1.0376 | 158.11 KB | 0.96 | +| CreateCss | .NET 8.0 | .NET 8.0 | 41.71 μs | 1.585 μs | 4.391 μs | 40.90 μs | 0.81 | 0.08 | 19.2871 | 1.0986 | 157.98 KB | 0.96 | +| CreateCss | .NET 9.0 | .NET 9.0 | 33.33 μs | 0.660 μs | 1.840 μs | 32.80 μs | 0.62 | 0.03 | 19.2871 | 1.0986 | 157.98 KB | 0.96 | + ## Special Thanks From b1c13aa98515bfbddea256dced0ec50568bff319 Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Fri, 10 Jan 2025 18:39:22 +0800 Subject: [PATCH 3/6] add FastColor --- src/Colors/ColorInput.cs | 16 +++ src/Colors/FastColor.cs | 271 +++++++++++++++++++++++++++++++++++++++ src/Colors/Util.cs | 11 ++ src/Css/CSSCalculator.cs | 50 ++++++++ src/Utils/Util.cs | 10 ++ 5 files changed, 358 insertions(+) create mode 100644 src/Colors/FastColor.cs create mode 100644 src/Css/CSSCalculator.cs create mode 100644 src/Utils/Util.cs diff --git a/src/Colors/ColorInput.cs b/src/Colors/ColorInput.cs index 1072578..c828c89 100644 --- a/src/Colors/ColorInput.cs +++ b/src/Colors/ColorInput.cs @@ -16,6 +16,7 @@ public sealed class ColorInput private readonly string _value0; private readonly int _value1; private readonly TinyColor _value2; + private readonly FastColor _value3; public ColorInput() { @@ -27,6 +28,7 @@ private ColorInput( string value0 = default, int value1 = default, TinyColor value2 = default, + FastColor value3 = default, RGB rgb = default, RGBA rgba = default, HSL hsl = default, @@ -38,6 +40,7 @@ private ColorInput( _value0 = value0; _value1 = value1; _value2 = value2; + _value3 = value3; if (rgb != default) { @@ -83,6 +86,7 @@ private ColorInput( public static implicit operator ColorInput(string value) => new(0, value0: value); public static implicit operator ColorInput(int value) => new(1, value1: value); public static implicit operator ColorInput(TinyColor value) => new(2, value2: value); + public static implicit operator ColorInput(FastColor value) => new(3, value3: value); public static implicit operator ColorInput(RGB value) => new(-1, rgb: value); public static implicit operator ColorInput(RGBA value) => new(-1, rgba: value); public static implicit operator ColorInput(HSL value) => new(-1, hsl: value); @@ -109,6 +113,8 @@ private ColorInput( public bool IsColor => _index == 2; + public bool IsFastColor => _index == 3; + public string AsString => _index == 0 ? _value0 : @@ -124,6 +130,16 @@ private ColorInput( _value2 : throw new InvalidOperationException(); + public FastColor AsFastColor => + _index == 3 ? + _value3 : + throw new InvalidOperationException(); + + public bool Is(string type) + { + return true; + } + public override string ToString() { return _index switch diff --git a/src/Colors/FastColor.cs b/src/Colors/FastColor.cs new file mode 100644 index 0000000..85ab6d4 --- /dev/null +++ b/src/Colors/FastColor.cs @@ -0,0 +1,271 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; + +namespace CssInCSharp.Colors +{ + public sealed class FastColor + { + public double _r = 0; + public double _g = 0; + public double _b = 0; + public double _a = 1; + public double? _h; + public double? _s; + public double? _l; + public double? _v; + + public FastColor(ColorInput input) + { + if (input == null) return; + + if (input.IsString) + { + var trimStr = input.AsString.Trim(); + if (Regex.IsMatch(trimStr, @"^#?[A-F\d]{3,8}$")) + { + FromHexString(trimStr); + } + else if (trimStr.StartsWith("rgb")) + { + FromRgbString(trimStr); + } + else if (trimStr.StartsWith("hsl")) + { + FromHslString(trimStr); + } + else if (trimStr.StartsWith("hsv") || trimStr.StartsWith("hsb")) + { + FromHsvString(trimStr); + } + } + else if (input.IsFastColor) + { + var color = input.AsFastColor; + _r = color._r; + _g = color._g; + _b = color._b; + _a = color._a; + _h = color._h; + _s = color._s; + _l = color._l; + _v = color._v; + } + else if (input.Is("rgb")) + { + _r = LimitRange(input.R.AsNumber); + _g = LimitRange(input.G.AsNumber); + _b = LimitRange(input.B.AsNumber); + _a = input.A is { IsNumber: true } ? LimitRange(input.A.Value.AsNumber) : 1; + } + else if (input.Is("hsl")) + { + FromHsl(input.H.AsNumber, input.S.AsNumber, input.L.AsNumber, input.A); + } + else if (input.Is("hsv")) + { + FromHsv(input.H.AsNumber, input.S.AsNumber, input.V.AsNumber, input.A); + } + else + { + throw new Exception(""); + } + } + + private void FromHexString(string trimStr) + { + var withoutPrefix = trimStr.Replace("#", ""); + double connectNum(int index1, int? index2 = null) + { + return Convert.ToInt32($"{withoutPrefix[index1]}{withoutPrefix[index2 ?? index1]}", 16); + } + + if (withoutPrefix.Length < 6) + { + _r = connectNum(0); + _g = connectNum(1); + _b = connectNum(2); + _a = withoutPrefix.Length > 3 ? connectNum(3) / 255 : 1; + } + else + { + _r = connectNum(0, 1); + _g = connectNum(2, 3); + _b = connectNum(4, 5); + _a = withoutPrefix.Length > 6 ? connectNum(6, 7) / 255 : 1; + } + } + + private void FromHsl(double h, double s, double l, StringNumber? a) + { + _h = h % 360; + _s = s; + _l = l; + _a = a is { IsNumber: true } ? a.Value.AsNumber : 1; + + if (s <= 0) + { + var rgb = Util.MathRound(l * 255); + _r = rgb; + _g = rgb; + _b = rgb; + } + + double r = 0, g = 0, b = 0; + var huePrime = h / 60; + var chroma = (1 - Math.Abs(2 * l - 1)) * s; + var secondComponent = chroma * (1 - Math.Abs((huePrime % 2) - 1)); + + if (huePrime >= 0 && huePrime < 1) + { + r = chroma; + g = secondComponent; + } + else if (huePrime >= 1 && huePrime < 2) + { + r = secondComponent; + g = chroma; + } + else if (huePrime >= 2 && huePrime < 3) + { + g = chroma; + b = secondComponent; + } + else if (huePrime >= 3 && huePrime < 4) + { + g = secondComponent; + b = chroma; + } + else if (huePrime >= 4 && huePrime < 5) + { + r = secondComponent; + b = chroma; + } + else if (huePrime >= 5 && huePrime < 6) + { + r = chroma; + b = secondComponent; + } + + var lightnessModification = l - chroma / 2; + _r = Util.MathRound((r + lightnessModification) * 255); + _g = Util.MathRound((g + lightnessModification) * 255); + _b = Util.MathRound((b + lightnessModification) * 255); + } + + private void FromHsv(double h, double s, double v, StringNumber? a) + { + _h = h % 360; + _s = s; + _v = v; + _a = a is { IsNumber: true } ? a.Value.AsNumber : 1; + + var vv = Util.MathRound(v * 255); + _r = vv; + _g = vv; + _b = vv; + + if (s <= 0) + { + return; + } + + var hh = h / 60; + var i = Math.Floor(hh); + var ff = hh - i; + var p = Util.MathRound(v * (1.0 - s) * 255); + var q = Util.MathRound(v * (1.0 - s * ff) * 255); + var t = Util.MathRound(v * (1.0 - s * (1.0 - ff)) * 255); + + switch (i) + { + case 0: + _g = t; + _b = p; + break; + case 1: + _r = q; + _b = p; + break; + case 2: + _r = p; + _b = t; + break; + case 3: + _r = p; + _g = q; + break; + case 4: + _r = t; + _g = p; + break; + case 5: + default: + _g = p; + _b = q; + break; + } + } + + private void FromHsvString(string trimStr) + { + var cells = SplitColorStr(trimStr, ParseHSVorHSL); + + FromHsv(cells[0], cells[1], cells[2], cells[3]); + } + + private void FromHslString(string trimStr) + { + + } + + private void FromRgbString(string trimStr) + { + + } + + private static double[] SplitColorStr(string str, Func parseNum) + { + var match = str + .RegexReplace(@"^[^(]*\((.*)", "$1") + .RegexReplace(@"\).*", "") + .Match(@"\d*\.?\d+%?"); + var numList = match.Select((item) => Convert.ToDouble(item)).ToArray(); + + for (var i = 0; i < 3; i += 1) + { + numList[i] = parseNum(numList[i], match[i], i); + } + + // For alpha. 50% should be 0.5 + if (match.Length > 3) { + numList[3] = match[3].Contains('%') ? numList[3] / 100 : numList[3]; + } else + { + // By default, alpha is 1 + numList[3] = 1; + } + + return numList; + } + + private static double ParseHSVorHSL(double num, string text, int index) + { + return index == 0 ? num : num / 100; + } + + private static double LimitRange(double value, double? max = null) + { + var mergedMax = max ?? 255; + if (value > mergedMax) + { + return mergedMax; + } + if (value < 0) + { + return 0; + } + return value; + } + } +} diff --git a/src/Colors/Util.cs b/src/Colors/Util.cs index c27770f..0a2918f 100644 --- a/src/Colors/Util.cs +++ b/src/Colors/Util.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; namespace CssInCSharp.Colors { @@ -11,6 +12,16 @@ public static string CharAt(this string str, int index) return str[index].ToString(); } + public static string RegexReplace(this string str, string pattern, string replacement) + { + return Regex.Replace(str, pattern, replacement); + } + + public static string[] Match(this string str, string pattern) + { + return Regex.Matches(str, pattern).Select(x => x.Value).ToArray(); + } + public static string Join(this IEnumerable values, string separator) { return string.Join(separator, values); diff --git a/src/Css/CSSCalculator.cs b/src/Css/CSSCalculator.cs new file mode 100644 index 0000000..d8b8bcd --- /dev/null +++ b/src/Css/CSSCalculator.cs @@ -0,0 +1,50 @@ +using CssInCSharp.Colors; + +namespace CssInCSharp.Css +{ + public static class CSSUtil + { + public static string Unit(StringNumber num) + { + return ""; + } + + public static CSSCalculator GenCalc() + { + return new CSSCalculator(); + } + } + + public class CSSCalculator + { + public CSSCalculator Add(StringNumber num) + { + return this; + } + + public CSSCalculator Sub(StringNumber num) + { + return this; + } + + public CSSCalculator Mul(StringNumber num) + { + return this; + } + + public CSSCalculator Div(StringNumber num) + { + return this; + } + + public string GetResult(bool force) + { + return ""; + } + + public string Equal() + { + return ""; + } + } +} diff --git a/src/Utils/Util.cs b/src/Utils/Util.cs new file mode 100644 index 0000000..b66abb0 --- /dev/null +++ b/src/Utils/Util.cs @@ -0,0 +1,10 @@ +namespace CssInCSharp.Utils +{ + public static class Util + { + public static string Unit(Property num) + { + return num.GetValue().ToString(); + } + } +} From 3991f64e7a17dd21a86c0166ba2062c254210a07 Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Mon, 28 Apr 2025 16:39:49 +0800 Subject: [PATCH 4/6] add FastColor tests --- src/Colors/ColorInput.cs | 9 +- src/Colors/FastColor.cs | 373 ++++++++++++- src/Colors/Util.cs | 39 ++ test/CssInCSharp.Tests/FastColorTests.cs | 642 +++++++++++++++++++++++ 4 files changed, 1055 insertions(+), 8 deletions(-) create mode 100644 test/CssInCSharp.Tests/FastColorTests.cs diff --git a/src/Colors/ColorInput.cs b/src/Colors/ColorInput.cs index c828c89..40d7ef1 100644 --- a/src/Colors/ColorInput.cs +++ b/src/Colors/ColorInput.cs @@ -17,6 +17,7 @@ public sealed class ColorInput private readonly int _value1; private readonly TinyColor _value2; private readonly FastColor _value3; + private string _type; public ColorInput() { @@ -47,6 +48,7 @@ private ColorInput( R = rgb.R; G = rgb.G; B = rgb.B; + _type = "rgb"; } if (rgba != default) { @@ -54,12 +56,14 @@ private ColorInput( G = rgba.G; B = rgba.B; A = rgba.A; + _type = "rgb"; } if (hsl != default) { H = hsl.H; S = hsl.S; L = hsl.L; + _type = "hsl"; } if (hsla != default) { @@ -67,12 +71,14 @@ private ColorInput( S = hsla.S; L = hsla.L; A = hsla.A; + _type = "hsl"; } if (hsv != default) { H = hsv.H; S = hsv.S; V = hsv.V; + _type = "hsv"; } if (hsva != default) { @@ -80,6 +86,7 @@ private ColorInput( S = hsva.S; V = hsva.V; A = hsva.A; + _type = "hsv"; } } @@ -137,7 +144,7 @@ private ColorInput( public bool Is(string type) { - return true; + return type == _type; } public override string ToString() diff --git a/src/Colors/FastColor.cs b/src/Colors/FastColor.cs index 85ab6d4..24bfa96 100644 --- a/src/Colors/FastColor.cs +++ b/src/Colors/FastColor.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Text; using System.Text.RegularExpressions; namespace CssInCSharp.Colors @@ -14,6 +15,40 @@ public sealed class FastColor public double? _s; public double? _l; public double? _v; + private double? _max; + private double? _min; + private double? _brightness; + + public double R => _r; + public double G => _g; + public double B => _b; + public double A => _a; + + public double this[string key] + { + get + { + switch (key) + { + case "r": return _r; + case "g": return _g; + case "b": return _b; + case "a": return _a; + } + + throw new Exception($"Invalid key: {key}"); + } + set + { + switch (key) + { + case "r": _r = value; break; + case "g": _g = value; break; + case "b": _b = value; break; + case "a": _a = value; break; + } + } + } public FastColor(ColorInput input) { @@ -22,7 +57,7 @@ public FastColor(ColorInput input) if (input.IsString) { var trimStr = input.AsString.Trim(); - if (Regex.IsMatch(trimStr, @"^#?[A-F\d]{3,8}$")) + if (Regex.IsMatch(trimStr, @"^#?[A-F\d]{3,8}$", RegexOptions.IgnoreCase)) { FromHexString(trimStr); } @@ -72,6 +107,314 @@ public FastColor(ColorInput input) } } + public FastColor SetR(double value) + { + return _sc("r", value); + } + + public FastColor SetG(double value) + { + return _sc("g", value); + } + + public FastColor SetB(double value) + { + return _sc("b", value); + } + + public FastColor SetA(double value) + { + return _sc("a", value, 1); + } + + public FastColor SetHue(double value) + { + var hsv = ToHsv(); + hsv.H = value; + return _c(hsv); + } + + public double GetLuminance() + { + double adjustGamma(double raw) + { + var val = raw / 255; + + return val <= 0.03928 + ? val / 12.92 + : Math.Pow((val + 0.055) / 1.055, 2.4); + } + + var r = adjustGamma(_r); + var g = adjustGamma(_g); + var b = adjustGamma(_b); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; + } + + public double GetHue() + { + if (!_h.HasValue) { + var delta = GetMax() - GetMin(); + if (delta == 0) + { + this._h = 0; + } + else + { + this._h = Util.MathRound(( + 60 * + (_r == GetMax() + ? (_g - _b) / delta + (_g < _b ? 6 : 0) + : _g == GetMax() + ? (_b - _r) / delta + 2 + : (_r - _g) / delta + 4) + ) + ); + } + } + return _h.Value; + } + + public double GetSaturation() + { + if (!_s.HasValue) { + var delta = GetMax() - GetMin(); + if (delta == 0) + { + _s = 0; + } + else + { + _s = delta / GetMax(); + } + } + return _s.Value; + } + + public double GetLightness() + { + if (!_l.HasValue) { + _l = (GetMax() + GetMin()) / 510; + } + return _l.Value; + } + + public double GetValue() + { + if (!_v.HasValue) { + _v = GetMax() / 255; + } + return _v.Value; + } + + public double GetBrightness() + { + if (!_brightness.HasValue) { + _brightness = (_r * 299 + _g * 587 + _b * 114) / 1000; + } + return _brightness.Value; + } + + public FastColor Darken(double amount = 10) + { + var h = GetHue(); + var s = GetSaturation(); + var l = GetLightness() - amount / 100; + if (l < 0) + { + l = 0; + } + + return _c(new HSLA { H = h, S = s, L = l, A = _a }); + } + + public FastColor Lighten(double amount = 10) + { + var h = GetHue(); + var s = GetSaturation(); + var l = GetLightness() + amount / 100; + if (l > 1) + { + l = 1; + } + + return _c(new HSLA { H = h, S = s, L = l, A = _a }); + } + + public FastColor Mix(ColorInput input, double amount = 50) + { + var color = _c(input); + + var p = amount / 100; + var calc = (string key) => (color[key] - this[key]) * p + this[key]; + + var rgba = new RGBA { + R = Util.MathRound(calc("r")), + G = Util.MathRound(calc("g")), + B = Util.MathRound(calc("b")), + A = Util.MathRound(calc("a") * 100) / 100, + }; + + return _c(rgba); + } + + public FastColor Tint(double amount = 10) + { + return Mix(new RGBA { R = 255, G = 255, B = 255, A = 1 }, amount); + } + + public FastColor Shade(double amount = 10) + { + return Mix(new RGBA { R = 0, G = 0, B = 0, A = 1 }, amount); + } + + public FastColor OnBackground(ColorInput background) + { + var bg = _c(background); + var alpha = _a + bg._a * (1 - _a); + + var calc = (string key) => Util.MathRound( + (this[key] * _a + bg[key] * bg._a * (1 - _a)) / alpha + ); + + return _c(new RGBA{ + R = calc("r"), + G = calc("g"), + B = calc("b"), + A = alpha, + }); + } + + public bool IsDark() { + return GetBrightness() < 128; + } + + public bool IsLight() + { + return GetBrightness() >= 128; + } + + public override bool Equals(object obj) + { + if (obj == null) return false; + if (obj is FastColor other) + { + return ( + _r == other._r && + _g == other._g && + _b == other._b && + _a == other._a + ); + } + + return false; + } + + public FastColor Clone() + { + return _c(this); + } + + public string ToHexString() + { + var hex = new StringBuilder("#"); + var rHex = ((long)_r).ToString("x").PadLeft(2, '0'); + hex.Append(rHex); + var gHex = ((long)_g).ToString("x").PadLeft(2, '0'); + hex.Append(gHex); + var bHex = ((long)_b).ToString("x").PadLeft(2, '0'); + hex.Append(bHex); + if (_a >= 0 && _a< 1) + { + var aHex = ((long)Util.MathRound(_a * 255)).ToString("X").PadLeft(2, '0'); + hex.Append(aHex); + } + return hex.ToString(); + } + + public HSLA ToHsl() + { + return new HSLA + { + H = GetHue(), + S = GetSaturation(), + L = GetLightness(), + A = _a, + }; + } + + public string ToHslString() { + var h = GetHue(); + var s = Util.MathRound(GetSaturation() * 100); + var l = Util.MathRound(GetLightness() * 100); + return _a != 1 + ? $"hsla({h},{s}%,{l}%,{_a})" + : $"hsl({h},{s}%,{l}%)"; + } + + public HSVA ToHsv() + { + return new HSVA + { + H = GetHue(), + S = GetSaturation(), + V = GetValue(), + A = _a, + }; + } + + public RGBA ToRgb() + { + return new RGBA + { + R = _r, + G = _g, + B = _b, + A = _a, + }; + } + + public string ToRgbString() + { + return _a != 1 + ? $"rgba({_r},{_g},{_b},{_a})" + : $"rgb({_r},{_g},{_b})"; + } + + public override string ToString() + { + return ToRgbString(); + } + + private FastColor _sc(string rgb, double value, double? max = null) + { + var clone = Clone(); + clone[rgb] = LimitRange(value, max); + return clone; + } + + private FastColor _c(ColorInput input) + { + return new FastColor(input); + } + + private double GetMax() + { + if (!_max.HasValue) { + _max = Util.MathMax(_r, _g, _b); + } + return _max.Value; + } + + private double GetMin() + { + if (!_min.HasValue) { + _min = Util.MathMin(_r, _g, _b); + } + return _min.Value; + } + private void FromHexString(string trimStr) { var withoutPrefix = trimStr.Replace("#", ""); @@ -216,12 +559,21 @@ private void FromHsvString(string trimStr) private void FromHslString(string trimStr) { - + var cells = SplitColorStr(trimStr, ParseHSVorHSL); + FromHsl(cells[0], cells[1], cells[2], cells[3]); } private void FromRgbString(string trimStr) { - + var cells = SplitColorStr(trimStr, (num, txt, _) => + // Convert percentage to number. e.g. 50% -> 128 + txt.Contains("%") ? Util.MathRound((num / 100) * 255) : num + ); + + _r = cells[0]; + _g = cells[1]; + _b = cells[2]; + _a = cells[3]; } private static double[] SplitColorStr(string str, Func parseNum) @@ -230,11 +582,18 @@ private static double[] SplitColorStr(string str, Func Convert.ToDouble(item)).ToArray(); + var numList = match.Select((item) => Util.ParseFloat(item)).ToList(); for (var i = 0; i < 3; i += 1) { - numList[i] = parseNum(numList[i], match[i], i); + if (i < numList.Count) + { + numList[i] = parseNum(numList[i], match[i], i); + } + else + { + numList.Add(parseNum(0, "", i)); + } } // For alpha. 50% should be 0.5 @@ -243,10 +602,10 @@ private static double[] SplitColorStr(string str, Func= '0' && c <= '9') || (c == '.' && !hasDot) || (i == 0 && (c == '+' || c == '-'))) + { + if (c == '.') + hasDot = true; + else if (char.IsDigit(c)) + hasDigit = true; + i++; + } + else + { + break; + } + } + + if (!hasDigit) + return double.NaN; + + var numberPart = input.Substring(0, i); + + if (double.TryParse(numberPart, out var result)) + return result; + + return double.NaN; + } } } diff --git a/test/CssInCSharp.Tests/FastColorTests.cs b/test/CssInCSharp.Tests/FastColorTests.cs new file mode 100644 index 0000000..4e59945 --- /dev/null +++ b/test/CssInCSharp.Tests/FastColorTests.cs @@ -0,0 +1,642 @@ +using CssInCSharp.Colors; +using Shouldly; +using Xunit; + +namespace CssInCSharp.Tests +{ + public class FastColorTests + { + [Fact] + public void Should_Init() + { + var r = new FastColor("#66ccff"); + r.ToHexString().ShouldBe("#66ccff"); + } + + [Fact] + public void Should_Clone() + { + var color1 = new FastColor("#66ccff"); + color1.ToString().ShouldBe("rgb(102,204,255)"); + color1.ToRgb().ShouldBe(new RGBA + { + A = 1, + B = 255, + G = 204, + R = 102 + }); + + var color2 = color1.Clone(); + color2.ToRgb().ShouldBe(new RGBA{ + A = 1, + B = 255, + G = 204, + R = 102, + }); + color2 = color2.SetA(0.5); + color2.ToString().ShouldBe("rgba(102,204,255,0.5)"); + } + + [Fact] + public void Should_Parse_Hex() + { + new FastColor("#000").ToHexString().ShouldBe("#000000"); + new FastColor("#0000").ToHexString().ShouldBe("#00000000"); + new FastColor("#000").A.ShouldBe(1); + new FastColor("#0000").A.ShouldBe(0); + } + + [Fact] + public void Should_Parse_Rgb() + { + new FastColor("rgb(255,0,0)").ToHexString().ShouldBe("#ff0000"); + // parenthesized spaced input + new FastColor("rgb (255,0,0)").ToHexString().ShouldBe("#ff0000"); + // object input + new FastColor(new RGB{ R = 255, G = 0, B = 0 }).ToHexString().ShouldBe("#ff0000"); + // object input and compare + new FastColor(new RGB { R = 255, G = 0, B = 0 }).ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 0, + B = 0, + A = 1, + }); + + new FastColor(new RGB { R = 200, G = 100, B = 0 }).Equals(new FastColor("rgb(200, 100, 0)")).ShouldBe(true); + + new FastColor(new RGBA { R = 200, G = 100, B = 0, A = 0.4 }).Equals + ( + new FastColor("rgba(200 100 0 .4)") + ).ShouldBe(true); + + new FastColor(new RGB { R = 199, G = 100, B = 0 }).Equals + ( + new FastColor(new RGB { R = 200, G = 100, B = 0 }) + ).ShouldBe(false); + } + + [Fact] + public void Should_Parse_Percentage_Rgb_Text() + { + // parenthesized input + new FastColor("rgb(100%, 0%, 0%)").ToHexString().ShouldBe("#ff0000"); + // parenthesized spaced input + new FastColor("rgb (100%, 0%, 0%)").ToHexString().ShouldBe("#ff0000"); + } + + [Fact] + public void Should_Parse_HSL() + { + new FastColor(new HSL { H = 251, S = 1, L = 0.38 }).ToHexString().ShouldBe("#2400c2"); + // to rgb + new FastColor(new HSL { H = 251, S = 1, L = 0.38 }).ToRgbString().ShouldBe("rgb(36,0,194)"); + // to hsl + new FastColor(new HSL { H = 251, S = 1, L = 0.38 }).ToHslString().ShouldBe("hsl(251,100%,38%)"); + + new FastColor(new HSLA { H = 251, S = 1, L = 0.38, A = 0.38 }).ToHslString().ShouldBe("hsla(251,100%,38%,0.38)"); + // to hex + new FastColor("hsl(251,100,38)").ToHexString().ShouldBe("#2400c2"); + // to rgb + new FastColor("hsl(251,100%,38%)").ToRgbString().ShouldBe("rgb(36,0,194)"); + // to hsl + new FastColor("hsl(251,100%,38%)").ToHslString().ShouldBe("hsl(251,100%,38%)"); + } + + [Fact] + public void Should_Get_Alpha() + { + var hexSetter = new FastColor("rgba(255,0,0,1)"); + // Alpha should start as 1 + hexSetter.A.ShouldBe(1); + hexSetter = hexSetter.SetA(0.9); + // SetAlpha should change alpha value + hexSetter.A.ShouldBe(0.9); + hexSetter = hexSetter.SetA(0.5); + // SetAlpha should change alpha value + hexSetter.A.ShouldBe(0.5); + } + + [Fact] + public void Should_Set_Alpha() + { + var hexSetter = new FastColor("rgba(255,0,0,1)"); + // Alpha should start as 1 + hexSetter.A.ShouldBe(1); + hexSetter = hexSetter.SetA(0.5); + // SetAlpha should change alpha value + hexSetter.A.ShouldBe(0.5); + hexSetter = hexSetter.SetA(0); + // SetAlpha should change alpha value + hexSetter.A.ShouldBe(0); + hexSetter = hexSetter.SetA(-1); + // SetAlpha with value < 0 is corrected to 0 + hexSetter.A.ShouldBe(0); + hexSetter = hexSetter.SetA(2); + // SetAlpha with value > 1 is corrected to 1 + hexSetter.A.ShouldBe(1); + } + + [Fact] + public void Should_GetBrightness() + { + new FastColor("#000").GetBrightness().ShouldBe(0); + new FastColor("#fff").GetBrightness().ShouldBe(255); + } + + [Fact] + public void Should_GetLuminance() + { + new FastColor("#000").GetLuminance().ShouldBe(0); + new FastColor("#fff").GetLuminance().ShouldBe(1); + } + + [Fact] + public void IsDark_Returns_True_Or_False_For_Dark_Or_Light_Colors() + { + new FastColor("#000").IsDark().ShouldBe(true); + new FastColor("#111").IsDark().ShouldBe(true); + new FastColor("#222").IsDark().ShouldBe(true); + new FastColor("#333").IsDark().ShouldBe(true); + new FastColor("#444").IsDark().ShouldBe(true); + new FastColor("#555").IsDark().ShouldBe(true); + new FastColor("#666").IsDark().ShouldBe(true); + new FastColor("#777").IsDark().ShouldBe(true); + new FastColor("#888").IsDark().ShouldBe(false); + new FastColor("#999").IsDark().ShouldBe(false); + new FastColor("#aaa").IsDark().ShouldBe(false); + new FastColor("#bbb").IsDark().ShouldBe(false); + new FastColor("#ccc").IsDark().ShouldBe(false); + new FastColor("#ddd").IsDark().ShouldBe(false); + new FastColor("#eee").IsDark().ShouldBe(false); + new FastColor("#fff").IsDark().ShouldBe(false); + } + + [Fact] + public void IsLight_Returns_True_Or_False_For_Dark_Or_Light_Colors() + { + new FastColor("#000").IsLight().ShouldBe(false); + new FastColor("#111").IsLight().ShouldBe(false); + new FastColor("#222").IsLight().ShouldBe(false); + new FastColor("#333").IsLight().ShouldBe(false); + new FastColor("#444").IsLight().ShouldBe(false); + new FastColor("#555").IsLight().ShouldBe(false); + new FastColor("#666").IsLight().ShouldBe(false); + new FastColor("#777").IsLight().ShouldBe(false); + new FastColor("#888").IsLight().ShouldBe(true); + new FastColor("#999").IsLight().ShouldBe(true); + new FastColor("#aaa").IsLight().ShouldBe(true); + new FastColor("#bbb").IsLight().ShouldBe(true); + new FastColor("#ccc").IsLight().ShouldBe(true); + new FastColor("#ddd").IsLight().ShouldBe(true); + new FastColor("#eee").IsLight().ShouldBe(true); + new FastColor("#fff").IsLight().ShouldBe(true); + } + + [Fact] + public void Color_Equality() + { + new FastColor("#ff0000").Equals(new FastColor("#ff0000")).ShouldBe(true); + new FastColor("#ff0000").Equals(new FastColor("rgb(255,0,0)")).ShouldBe(true); + new FastColor("#ff0000").Equals(new FastColor("rgba(255,0,0,.1)")).ShouldBe(false); + new FastColor("#ff000066").Equals(new FastColor("rgba(255,0,0,.4)")).ShouldBe(true); + new FastColor("#f009").Equals(new FastColor("rgba(255,0,0,.6)")).ShouldBe(true); + new FastColor("#336699CC").Equals(new FastColor("#369C")).ShouldBe(true); + new FastColor("#f00").Equals(new FastColor("#ff0000")).ShouldBe(true); + new FastColor("#f00").Equals(new FastColor("#ff0000")).ShouldBe(true); + new FastColor("#ff0000").Equals(new FastColor("#00ff00")).ShouldBe(false); + new FastColor("#ff8000").Equals(new FastColor("rgb(100%, 50%, 0%)")).ShouldBe(true); + } + + [Fact] + public void OnBackground() + { + new FastColor("#ffffff").OnBackground(new FastColor("#000")).ToHexString().ShouldBe("#ffffff"); + new FastColor("#ffffff00").OnBackground(new FastColor("#000")).ToHexString().ShouldBe("#000000"); + new FastColor("#ffffff77").OnBackground(new FastColor("#000")).ToHexString().ShouldBe("#777777"); + new FastColor("#262a6d82").OnBackground(new FastColor("#644242")).ToHexString().ShouldBe("#443658"); + new FastColor("rgba(255,0,0,0.5)").OnBackground(new FastColor("rgba(0,255,0,0.5)")).ToRgbString().ShouldBe("rgba(170,85,0,0.75)"); + new FastColor("rgba(255,0,0,0.5)").OnBackground(new FastColor("rgba(0,0,255,1)")).ToRgbString().ShouldBe("rgb(128,0,128)"); + new FastColor("rgba(0,0,255,1)").OnBackground(new FastColor("rgba(0,0,0,0.5)")).ToRgbString().ShouldBe("rgb(0,0,255)"); + } + + [Fact] + public void Default_Of_Empty() + { + new FastColor("").ToRgb().ShouldBe(new RGBA + { + R = 0, + G = 0, + B = 0, + A = 1 + }); + } + + [Fact] + public void Mix_Should_Round() + { + var source = new FastColor("rgba(255, 255, 255, 0.1128)"); + var target = new FastColor("rgba(0, 0, 0, 0.93)"); + + var mixed = source.Mix(target, 50); + + mixed.ToRgbString().ShouldBe("rgba(128,128,128,0.52)"); + } + + [Fact] + public void Hsv_Object_To_Hex() + { + new FastColor(new HSV { H = 270, S = 0.6, V = 0.4 }).ToHexString().ShouldBe("#472966"); + } + + [Fact] + public void Hsv_String_To_Hex() + { + new FastColor("hsv(270, 60%, 40%)").ToHexString().ShouldBe("#472966"); + } + + [Fact] + public void Hsb_String_To_Hex() + { + new FastColor("hsb(270 60 40)").ToHexString().ShouldBe("#472966"); + } + + [Fact] + public void Hex_To_Hsv_Object () + { + new FastColor("#472966").ToHsv().ShouldBe(new HSVA + { + H = 270, + S = 0.5980392156862745, + V = 0.4, + A = 1, + }); + } + + [Fact] + public void Should_Be_Same_RGB() + { + var baseColor = new FastColor(new HSV + { + H = 180, + S = 0, + V = 0, + }); + + var turn = baseColor.SetHue(0); + + baseColor.ToHexString().ShouldBe(turn.ToHexString()); + } + + [Fact] + public void Clone_Should_Be_Same_Hsv() + { + var baseColor = new FastColor(new HSV{ + H = 180, + S = 0, + V = 0, + }); + + var turn = baseColor.Clone(); + baseColor.ToHsv().ShouldBe(turn.ToHsv()); + } + + [Fact] + public void SetHue_Should_Be_Stable() + { + var baseColor = new FastColor("#1677ff"); + baseColor.GetValue().ShouldBe(1); + + var turn = baseColor.SetHue(233); + turn.GetValue().ShouldBe(1); + } + + [Fact] + public void New_Space_Separated_Syntax_Without_Units() + { + new FastColor("hsl(270 60 40)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 1, + }); + + new FastColor("hsl(270 60 40 / 0.2)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsl(270 60 40 / .2)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsl(270 60 40 / 0.233)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + + new FastColor("hsl(270 60 40 / .233)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + } + + [Fact] + public void New_Space_Separated_Syntax_With_Units() + { + new FastColor("hsl(270 60 40)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 1, + }); + + new FastColor("hsl(270 60 40 / 20%)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsl(270 60 40 / 23.3%)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + } + + [Fact] + public void Old_Comma_Separated_Syntax_Without_Units() + { + new FastColor("hsl(270, 60, 40)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 1, + }); + + new FastColor("hsla(270, 60, 40, 0.2)").ToRgb().ShouldBe(new RGBA{ + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsla(270, 60, 40, .2)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsla(270, 60, 40, 0.233)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + + new FastColor("hsla(270, 60, 40, .233)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + } + + [Fact] + public void Old_Comma_Separated_Syntax_With_Units() + { + new FastColor("hsl(270deg, 60%, 40%)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 1, + }); + + new FastColor("hsla(270deg, 60%, 40%, 20%)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.2, + }); + + new FastColor("hsla(270deg, 60%, 40%, 23.3%)").ToRgb().ShouldBe(new RGBA + { + R = 102, + G = 41, + B = 163, + A = 0.233, + }); + + // String back + new FastColor("hsla(270, 60, 40, 0.2)").ToHslString().ShouldBe("hsla(270,60%,40%,0.2)"); + + new FastColor("hsla(270deg, 60%, 40%, 23.3%)").ToHslString().ShouldBe("hsla(270,60%,40%,0.233)"); + } + + [Fact] + public void New_Space_Separated_Syntax() + { + new FastColor("rgb(255 255 255)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 1, + }); + + new FastColor("rgb(255 255 255 / 0.2)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgb(255 255 255 / .2)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgb(255 255 255 / 0.233)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + + new FastColor("rgb(255 255 255 / .233)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + + new FastColor("rgb(100% 100% 100%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 1, + }); + + new FastColor("rgb(100% 100% 100%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 1, + }); + + new FastColor("rgb(100% 100% 100% / 20%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgb(100% 100% 100% / 23.3%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + } + + [Fact] + public void Old_Comma_Separated_Syntax() + { + new FastColor("rgb(255, 255, 255)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 1, + }); + + new FastColor("rgba(255, 255, 255, 0.2)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgba(255, 255, 255, .2)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgba(255, 255, 255, 0.233)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + + new FastColor("rgba(255, 255, 255, .233)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + + new FastColor("rgb(100%, 100%, 100%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 1, + }); + + new FastColor("rgba(100%, 100%, 100%, 20%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.2, + }); + + new FastColor("rgba(100%, 100%, 100%, 23.3%)").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 255, + B = 255, + A = 0.233, + }); + } + + [Fact] + public void Invalid_Rgb() + { + new FastColor("rgb").ToRgb().ShouldBe(new RGBA + { + R = 0, + G = 0, + B = 0, + A = 1, + }); + } + + [Fact] + public void Rgb_With_Extra_Stop() + { + new FastColor("rgb(255, 90, 30) 0%").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 90, + B = 30, + A = 1, + }); + } + + [Fact] + public void Pure_Rbg() + { + new FastColor("FF00FF").ToRgb().ShouldBe(new RGBA + { + R = 255, + G = 0, + B = 255, + A = 1, + }); + } + } +} From c74b7087bae83828ae86921f49d226fdb8995c15 Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Mon, 28 Apr 2025 16:44:15 +0800 Subject: [PATCH 5/6] remove unimplemented class --- src/Css/CSSCalculator.cs | 50 ---------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/Css/CSSCalculator.cs diff --git a/src/Css/CSSCalculator.cs b/src/Css/CSSCalculator.cs deleted file mode 100644 index d8b8bcd..0000000 --- a/src/Css/CSSCalculator.cs +++ /dev/null @@ -1,50 +0,0 @@ -using CssInCSharp.Colors; - -namespace CssInCSharp.Css -{ - public static class CSSUtil - { - public static string Unit(StringNumber num) - { - return ""; - } - - public static CSSCalculator GenCalc() - { - return new CSSCalculator(); - } - } - - public class CSSCalculator - { - public CSSCalculator Add(StringNumber num) - { - return this; - } - - public CSSCalculator Sub(StringNumber num) - { - return this; - } - - public CSSCalculator Mul(StringNumber num) - { - return this; - } - - public CSSCalculator Div(StringNumber num) - { - return this; - } - - public string GetResult(bool force) - { - return ""; - } - - public string Equal() - { - return ""; - } - } -} From 0ed90484de415918602c8389b91a57661c2209d4 Mon Sep 17 00:00:00 2001 From: zhaowenkai <799480165@qq.com> Date: Mon, 28 Apr 2025 16:51:49 +0800 Subject: [PATCH 6/6] remove unused class --- src/Utils/Util.cs | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/Utils/Util.cs diff --git a/src/Utils/Util.cs b/src/Utils/Util.cs deleted file mode 100644 index b66abb0..0000000 --- a/src/Utils/Util.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CssInCSharp.Utils -{ - public static class Util - { - public static string Unit(Property num) - { - return num.GetValue().ToString(); - } - } -}