// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace GenericImage { using System; using System.Threading; using GenericImage.PackedVectors; /// /// Allows the application of processors to images. /// public abstract class ImageProcessor : IImageProcessor { /// public event ProgressEventHandler OnProgress; /// /// The number of rows processed by a derived class. /// private int numRowsProcessed; /// /// The total number of rows that will be processed by a derived class. /// private int totalRows; /// public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle) where TColor : IColor, new() where TDepth : struct { try { this.OnApply(target, source, target.Bounds, sourceRectangle); this.numRowsProcessed = 0; this.totalRows = sourceRectangle.Height; this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } catch (Exception ex) { throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); } } /// public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle) where TColor : IColor, new() where TDepth : struct { try { TColor[] pixels = new TColor[width * height]; target.SetPixels(width, height, pixels); // Ensure we always have bounds. if (sourceRectangle == Rectangle.Empty) { sourceRectangle = source.Bounds; } if (targetRectangle == Rectangle.Empty) { targetRectangle = target.Bounds; } this.OnApply(target, source, targetRectangle, sourceRectangle); this.numRowsProcessed = 0; this.totalRows = targetRectangle.Height; this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } catch (Exception ex) { throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); } } /// /// This method is called before the process is applied to prepare the processor. /// /// Target image to apply the process to. /// The source image. Cannot be null. /// /// The structure that specifies the location and size of the drawn image. /// The image is scaled to fit the rectangle. /// /// /// The structure that specifies the portion of the image object to draw. /// protected virtual void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) where TColor : IColor, new() where TDepth : struct { } /// /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The type of pixels contained within the image. /// Target image to apply the process to. /// The source image. Cannot be null. /// /// The structure that specifies the location and size of the drawn image. /// The image is scaled to fit the rectangle. /// /// /// The structure that specifies the portion of the image object to draw. /// /// The index of the row within the source image to start processing. /// The index of the row within the source image to end processing. /// /// The method keeps the source image unchanged and returns the /// the result of image process as new image. /// protected abstract void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) where TColor : IColor, new() where TDepth : struct; /// /// This method is called after the process is applied to prepare the processor. /// /// The type of pixels contained within the image. /// Target image to apply the process to. /// The source image. Cannot be null. /// /// The structure that specifies the location and size of the drawn image. /// The image is scaled to fit the rectangle. /// /// /// The structure that specifies the portion of the image object to draw. /// protected virtual void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) where TColor : IColor, new() where TDepth : struct { } /// /// Must be called by derived classes after processing a single row. /// protected void OnRowProcessed() { if (this.OnProgress != null) { int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1); // Multi-pass filters process multiple times more rows than totalRows, so update totalRows on the fly if (currThreadNumRows > this.totalRows) { this.totalRows = currThreadNumRows; } // Report progress. This may be on the client's thread, or on a Task library thread. this.OnProgress(this, new ProgressEventArgs { RowsProcessed = currThreadNumRows, TotalRows = this.totalRows }); } } } }