/// <summary> /// Allows you to find the bounding box for a "region" that is described as an /// array of bounding boxes. /// </summary> /// <param name="rectsF">The "region" you want to find a bounding box for.</param> /// <returns>A RectangleF structure that surrounds the Region.</returns> public static Rectangle GetRegionBounds (Rectangle[] rects, int startIndex, int length) { if (rects.Length == 0) { return Rectangle.Empty; } int left = rects[startIndex].Left; int top = rects[startIndex].Top; int right = rects[startIndex].Right; int bottom = rects[startIndex].Bottom; for (int i = startIndex + 1; i < startIndex + length; ++i) { Rectangle rect = rects[i]; if (rect.Left < left) { left = rect.Left; } if (rect.Top < top) { top = rect.Top; } if (rect.Right > right) { right = rect.Right; } if (rect.Bottom > bottom) { bottom = rect.Bottom; } } return Rectangle.FromLTRB (left, top, right, bottom); }
public static Rectangle Intersect (Rectangle a, Rectangle b) { return Rectangle.FromLTRB ( Math.Max (a.Left, b.Left), Math.Max (a.Top, b.Top), Math.Min (a.Right, b.Right), Math.Min (a.Bottom, b.Bottom)); }
public void Intersect (Rectangle r) { var new_r = Rectangle.Intersect (this, r); X = new_r.X; Y = new_r.Y; Width = new_r.Width; Height = new_r.Height; }
protected override unsafe void AddSurfaceRectangleToHistogram (ISurface surface, 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.GetPointAddress (rect.X, y); for (int x = rect.X; x <= rect_right; ++x) { ++histogramB[ptr->B]; ++histogramG[ptr->G]; ++histogramR[ptr->R]; ++ptr; } } }
public Task ApplyAsync (ISurface src, ISurface dst, Rectangle roi) { return ApplyAsync (src, dst, dst.Bounds, CancellationToken.None); }
protected void ApplyLoop (ISurface lhs, ISurface rhs, ISurface dst, Rectangle roi, CancellationToken token, IRenderProgress progress) { 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; var dstPtr = dst.GetRowAddress (y); var lhsPtr = lhs.GetRowAddress (y); var rhsPtr = rhs.GetRowAddress (y); Apply (lhsPtr, rhsPtr, dstPtr, roi.Width); 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) => { var dstPtr = dst.GetRowAddress (y); var lhsPtr = lhs.GetRowAddress (y); var rhsPtr = rhs.GetRowAddress (y); Apply (lhsPtr, rhsPtr, dstPtr, roi.Width); 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; } }); } }
public void Apply (ISurface lhs, ISurface rhs, ISurface dst, Rectangle roi) { ApplyLoop (lhs, rhs, dst, roi, CancellationToken.None, null); }
/// <summary> /// Performs the actual work of rendering an effect. 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="roi">A rectangle of interest (roi) specifying the area to modify. Only these areas should be modified</param> protected unsafe virtual void RenderLine (ISurface src, ISurface dst, Rectangle roi) { var srcPtr = src.GetPointAddress (roi.X, roi.Y); var dstPtr = dst.GetPointAddress (roi.X, roi.Y); Render (srcPtr, dstPtr, roi.Width); }
public Task ApplyAsync (ISurface lhs, ISurface rhs, ISurface dst, Rectangle roi, CancellationToken token) { return Task.Factory.StartNew (() => ApplyLoop (lhs, rhs, dst, dst.Bounds, token, null)); }
public void Set (Rectangle rect, bool newValue) { for (int y = rect.Y; y <= rect.Bottom; ++y) { for (int x = rect.X; x <= rect.Right; ++x) { Set (x, y, newValue); } } }
public Task RenderAsync (ISurface src, ISurface dst, Rectangle roi, CancellationToken token, IRenderProgress progress) { return Task.Factory.StartNew (() => RenderLoop (src, dst, roi, token, progress)); }
public Task ApplyAsync (ISurface src, ISurface dst, Rectangle roi, CancellationToken token) { return Task.Factory.StartNew (() => ApplyLoop (src, dst, roi, token, null)); }
public void Apply (ISurface surface, Rectangle roi) { ApplyLoop (surface, roi, CancellationToken.None, null); }
protected abstract void AddSurfaceRectangleToHistogram (ISurface surface, Rectangle rect);
//public void UpdateHistogram(Surface surface) //{ // Clear(); // AddSurfaceRectangleToHistogram(surface, surface.Bounds); // OnHistogramUpdated(); //} public void UpdateHistogram (ISurface surface, Rectangle rect) { Clear (); AddSurfaceRectangleToHistogram (surface, rect); OnHistogramUpdated (); }
/// <summary> /// Render the effect from the source surface to the destination surface. /// </summary> /// <param name="src">The source surface.</param> /// <param name="dst">The destination surface.</param> /// <param name="roi">A rectangle of interest (roi) specifying the area(s) to modify. Only these areas should be modified.</param> public void Render (ISurface src, ISurface dst, Rectangle roi) { RenderLoop (src, dst, roi, CancellationToken.None, null); }
/// <summary> /// Render the effect on the specified surface within the specified rectangle of interest. /// </summary> /// <param name="surface">Surface to use a the source and destination.</param> /// <param name="roi">A rectangle of interest (roi) specifying the area(s) to modify. Only these areas should be modified.</param> public void Render (ISurface surface, Rectangle roi) { RenderLoop (surface, roi, CancellationToken.None, null); }
protected virtual void OnBeginRender (ISurface src, ISurface dst, Rectangle roi) { }
public Task ApplyAsync (ISurface src, ISurface dst, Rectangle roi, CancellationToken token, IRenderProgress progress) { return Task.Factory.StartNew (() => ApplyLoop (src, dst, dst.Bounds, token, progress)); }
protected virtual void RenderLoop (ISurface src, ISurface dst, Rectangle roi, CancellationToken token, IRenderProgress progress) { src.BeginUpdate (); dst.BeginUpdate (); OnBeginRender (src, dst, 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 (src, dst, 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 (src, dst, 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; } }); } src.EndUpdate (); dst.EndUpdate (); }
public Rectangle UnscaleRectangle (Rectangle rect) { return new Rectangle (UnscalePoint (rect.Location), UnscaleSize (rect.Size)); }
private SurfaceDiff (BitArray bitmask, Rectangle bounds, Pinta.ImageManipulation.ColorBgra[] pixels) { this.bitmask = bitmask; this.bounds = bounds; this.pixels = pixels; }
public void Invert (Rectangle rect) { for (int y = rect.Y; y <= rect.Bottom; ++y) { for (int x = rect.X; x <= rect.Right; ++x) { Invert (x, y); } } }
public Task RenderAsync (ISurface surface, Rectangle roi, CancellationToken token) { return Task.Factory.StartNew (() => RenderLoop (surface, roi, token, null)); }
public Task ApplyAsync (ISurface surface, Rectangle roi) { return ApplyAsync (surface, roi, CancellationToken.None); }
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 Task ApplyAsync (ISurface surface, Rectangle roi, CancellationToken token) { return Task.Factory.StartNew (() => ApplyLoop (surface, surface.Bounds, token, null)); }
public Task ApplyAsync (ISurface lhs, ISurface rhs, ISurface dst, Rectangle roi) { return ApplyAsync (lhs, rhs, dst, roi, CancellationToken.None); }
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 Task RenderAsync (ISurface src, ISurface dst, Rectangle roi) { return RenderAsync (src, dst, roi, CancellationToken.None); }