// // 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.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.00001f; /// /// 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; Parallel.For( startY, endY, y => { if (y >= targetY && y < targetBottom) { List verticalValues = this.verticalWeights[y].Values; float verticalSum = this.verticalWeights[y].Sum; for (int x = startX; x < endX; x++) { List horizontalValues = this.horizontalWeights[x].Values; float horizontalSum = this.horizontalWeights[x].Sum; // Destination color components Color destination = new Color(0, 0, 0, 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; Color sourceColor = PixelOperations.ToLinear(source[originX, originY]); if (Math.Abs(sourceColor.A) < Epsilon) { continue; } float weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum); destination.R += sourceColor.R * weight; destination.G += sourceColor.G * weight; destination.B += sourceColor.B * weight; destination.A += sourceColor.A * weight; } } destination = PixelOperations.ToSrgb(destination); target[x, y] = destination; } } }); } /// /// 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; float du = sourceSize / (float)destinationSize; float scale = du; if (scale < 1) { scale = 1; } float ru = (float)Math.Ceiling(scale * sampler.Radius); Weights[] result = new Weights[destinationSize]; Parallel.For( 0, destinationSize, i => { float fu = ((i + .5f) * du) - 0.5f; 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; } float sum = 0; result[i] = new Weights(); for (int a = startU; a <= endU; a++) { float w = 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 float Value; /// /// Initializes a new instance of the struct. /// /// The index. /// The value. public Weight(int index, float 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 float Sum { get; set; } } } }