public HistogramRgb () : base (3, 256) { visualColors = new ColorBgra[]{ ColorBgra.Blue, ColorBgra.Green, ColorBgra.Red }; }
public abstract ColorBgra Apply (ColorBgra lhs, ColorBgra rhs);
public override ColorBgra GetPercentileColor(float fraction) { int[] perc = GetPercentile(fraction); return(ColorBgra.FromBgr((byte)(perc[0]), (byte)(perc[1]), (byte)(perc[2]))); }
/// <summary> /// Blends the colors based on the given weight values. /// </summary> /// <param name="c">The array of color values.</param> /// <param name="w">The array of weight values.</param> /// <returns> /// Each color will be blended in proportionally to its weight value respective to /// the total summation of the weight values. /// </returns> /// <remarks> /// "WAIP" stands for "weights, floating-point"</remarks> public static ColorBgra BlendColorsWFP(ColorBgra[] c, double[] w) { if (c.Length != w.Length) { throw new ArgumentException("c.Length != w.Length"); } if (c.Length == 0) { return ColorBgra.FromUInt32(0); } double wsum = 0; double asum = 0; for (int i = 0; i < w.Length; ++i) { wsum += w[i]; asum += (double)c[i].A * w[i]; } double a = asum / wsum; double aMultWsum = a * wsum; double b; double g; double r; if (asum == 0) { b = 0; g = 0; r = 0; } else { b = 0; g = 0; r = 0; for (int i = 0; i < c.Length; ++i) { b += (double)c[i].A * c[i].B * w[i]; g += (double)c[i].A * c[i].G * w[i]; r += (double)c[i].A * c[i].R * w[i]; } b /= aMultWsum; g /= aMultWsum; r /= aMultWsum; } return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); }
/// <summary> /// Constructs a new ColorBgra instance from the values in the given Color instance. /// </summary> // public static ColorBgra FromColor(Color c) // { // return FromBgra(c.B, c.G, c.R, c.A); // } /// <summary> /// Converts this ColorBgra instance to a Color instance. /// </summary> // public Color ToColor() // { // return Color.FromArgb(A, R, G, B); // } /// <summary> /// Smoothly blends between two colors. /// </summary> public static ColorBgra Blend (ColorBgra ca, ColorBgra cb, byte cbAlpha) { uint caA = (uint)Utility.FastScaleByteByByte ((byte)(255 - cbAlpha), ca.A); uint cbA = (uint)Utility.FastScaleByteByByte (cbAlpha, cb.A); uint cbAT = caA + cbA; uint r; uint g; uint b; if (cbAT == 0) { r = 0; g = 0; b = 0; } else { r = ((ca.R * caA) + (cb.R * cbA)) / cbAT; g = ((ca.G * caA) + (cb.G * cbA)) / cbAT; b = ((ca.B * caA) + (cb.B * cbA)) / cbAT; } return ColorBgra.FromBgra ((byte)b, (byte)g, (byte)r, (byte)cbAT); }
/// <summary> /// Blends four colors together based on the given weight values. /// </summary> /// <returns>The blended color.</returns> /// <remarks> /// The weights should be 16-bit fixed point numbers that add up to 65536 ("1.0"). /// 4W16IP means "4 colors, weights, 16-bit integer precision" /// </remarks> public static ColorBgra BlendColors4W16IP(ColorBgra c1, uint w1, ColorBgra c2, uint w2, ColorBgra c3, uint w3, ColorBgra c4, uint w4) { #if DEBUG if ((w1 + w2 + w3 + w4) != 65536) { throw new ArgumentOutOfRangeException("w1 + w2 + w3 + w4 must equal 65536!"); } #endif const uint ww = 32768; uint af = (c1.A * w1) + (c2.A * w2) + (c3.A * w3) + (c4.A * w4); uint a = (af + ww) >> 16; uint b; uint g; uint r; if (a == 0) { b = 0; g = 0; r = 0; } else { b = (uint)((((long)c1.A * c1.B * w1) + ((long)c2.A * c2.B * w2) + ((long)c3.A * c3.B * w3) + ((long)c4.A * c4.B * w4)) / af); g = (uint)((((long)c1.A * c1.G * w1) + ((long)c2.A * c2.G * w2) + ((long)c3.A * c3.G * w3) + ((long)c4.A * c4.G * w4)) / af); r = (uint)((((long)c1.A * c1.R * w1) + ((long)c2.A * c2.R * w2) + ((long)c3.A * c3.R * w3) + ((long)c4.A * c4.R * w4)) / af); } return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); }
public abstract ColorBgra Apply(ColorBgra lhs, ColorBgra rhs);
/// <summary> /// Creates a new ColorBgra instance with the given color and alpha values. /// </summary> public static ColorBgra FromBgra(byte b, byte g, byte r, byte a) { ColorBgra color = new ColorBgra(); color.Bgra = BgraToUInt32(b, g, r, a); return color; }
/// <summary> /// Performs the actual work of rendering an effect. This overload represent a single pixel of the image. /// </summary> /// <param name="color">The color of the source surface pixel.</param> /// <returns>The color to be used for the destination pixel.</returns> protected virtual ColorBgra Render (ColorBgra color) { return color; }
public static Cairo.Color ToCairoColor(this ColorBgra color) { return(new Cairo.Color(color.R / 255d, color.G / 255d, color.B / 255d, color.A / 255d)); }
/// <summary> /// Performs the actual work of rendering an effect. This overload represent a single line of the image. Do not call base.Render (). /// </summary> /// <param name="src">The source surface. DO NOT MODIFY.</param> /// <param name="dst">The destination surface.</param> /// <param name="length">The number of pixels to render.</param> protected unsafe virtual void Render (ColorBgra* src, ColorBgra* dst, int length) { while (length > 0) { *dst = Render (*src); ++dst; ++src; --length; } }
protected unsafe virtual void RenderLoop (ISurface surface, Rectangle roi, CancellationToken token, IRenderProgress progress) { var dst = new ColorBgra[surface.Height * surface.Width]; fixed (ColorBgra* dst_ptr = dst) { var dst_wrap = new ColorBgraArrayWrapper (dst_ptr, surface.Width, surface.Height); surface.BeginUpdate (); dst_wrap.BeginUpdate (); OnBeginRender (surface, dst_wrap, roi); var completed_lines = new bool[roi.Height]; var last_completed_index = 0; if (Settings.SingleThreaded || roi.Height <= 1) { for (var y = roi.Y; y <= roi.Bottom; ++y) { if (token.IsCancellationRequested) return; RenderLine (surface, dst_wrap, new Rectangle (roi.X, y, roi.Width, 1)); completed_lines[y - roi.Top] = true; if (progress != null) { var last_y = FindLastCompletedLine (completed_lines, last_completed_index); last_completed_index = last_y; progress.CompletedRoi = new Rectangle (roi.X, roi.Y, roi.Width, last_y); progress.PercentComplete = (float)last_y / (float)roi.Height; } } } else { ParallelExtensions.OrderedFor (roi.Y, roi.Bottom + 1, token, (y) => { RenderLine (surface, dst_wrap, new Rectangle (roi.X, y, roi.Width, 1)); completed_lines[y - roi.Top] = true; if (progress != null) { var last_y = FindLastCompletedLine (completed_lines, last_completed_index); last_completed_index = last_y; progress.CompletedRoi = new Rectangle (roi.X, roi.Y, roi.Width, last_y); progress.PercentComplete = (float)last_y / (float)roi.Height; } }); } // Copy the result from our temp destination back into the source var op = new IdentityOp (); op.ApplyAsync (dst_wrap, surface, token).Wait (); surface.EndUpdate (); dst_wrap.EndUpdate (); } }
public void SetFromLeveledHistogram(HistogramRgb inputHistogram, LevelOp 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 static ColorBgra ToPintaColorBgra (this Cairo.Color color) { var c = new ColorBgra (); c.R = (byte)(color.R * 255); c.G = (byte)(color.G * 255); c.B = (byte)(color.B * 255); c.A = (byte)(color.A * 255); return c; }
public unsafe virtual void Apply (ColorBgra* ptr, int length) { unsafe { while (length > 0) { *ptr = Apply (*ptr); ++ptr; --length; } } }
public static unsafe ColorBgra GetBilinearSampleWrapped (ISurface src, ColorBgra* srcDataPtr, int srcWidth, int srcHeight, float x, float y) { if (!Utility.IsNumber (x) || !Utility.IsNumber (y)) return ColorBgra.Transparent; float u = x; float v = y; unchecked { int iu = (int)Math.Floor (u); uint sxfrac = (uint)(256 * (u - (float)iu)); uint sxfracinv = 256 - sxfrac; int iv = (int)Math.Floor (v); uint syfrac = (uint)(256 * (v - (float)iv)); uint syfracinv = 256 - syfrac; uint wul = (uint)(sxfracinv * syfracinv); uint wur = (uint)(sxfrac * syfracinv); uint wll = (uint)(sxfracinv * syfrac); uint wlr = (uint)(sxfrac * syfrac); int sx = iu; if (sx < 0) sx = (srcWidth - 1) + ((sx + 1) % srcWidth); else if (sx > (srcWidth - 1)) sx = sx % srcWidth; int sy = iv; if (sy < 0) sy = (srcHeight - 1) + ((sy + 1) % srcHeight); else if (sy > (srcHeight - 1)) sy = sy % srcHeight; int sleft = sx; int sright; if (sleft == (srcWidth - 1)) sright = 0; else sright = sleft + 1; int stop = sy; int sbottom; if (stop == (srcHeight - 1)) sbottom = 0; else sbottom = stop + 1; ColorBgra cul = src.GetPoint (sleft, stop); ColorBgra cur = src.GetPoint (sright, stop); ColorBgra cll = src.GetPoint (sleft, sbottom); ColorBgra clr = src.GetPoint (sright, sbottom); ColorBgra c = ColorBgra.BlendColors4W16IP (cul, wul, cur, wur, cll, wll, clr, wlr); return c; } }
public abstract ColorBgra Apply (ColorBgra color);
/// <summary> /// Constructs a new ColorBgra instance with the given 32-bit value. /// </summary> public static ColorBgra FromUInt32(UInt32 bgra) { ColorBgra color = new ColorBgra(); color.Bgra = bgra; return color; }
public unsafe ColorBgraArrayWrapper (ColorBgra* data, int width, int height) { data_ptr = data; this.height = height; this.width = width; }
/// <summary> /// Linearly interpolates between two color values. /// </summary> /// <param name="from">The color value that represents 0 on the lerp number line.</param> /// <param name="to">The color value that represents 1 on the lerp number line.</param> /// <param name="frac">A value in the range [0, 1].</param> /// <remarks> /// This method does a simple lerp on each color value and on the alpha channel. It does /// not properly take into account the alpha channel's effect on color blending. /// </remarks> public static ColorBgra Lerp(ColorBgra from, ColorBgra to, double frac) { ColorBgra ret = new ColorBgra(); ret.B = (byte)ClampToByte(Lerp(from.B, to.B, frac)); ret.G = (byte)ClampToByte(Lerp(from.G, to.G, frac)); ret.R = (byte)ClampToByte(Lerp(from.R, to.R, frac)); ret.A = (byte)ClampToByte(Lerp(from.A, to.A, frac)); return ret; }
public unsafe abstract void Apply (ColorBgra* src, ColorBgra* dst, int length);
/// <summary> /// Blends the colors based on the given weight values. /// </summary> /// <param name="c">The array of color values.</param> /// <param name="w">The array of weight values.</param> /// <returns> /// The weights should be fixed point numbers. /// The total summation of the weight values will be treated as "1.0". /// Each color will be blended in proportionally to its weight value respective to /// the total summation of the weight values. /// </returns> /// <remarks> /// "WAIP" stands for "weights, arbitrary integer precision"</remarks> public static ColorBgra BlendColorsWAIP(ColorBgra[] c, uint[] w) { if (c.Length != w.Length) { throw new ArgumentException("c.Length != w.Length"); } if (c.Length == 0) { return ColorBgra.FromUInt32(0); } long wsum = 0; long asum = 0; for (int i = 0; i < w.Length; ++i) { wsum += w[i]; asum += c[i].A * w[i]; } uint a = (uint)((asum + (wsum >> 1)) / wsum); long b; long g; long r; if (a == 0) { b = 0; g = 0; r = 0; } else { b = 0; g = 0; r = 0; for (int i = 0; i < c.Length; ++i) { b += (long)c[i].A * c[i].B * w[i]; g += (long)c[i].A * c[i].G * w[i]; r += (long)c[i].A * c[i].R * w[i]; } b /= asum; g /= asum; r /= asum; } return ColorBgra.FromUInt32((uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24)); }
public virtual void Apply (ColorBgra* lhs, ColorBgra* rhs, ColorBgra* dst, int length) { unsafe { while (length > 0) { *dst = Apply (*lhs, *rhs); ++dst; ++lhs; ++rhs; --length; } } }
/// <summary> /// Smoothly blends the given colors together, assuming equal weighting for each one. /// </summary> /// <param name="colors"></param> /// <param name="colorCount"></param> /// <returns></returns> public unsafe static ColorBgra Blend(ColorBgra* colors, int count) { if (count < 0) { throw new ArgumentOutOfRangeException("count must be 0 or greater"); } if (count == 0) { return ColorBgra.Transparent; } ulong aSum = 0; for (int i = 0; i < count; ++i) { aSum += (ulong)colors[i].A; } byte b = 0; byte g = 0; byte r = 0; byte a = (byte)(aSum / (ulong)count); if (aSum != 0) { ulong bSum = 0; ulong gSum = 0; ulong rSum = 0; for (int i = 0; i < count; ++i) { bSum += (ulong)(colors[i].A * colors[i].B); gSum += (ulong)(colors[i].A * colors[i].G); rSum += (ulong)(colors[i].A * colors[i].R); } b = (byte)(bSum / aSum); g = (byte)(gSum / aSum); r = (byte)(rSum / aSum); } return ColorBgra.FromBgra(b, g, r, a); }
public unsafe override void Apply (ColorBgra* src, ColorBgra* dst, int length) { unsafe { while (length > 0) { *dst = Apply (*dst, *src); ++dst; ++src; --length; } } }
public static unsafe SurfaceDiff Create (ISurface original, ISurface updated_surf, bool force = false) { if (original.Width != updated_surf.Width || original.Height != updated_surf.Height) { // If the surface changed size, only throw an error if the user forced the use of a diff. if (force) { throw new InvalidOperationException ("SurfaceDiff requires surfaces to be same size."); } else { return null; } } // Cache some pinvokes var orig_width = original.Width; var orig_height = original.Height; #if DEBUG_DIFF Console.WriteLine ("Original surface size: {0}x{1}", orig_width, orig_height); System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); timer.Start(); #endif // STEP 1 - Find the bounds of the changed pixels. var orig_ptr = (int*)original.GetRowAddress (0); var updated_ptr = (int*)updated_surf.GetRowAddress (0); DiffBounds diff_bounds = new DiffBounds (orig_width, orig_height); object diff_bounds_lock = new Object(); // Split up the work among several threads, each of which processes one row at a time // and updates the bounds of the changed pixels it has seen so far. At the end, the // results from each thread are merged together to find the overall bounds of the changed // pixels. Parallel.For<DiffBounds>(0, orig_height, () => new DiffBounds (orig_width, orig_height), (row, loop, my_bounds) => { var offset = row * orig_width; var orig = orig_ptr + offset; var updated = updated_ptr + offset; bool change_in_row = false; for (int i = 0; i < orig_width; ++i) { if (*(orig++) != *(updated++)) { change_in_row = true; my_bounds.left = System.Math.Min(my_bounds.left, i); my_bounds.right = System.Math.Max(my_bounds.right, i); } } if (change_in_row) { my_bounds.top = System.Math.Min(my_bounds.top, row); my_bounds.bottom = System.Math.Max(my_bounds.bottom, row); } return my_bounds; }, (my_bounds) => { lock (diff_bounds_lock) { diff_bounds.Merge (my_bounds); } return; }); var bounds = new Rectangle (diff_bounds.left, diff_bounds.top, diff_bounds.right - diff_bounds.left + 1, diff_bounds.bottom - diff_bounds.top + 1); #if DEBUG_DIFF Console.WriteLine ("Truncated surface size: {0}x{1}", bounds.Width, bounds.Height); #endif // STEP 2 - Create a bitarray of whether each pixel in the bounds has changed, and count // how many changed pixels we need to store. var bitmask = new BitArray (bounds.Width * bounds.Height); int index = 0; int num_changed = 0; int bottom = bounds.Bottom; int right = bounds.Right; int bounds_x = bounds.X; int bounds_y = bounds.Y; for (int y = bounds_y; y <= bottom; ++y) { var offset = y * orig_width; var updated = updated_ptr + offset + bounds_x; var orig = orig_ptr + offset + bounds_x; for (int x = bounds_x; x <= right; ++x) { bool changed = *(orig++) != *(updated++); bitmask[index++] = changed; if (changed) { num_changed++; } } } var savings = 100 - (float)num_changed / (float)(orig_width * orig_height) * 100; #if DEBUG_DIFF Console.WriteLine ("Compressed bitmask: {0}/{1} = {2}%", num_changed, orig_height * orig_width, 100 - savings); #endif if (!force && savings < MINIMUM_SAVINGS_PERCENT) { #if DEBUG_DIFF Console.WriteLine ("Savings too small, returning null"); #endif return null; } // Store the old pixels. var pixels = new ColorBgra[num_changed]; var new_ptr = (ColorBgra*)original.GetRowAddress (0); int mask_index = 0; fixed (ColorBgra* fixed_ptr = pixels) { var pixel_ptr = fixed_ptr; for (int y = bounds_y; y <= bottom; ++y) { var new_pixel_ptr = new_ptr + bounds_x + y * orig_width; for (int x = bounds_x; x <= right; ++x) { if (bitmask[mask_index++]) { *pixel_ptr++ = *new_pixel_ptr; } new_pixel_ptr++; } } } #if DEBUG_DIFF timer.Stop(); System.Console.WriteLine("SurfaceDiff time: " + timer.ElapsedMilliseconds); #endif return new SurfaceDiff (bitmask, bounds, pixels); }
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))); }