From 32639d3090bec825297ca9cb9e1b06e5a5176077 Mon Sep 17 00:00:00 2001 From: Diogo Trindade Date: Mon, 19 Oct 2020 18:49:30 +0100 Subject: [PATCH] added comments, moved helper class to separate file --- .../Services/ColorQuantizer/ColorCube.cs | 67 +++++++++++++++ .../ColorQuantizerService.cs | 81 ++----------------- .../Services/ColorQuantizer/ColorType.cs | 12 +++ .../Interfaces/IColorQuantizerService.cs | 30 +++++++ .../Interfaces/IColorQuantizerService.cs | 17 ---- 5 files changed, 117 insertions(+), 90 deletions(-) create mode 100644 src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs rename src/Artemis.Core/Services/{ => ColorQuantizer}/ColorQuantizerService.cs (62%) create mode 100644 src/Artemis.Core/Services/ColorQuantizer/ColorType.cs create mode 100644 src/Artemis.Core/Services/ColorQuantizer/Interfaces/IColorQuantizerService.cs delete mode 100644 src/Artemis.Core/Services/Interfaces/IColorQuantizerService.cs diff --git a/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs b/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs new file mode 100644 index 000000000..631b9a87b --- /dev/null +++ b/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs @@ -0,0 +1,67 @@ +using SkiaSharp; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Artemis.Core.Services +{ + internal class ColorCube + { + private readonly List _colors; + + internal ColorCube(IEnumerable colors) + { + if (colors.Count() < 2) + { + _colors = colors.ToList(); + return; + } + + int redRange = colors.Max(c => c.Red) - colors.Min(c => c.Red); + int greenRange = colors.Max(c => c.Green) - colors.Min(c => c.Green); + int blueRange = colors.Max(c => c.Blue) - colors.Min(c => c.Blue); + + if (redRange > greenRange && redRange > blueRange) + _colors = colors.OrderBy(a => a.Red).ToList(); + else if (greenRange > blueRange) + _colors = colors.OrderBy(a => a.Green).ToList(); + else + _colors = colors.OrderBy(a => a.Blue).ToList(); + } + + internal bool TrySplit([NotNullWhen(returnValue: true)] out ColorCube? a, [NotNullWhen(returnValue: true)] out ColorCube? b) + { + if (_colors.Count < 2) + { + a = null; + b = null; + return false; + } + + int median = _colors.Count / 2; + + a = new ColorCube(_colors.GetRange(0, median)); + b = new ColorCube(_colors.GetRange(median, _colors.Count - median)); + + return true; + } + + internal SKColor GetAverageColor() + { + int r = 0, g = 0, b = 0; + + for (int i = 0; i < _colors.Count; i++) + { + r += _colors[i].Red; + g += _colors[i].Green; + b += _colors[i].Blue; + } + + return new SKColor( + (byte)(r / _colors.Count), + (byte)(g / _colors.Count), + (byte)(b / _colors.Count) + ); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/ColorQuantizerService.cs b/src/Artemis.Core/Services/ColorQuantizer/ColorQuantizerService.cs similarity index 62% rename from src/Artemis.Core/Services/ColorQuantizerService.cs rename to src/Artemis.Core/Services/ColorQuantizer/ColorQuantizerService.cs index 2281c5850..c90260f57 100644 --- a/src/Artemis.Core/Services/ColorQuantizerService.cs +++ b/src/Artemis.Core/Services/ColorQuantizer/ColorQuantizerService.cs @@ -2,36 +2,25 @@ using SkiaSharp; using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; namespace Artemis.Core.Services { - public enum ColorType - { - Vibrant, - LightVibrant, - DarkVibrant, - Muted, - LightMuted, - DarkMuted - } - + /// public class ColorQuantizerService : IColorQuantizerService { - #region Quantizer + /// public SKColor[] Quantize(IEnumerable colors, int amount) { if ((amount & (amount - 1)) != 0) throw new ArgumentException("Must be power of two", nameof(amount)); - Queue cubes = new Queue(amount); - cubes.Enqueue(new Cube(colors)); + Queue cubes = new Queue(amount); + cubes.Enqueue(new ColorCube(colors)); while (cubes.Count < amount) { - Cube cube = cubes.Dequeue(); + ColorCube cube = cubes.Dequeue(); if (cube.TrySplit(out var a, out var b)) { cubes.Enqueue(a); @@ -42,62 +31,7 @@ namespace Artemis.Core.Services return cubes.Select(c => c.GetAverageColor()).ToArray(); } - private class Cube - { - private readonly List _colors; - - internal Cube(IEnumerable colors) - { - int redRange = colors.Max(c => c.Red) - colors.Min(c => c.Red); - int greenRange = colors.Max(c => c.Green) - colors.Min(c => c.Green); - int blueRange = colors.Max(c => c.Blue) - colors.Min(c => c.Blue); - - if (redRange > greenRange && redRange > blueRange) - _colors = colors.OrderBy(a => a.Red).ToList(); - else if (greenRange > blueRange) - _colors = colors.OrderBy(a => a.Green).ToList(); - else - _colors = colors.OrderBy(a => a.Blue).ToList(); - } - - internal bool TrySplit([NotNullWhen(returnValue: true)] out Cube? a, [NotNullWhen(returnValue: true)] out Cube? b) - { - if (_colors.Count < 2) - { - a = null; - b = null; - return false; - } - - int median = _colors.Count / 2; - - a = new Cube(_colors.GetRange(0, median)); - b = new Cube(_colors.GetRange(median, _colors.Count - median)); - - return true; - } - - internal SKColor GetAverageColor() - { - int r = 0, g = 0, b = 0; - - for (int i = 0; i < _colors.Count; i++) - { - r += _colors[i].Red; - g += _colors[i].Green; - b += _colors[i].Blue; - } - - return new SKColor( - (byte)(r / _colors.Count), - (byte)(g / _colors.Count), - (byte)(b / _colors.Count) - ); - } - } - #endregion - - #region Vibrant Classifier + #region Constants private const float targetDarkLuma = 0.26f; private const float maxDarkLuma = 0.45f; private const float minLightLuma = 0.55f; @@ -111,7 +45,9 @@ namespace Artemis.Core.Services private const float minVibrantSaturation = 0.35f; private const float weightSaturation = 3f; private const float weightLuma = 5f; + #endregion + /// public SKColor FindColorVariation(IEnumerable colors, ColorType type, bool ignoreLimits = false) { (float targetLuma, float minLuma, float maxLuma, float targetSaturation, float minSaturation, float maxSaturation) = type switch @@ -157,6 +93,5 @@ namespace Artemis.Core.Services return totalValue / totalweight; } - #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/ColorQuantizer/ColorType.cs b/src/Artemis.Core/Services/ColorQuantizer/ColorType.cs new file mode 100644 index 000000000..e6f598a50 --- /dev/null +++ b/src/Artemis.Core/Services/ColorQuantizer/ColorType.cs @@ -0,0 +1,12 @@ +namespace Artemis.Core.Services +{ + public enum ColorType + { + Vibrant, + LightVibrant, + DarkVibrant, + Muted, + LightMuted, + DarkMuted + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/ColorQuantizer/Interfaces/IColorQuantizerService.cs b/src/Artemis.Core/Services/ColorQuantizer/Interfaces/IColorQuantizerService.cs new file mode 100644 index 000000000..e5690867c --- /dev/null +++ b/src/Artemis.Core/Services/ColorQuantizer/Interfaces/IColorQuantizerService.cs @@ -0,0 +1,30 @@ +using SkiaSharp; +using System.Collections.Generic; + +namespace Artemis.Core.Services.Interfaces +{ + /// + /// A service providing a pallette of colors in a bitmap based on vibrant.js + /// + public interface IColorQuantizerService : IArtemisService + { + /// + /// Reduces an to a given amount of relevant colors. Based on the Median Cut algorithm + /// + /// The colors to quantize. + /// The number of colors that should be calculated. Must be a power of two. + /// The quantized colors. + public SKColor[] Quantize(IEnumerable colors, int amount); + + /// + /// Finds colors with certain characteristics in a given . + /// Vibrant variants are more saturated, while Muted colors are less. + /// Light and Dark colors have higher and lower lightness values, respectively. + /// + /// The colors to find the variations in + /// Which type of color to find + /// Ignore hard limits on whether a color is considered for each category. Result may be if this is true + /// The color found + public SKColor FindColorVariation(IEnumerable colors, ColorType type, bool ignoreLimits = false); + } +} diff --git a/src/Artemis.Core/Services/Interfaces/IColorQuantizerService.cs b/src/Artemis.Core/Services/Interfaces/IColorQuantizerService.cs deleted file mode 100644 index d199e9163..000000000 --- a/src/Artemis.Core/Services/Interfaces/IColorQuantizerService.cs +++ /dev/null @@ -1,17 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Artemis.Core.Services.Interfaces -{ - /// - /// A service providing a pallette of colors in a bitmap based on vibrant.js - /// - public interface IColorQuantizerService : IArtemisService - { - public SKColor[] Quantize(IEnumerable colors, int amount); - - public SKColor FindColorVariation(IEnumerable colors, ColorType type, bool ignoreLimits = false); - } -}