mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +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 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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public class ColorQuantizerService : IColorQuantizerService
|
||||
{
|
||||
#region Quantizer
|
||||
/// <inheritdoc />
|
||||
public SKColor[] Quantize(IEnumerable<SKColor> colors, int amount)
|
||||
{
|
||||
if ((amount & (amount - 1)) != 0)
|
||||
throw new ArgumentException("Must be power of two", nameof(amount));
|
||||
|
||||
Queue<Cube> cubes = new Queue<Cube>(amount);
|
||||
cubes.Enqueue(new Cube(colors));
|
||||
Queue<ColorCube> cubes = new Queue<ColorCube>(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<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
|
||||
#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
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
@ -157,6 +93,5 @@ namespace Artemis.Core.Services
|
||||
|
||||
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