///////////////////////////////////////////////////////////////////////////////// // Paint.NET // // Copyright (C) Rick Brewster, Tom Jackson, and past contributors. // // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // // See license-pdn.txt for full licensing and attribution details. // ///////////////////////////////////////////////////////////////////////////////// using System; using Cairo; namespace Pinta.Core { /// /// Histogram is used to calculate a histogram for a surface (in a selection, /// if desired). This can then be used to retrieve percentile, average, peak, /// and distribution information. /// public sealed class HistogramRgb : Histogram { public HistogramRgb() : base(3, 256) { visualColors = new ColorBgra[]{ ColorBgra.Blue, ColorBgra.Green, ColorBgra.Red }; } public override ColorBgra GetMeanColor() { float[] mean = GetMean(); return ColorBgra.FromBgr((byte)(mean[0] + 0.5f), (byte)(mean[1] + 0.5f), (byte)(mean[2] + 0.5f)); } public override ColorBgra GetPercentileColor(float fraction) { int[] perc = GetPercentile(fraction); return ColorBgra.FromBgr((byte)(perc[0]), (byte)(perc[1]), (byte)(perc[2])); } protected override unsafe void AddSurfaceRectangleToHistogram(ImageSurface surface, Gdk.Rectangle rect) { long[] histogramB = histogram[0]; long[] histogramG = histogram[1]; long[] histogramR = histogram[2]; int rect_right = rect.Right; for (int y = rect.Y; y < rect.Bottom; ++y) { ColorBgra* ptr = surface.GetPointAddressUnchecked(rect.X, y); for (int x = rect.X; x < rect_right; ++x) { ++histogramB[ptr->B]; ++histogramG[ptr->G]; ++histogramR[ptr->R]; ++ptr; } } } public void SetFromLeveledHistogram(HistogramRgb inputHistogram, UnaryPixelOps.Level upo) { if (inputHistogram == null || upo == null) { return; } Clear(); float[] before = new float[3]; float[] slopes = new float[3]; for (int c = 0; c < 3; c++) { long[] channelHistogramOutput = histogram[c]; long[] channelHistogramInput = inputHistogram.histogram[c]; for (int v = 0; v <= 255; v++) { ColorBgra after = ColorBgra.FromBgr((byte)v, (byte)v, (byte)v); upo.UnApply(after, before, slopes); if (after[c] > upo.ColorOutHigh[c] || after[c] < upo.ColorOutLow[c] || (int)Math.Floor(before[c]) < 0 || (int)Math.Ceiling(before[c]) > 255 || float.IsNaN(before[c])) { channelHistogramOutput[v] = 0; } else if (before[c] <= upo.ColorInLow[c]) { channelHistogramOutput[v] = 0; for (int i = 0; i <= upo.ColorInLow[c]; i++) { channelHistogramOutput[v] += channelHistogramInput[i]; } } else if (before[c] >= upo.ColorInHigh[c]) { channelHistogramOutput[v] = 0; for (int i = upo.ColorInHigh[c]; i < 256; i++) { channelHistogramOutput[v] += channelHistogramInput[i]; } } else { channelHistogramOutput[v] = (int)(slopes[c] * Utility.Lerp( channelHistogramInput[(int)Math.Floor(before[c])], channelHistogramInput[(int)Math.Ceiling(before[c])], before[c] - Math.Floor(before[c]))); } } } OnHistogramUpdated(); } public UnaryPixelOps.Level MakeLevelsAuto() { ColorBgra lo = GetPercentileColor(0.005f); ColorBgra md = GetMeanColor(); ColorBgra hi = GetPercentileColor(0.995f); return UnaryPixelOps.Level.AutoFromLoMdHi(lo, md, hi); } } }