mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 10:13:30 +00:00
added comments, moved helper class to separate file
This commit is contained in:
parent
6a00e2f515
commit
32639d3090
67
src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs
Normal file
67
src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs
Normal file
@ -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<SKColor> _colors;
|
||||||
|
|
||||||
|
internal ColorCube(IEnumerable<SKColor> 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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,36 +2,25 @@
|
|||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
public enum ColorType
|
/// <inheritdoc />
|
||||||
{
|
|
||||||
Vibrant,
|
|
||||||
LightVibrant,
|
|
||||||
DarkVibrant,
|
|
||||||
Muted,
|
|
||||||
LightMuted,
|
|
||||||
DarkMuted
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ColorQuantizerService : IColorQuantizerService
|
public class ColorQuantizerService : IColorQuantizerService
|
||||||
{
|
{
|
||||||
#region Quantizer
|
/// <inheritdoc />
|
||||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount)
|
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount)
|
||||||
{
|
{
|
||||||
if ((amount & (amount - 1)) != 0)
|
if ((amount & (amount - 1)) != 0)
|
||||||
throw new ArgumentException("Must be power of two", nameof(amount));
|
throw new ArgumentException("Must be power of two", nameof(amount));
|
||||||
|
|
||||||
Queue<Cube> cubes = new Queue<Cube>(amount);
|
Queue<ColorCube> cubes = new Queue<ColorCube>(amount);
|
||||||
cubes.Enqueue(new Cube(colors));
|
cubes.Enqueue(new ColorCube(colors));
|
||||||
|
|
||||||
while (cubes.Count < amount)
|
while (cubes.Count < amount)
|
||||||
{
|
{
|
||||||
Cube cube = cubes.Dequeue();
|
ColorCube cube = cubes.Dequeue();
|
||||||
if (cube.TrySplit(out var a, out var b))
|
if (cube.TrySplit(out var a, out var b))
|
||||||
{
|
{
|
||||||
cubes.Enqueue(a);
|
cubes.Enqueue(a);
|
||||||
@ -42,62 +31,7 @@ namespace Artemis.Core.Services
|
|||||||
return cubes.Select(c => c.GetAverageColor()).ToArray();
|
return cubes.Select(c => c.GetAverageColor()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Cube
|
#region Constants
|
||||||
{
|
|
||||||
private readonly List<SKColor> _colors;
|
|
||||||
|
|
||||||
internal Cube(IEnumerable<SKColor> 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
|
|
||||||
private const float targetDarkLuma = 0.26f;
|
private const float targetDarkLuma = 0.26f;
|
||||||
private const float maxDarkLuma = 0.45f;
|
private const float maxDarkLuma = 0.45f;
|
||||||
private const float minLightLuma = 0.55f;
|
private const float minLightLuma = 0.55f;
|
||||||
@ -111,7 +45,9 @@ namespace Artemis.Core.Services
|
|||||||
private const float minVibrantSaturation = 0.35f;
|
private const float minVibrantSaturation = 0.35f;
|
||||||
private const float weightSaturation = 3f;
|
private const float weightSaturation = 3f;
|
||||||
private const float weightLuma = 5f;
|
private const float weightLuma = 5f;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false)
|
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false)
|
||||||
{
|
{
|
||||||
(float targetLuma, float minLuma, float maxLuma, float targetSaturation, float minSaturation, float maxSaturation) = type switch
|
(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;
|
return totalValue / totalweight;
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
12
src/Artemis.Core/Services/ColorQuantizer/ColorType.cs
Normal file
12
src/Artemis.Core/Services/ColorQuantizer/ColorType.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Artemis.Core.Services
|
||||||
|
{
|
||||||
|
public enum ColorType
|
||||||
|
{
|
||||||
|
Vibrant,
|
||||||
|
LightVibrant,
|
||||||
|
DarkVibrant,
|
||||||
|
Muted,
|
||||||
|
LightMuted,
|
||||||
|
DarkMuted
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services.Interfaces
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service providing a pallette of colors in a bitmap based on vibrant.js
|
||||||
|
/// </summary>
|
||||||
|
public interface IColorQuantizerService : IArtemisService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reduces an <see cref="IEnumerable{SKColor}"/> to a given amount of relevant colors. Based on the Median Cut algorithm
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="colors">The colors to quantize.</param>
|
||||||
|
/// <param name="amount">The number of colors that should be calculated. Must be a power of two.</param>
|
||||||
|
/// <returns>The quantized colors.</returns>
|
||||||
|
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds colors with certain characteristics in a given <see cref="IEnumerable{SKColor}"/>.<para />
|
||||||
|
/// Vibrant variants are more saturated, while Muted colors are less.<para />
|
||||||
|
/// Light and Dark colors have higher and lower lightness values, respectively.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="colors">The colors to find the variations in</param>
|
||||||
|
/// <param name="type">Which type of color to find</param>
|
||||||
|
/// <param name="ignoreLimits">Ignore hard limits on whether a color is considered for each category. Result may be <see cref="SKColor.Empty"/> if this is true</param>
|
||||||
|
/// <returns>The color found</returns>
|
||||||
|
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
using SkiaSharp;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Services.Interfaces
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A service providing a pallette of colors in a bitmap based on vibrant.js
|
|
||||||
/// </summary>
|
|
||||||
public interface IColorQuantizerService : IArtemisService
|
|
||||||
{
|
|
||||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount);
|
|
||||||
|
|
||||||
public SKColor FindColorVariation(IEnumerable<SKColor> colors, ColorType type, bool ignoreLimits = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user