// // Copyright (c) James South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessor.Samplers { using System; using System.Collections.Generic; using System.Numerics; using System.Threading.Tasks; /// /// Provides methods that allow the resizing of images using various resampling algorithms. /// public class Resize : ParallelImageProcessor { /// /// The epsilon for comparing floating point numbers. /// private const float Epsilon = 0.0001f; /// /// The horizontal weights. /// private Weights[] horizontalWeights; /// /// The vertical weights. /// private Weights[] verticalWeights; /// /// Initializes a new instance of the class. /// /// /// The sampler to perform the resize operation. /// public Resize(IResampler sampler) { Guard.NotNull(sampler, nameof(sampler)); this.Sampler = sampler; } /// /// Gets the sampler to perform the resize operation. /// public IResampler Sampler { get; } /// protected override void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle) { this.horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); this.verticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int targetY = targetRectangle.Y; int targetBottom = targetRectangle.Bottom; int startX = targetRectangle.X; int endX = targetRectangle.Right; //Vector endVX = new Vector(targetRectangle.Right); Parallel.For( startY, endY, y => { if (y >= targetY && y < targetBottom) { List verticalValues = this.verticalWeights[y].Values; double verticalSum = this.verticalWeights[y].Sum; for (int x = startX; x < endX; x++) { List horizontalValues = this.horizontalWeights[x].Values; double horizontalSum = this.horizontalWeights[x].Sum; // Destination color components double r = 0; double g = 0; double b = 0; double a = 0; foreach (Weight yw in verticalValues) { if (Math.Abs(yw.Value) < Epsilon) { continue; } int originY = yw.Index; foreach (Weight xw in horizontalValues) { if (Math.Abs(xw.Value) < Epsilon) { continue; } int originX = xw.Index; ColorVector sourceColor = source[originX, originY]; //sourceColor = PixelOperations.ToLinear(sourceColor); r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum); g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum); b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum); a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum); } } // TODO: Double cast? Bgra destinationColor = new ColorVector(b, g, r, a);//PixelOperations.ToSrgb(new ColorVector(b, g, r, a)); target[x, y] = destinationColor; } } }); } /// /// Computes the weights to apply at each pixel when resizing. /// /// The destination section size. /// The source section size. /// /// The . /// private Weights[] PrecomputeWeights(int destinationSize, int sourceSize) { IResampler sampler = this.Sampler; double du = sourceSize / (double)destinationSize; double scale = du; if (scale < 1) { scale = 1; } double ru = Math.Ceiling(scale * sampler.Radius); Weights[] result = new Weights[destinationSize]; Parallel.For( 0, destinationSize, i => { double fu = ((i + .5) * du) - 0.5; int startU = (int)Math.Ceiling(fu - ru); if (startU < 0) { startU = 0; } int endU = (int)Math.Floor(fu + ru); if (endU > sourceSize - 1) { endU = sourceSize - 1; } double sum = 0; result[i] = new Weights(); for (int a = startU; a <= endU; a++) { double w = 255 * sampler.GetValue((a - fu) / scale); if (Math.Abs(w) > Epsilon) { sum += w; result[i].Values.Add(new Weight(a, w)); } } result[i].Sum = sum; }); return result; } /// /// Represents the weight to be added to a scaled pixel. /// protected struct Weight { /// /// The pixel index. /// public readonly int Index; /// /// The result of the interpolation algorithm. /// public readonly double Value; /// /// Initializes a new instance of the struct. /// /// The index. /// The value. public Weight(int index, double value) { this.Index = index; this.Value = value; } } /// /// Represents a collection of weights and their sum. /// protected class Weights { /// /// Initializes a new instance of the class. /// public Weights() { this.Values = new List(); } /// /// Gets or sets the values. /// public List Values { get; set; } /// /// Gets or sets the sum. /// public double Sum { get; set; } } } }