diff --git a/README.md b/README.md index f3af515..efe61b1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + # Dark Colors @@ -88,4 +88,30 @@ var twoColorsCombined = ColorBlender.Combine(baseColor, colorToAdd); var baseColor = Color.FromArgb(0, 0, 0); var colorToAdd = Color.FromArgb(125, 55, 13); var twoColorsCombined = baseColor.Combine(colorToAdd); -``` \ No newline at end of file +``` + +## Color analyzer + +⚠️ Warning: this feature is pretty slow at the moment. It can process roughly one mega pixel per second, so it's not recommended to use it on large images. + +Find dominant color in an image. + + + +### Basic usage +```csharp +//array of pixels that represents an image +Color[] pixels; +//returns a list of dominant color candidates, ordered by probability +List candidates = ColorAnalyzer.FindDominantColors(pixels); +``` + +### Advanced (with configuration) +```csharp +Color[] pixels; +List candidates = ColorAnalyzer.FindDominantColors(pixels, options => +{ + options.MinSaturation = 0.4f, + options.MinSpaceCoverage = 0.05f, + options.ColorGrouping = 0.3f +}); \ No newline at end of file diff --git a/assets/sample_screenshot_analyzer.png b/assets/sample_screenshot_analyzer.png new file mode 100644 index 0000000..3281f73 Binary files /dev/null and b/assets/sample_screenshot_analyzer.png differ diff --git a/sample/SampleApp/BitmapUtils.cs b/sample/SampleApp/BitmapUtils.cs new file mode 100644 index 0000000..ec768f7 --- /dev/null +++ b/sample/SampleApp/BitmapUtils.cs @@ -0,0 +1,32 @@ +using SkiaSharp.Views.Desktop; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Media.Imaging; + +namespace SampleApp; + +public static class BitmapUtils +{ + public static Color[] GetPixels(Bitmap bitmap) + { + return bitmap.ToSKBitmap().Pixels + .Select(x => x.ToDrawingColor()) + .ToArray(); + } + + public static BitmapImage BitmapToImageSource(Bitmap bitmap) + { + using MemoryStream memory = new MemoryStream(); + + bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp); + memory.Position = 0; + BitmapImage bitmapimage = new BitmapImage(); + bitmapimage.BeginInit(); + bitmapimage.StreamSource = memory; + bitmapimage.CacheOption = BitmapCacheOption.OnLoad; + bitmapimage.EndInit(); + + return bitmapimage; + } +} \ No newline at end of file diff --git a/sample/SampleApp/ColorAnalyzerOptionsExample.cs b/sample/SampleApp/ColorAnalyzerOptionsExample.cs new file mode 100644 index 0000000..cc63bff --- /dev/null +++ b/sample/SampleApp/ColorAnalyzerOptionsExample.cs @@ -0,0 +1,58 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using DarkColors; +using System.Collections.ObjectModel; +using System.Drawing; +using System.Linq; +using System.Windows.Media.Imaging; + +namespace SampleApp; + +public partial class ColorAnalyzerExample : ObservableObject +{ + public class DemoImage + { + public BitmapImage? BitmapSource { get; set; } + public Color[] Pixels { get; set; } + public ObservableCollection Candidates { get; set; } = new(); + } + + public DominantColorAnalyzerOptions Options { get; } = new() + { + MaxCandidateCount = 5 + }; + + public ObservableCollection Images { get; } = new(); + + public ColorAnalyzerExample(Bitmap[] bitmaps) + { + var images = bitmaps.Select(x => new DemoImage + { + BitmapSource = BitmapUtils.BitmapToImageSource(x), + Pixels = BitmapUtils.GetPixels(x) + }); + + foreach (var image in images) + { + Images.Add(image); + } + + UpdateCandidates(); + } + + [RelayCommand] + private void UpdateCandidates() + { + foreach (var result in Images) + { + result.Candidates.Clear(); + + var candidates = ColorAnalyzer.FindDominantColors(result.Pixels, Options); + + foreach (var candidate in candidates) + { + result.Candidates.Add(candidate); + } + } + } +} \ No newline at end of file diff --git a/sample/SampleApp/ColorBlendingExample.cs b/sample/SampleApp/ColorBlendingExample.cs index bdb9a7a..871713f 100644 --- a/sample/SampleApp/ColorBlendingExample.cs +++ b/sample/SampleApp/ColorBlendingExample.cs @@ -17,4 +17,4 @@ public ColorBlendingExample(string Title, Color baseColor, params ColorLayer[] l Layers = layers; ResultColor = ColorBlender.Combine(baseColor, layers); } -} +} \ No newline at end of file diff --git a/sample/SampleApp/MainViewModel.cs b/sample/SampleApp/MainViewModel.cs index 8229202..4701d59 100644 --- a/sample/SampleApp/MainViewModel.cs +++ b/sample/SampleApp/MainViewModel.cs @@ -1,4 +1,5 @@ using DarkColors; +using SampleApp.Properties; using System.Collections.Generic; using System.Drawing; @@ -6,29 +7,34 @@ namespace SampleApp; public class MainViewModel { - public List Examples { get; } = new(); + public List ColorBlendingExamples { get; } = new(); + public ColorAnalyzerExample ColorAnalyzerExample { get; } public MainViewModel() { - Examples.Add(new ColorBlendingExample("A bit of gray", Hex("#000"), new ColorLayer(Hex("#fff"), 25))); - Examples.Add(new ColorBlendingExample("Fifty shades", Hex("#000"), new ColorLayer(Hex("#fff"), 50))); - Examples.Add(new ColorBlendingExample("Bright green", Hex("#007d40"), new ColorLayer(Hex("#fff"), 75))); + ColorBlendingExamples.Add(new ColorBlendingExample("A bit of gray", Hex("#000"), new ColorLayer(Hex("#fff"), 25))); + ColorBlendingExamples.Add(new ColorBlendingExample("Fifty shades", Hex("#000"), new ColorLayer(Hex("#fff"), 50))); + ColorBlendingExamples.Add(new ColorBlendingExample("Bright green", Hex("#007d40"), new ColorLayer(Hex("#fff"), 75))); - Examples.Add(new ColorBlendingExample("Is it blue or purple?", Hex("#000"), new ColorLayer(Hex("#4056F4"), 60))); + ColorBlendingExamples.Add(new ColorBlendingExample("Is it blue or purple?", Hex("#000"), new ColorLayer(Hex("#4056F4"), 60))); - Examples.Add(new ColorBlendingExample("More than two colors", Hex("#007d40"), new ColorLayer[] + ColorBlendingExamples.Add(new ColorBlendingExample("More than two colors", Hex("#007d40"), new ColorLayer[] { new ColorLayer(Hex("#4056F4"), 42), new ColorLayer(Hex("#B1740F"), 28) })); - Examples.Add(new ColorBlendingExample("Both alpha transparency and amount percentage used", Hex("#000"), new ColorLayer(Hex("#804056F4"), 55))); + ColorBlendingExamples.Add(new ColorBlendingExample("Both alpha transparency and amount percentage used", Hex("#000"), new ColorLayer(Hex("#804056F4"), 55))); - Examples.Add(new ColorBlendingExample("Combo - both alpha transparency and amount percentage used for multiple colors", Hex("#007d40"), new ColorLayer[] + ColorBlendingExamples.Add(new ColorBlendingExample("Combo - both alpha transparency and amount percentage used for multiple colors", Hex("#007d40"), new ColorLayer[] { new ColorLayer(Hex("#804056F4"), 71), new ColorLayer(Hex("#5CB1740F"), 20) })); + + var demoBitmaps = new[] { Resources.vildhjarta, Resources.rivers, Resources.abovebelow, Resources.greylotus, Resources.currents, Resources.dali, Resources.metallica, Resources.northlane }; + + ColorAnalyzerExample = new(demoBitmaps); } private static Color Hex(string hex) diff --git a/sample/SampleApp/MainWindow.xaml b/sample/SampleApp/MainWindow.xaml index f8a924a..930c742 100644 --- a/sample/SampleApp/MainWindow.xaml +++ b/sample/SampleApp/MainWindow.xaml @@ -5,6 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:SampleApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:skiaWpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" Title="Dark Colors Demo" Width="800" Height="450" @@ -15,139 +16,292 @@ - - - - - - - - - + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - + + - + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +