mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 084a071bbe2f57ed8eaa29fc48e77ebf24eb204b Former-commit-id: cf8ca002917043c93bd18c10e21c0005999ef972 Former-commit-id: a7f231eb49f32fe8b3bc9bff312fd9995e2dabbcaf/merge-core
11 changed files with 804 additions and 33 deletions
@ -0,0 +1,76 @@ |
|||||
|
// <copyright file="IImageProcessor.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace GenericImage |
||||
|
{ |
||||
|
using PackedVectors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A delegate which is called as progress is made processing an image.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sender">The source of the event.</param>
|
||||
|
/// <param name="e">An object that contains the event data.</param>
|
||||
|
public delegate void ProgressEventHandler(object sender, ProgressEventArgs e); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Encapsulates methods to alter the pixels of an image.
|
||||
|
/// </summary>
|
||||
|
public interface IImageProcessor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Event fires when each row of the source image has been processed.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This event may be called from threads other than the client thread, and from multiple threads simultaneously.
|
||||
|
/// Individual row notifications may arrived out of order.
|
||||
|
/// </remarks>
|
||||
|
event ProgressEventHandler OnProgress; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor,TDepth}"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
/// <param name="source">The source image. Cannot be null.</param>
|
||||
|
/// <param name="sourceRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
||||
|
/// </param>
|
||||
|
/// <remarks>
|
||||
|
/// The method keeps the source image unchanged and returns the
|
||||
|
/// the result of image processing filter as new image.
|
||||
|
/// </remarks>
|
||||
|
/// <exception cref="System.ArgumentNullException">
|
||||
|
/// <paramref name="target"/> is null or <paramref name="source"/> is null.
|
||||
|
/// </exception>
|
||||
|
/// <exception cref="System.ArgumentException">
|
||||
|
/// <paramref name="sourceRectangle"/> doesnt fit the dimension of the image.
|
||||
|
/// </exception>
|
||||
|
void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle sourceRectangle) |
||||
|
where TColor : IColor<TDepth>, new() where TDepth : struct; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TDepth}"/> at the specified
|
||||
|
/// location and with the specified size.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
/// <param name="source">The source image. Cannot be null.</param>
|
||||
|
/// <param name="width">The target width.</param>
|
||||
|
/// <param name="height">The target height.</param>
|
||||
|
/// <param name="targetRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
|
||||
|
/// The image is scaled to fit the rectangle.
|
||||
|
/// </param>
|
||||
|
/// <param name="sourceRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
||||
|
/// </param>
|
||||
|
/// <remarks>
|
||||
|
/// The method keeps the source image unchanged and returns the
|
||||
|
/// the result of image process as new image.
|
||||
|
/// </remarks>
|
||||
|
void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle) |
||||
|
where TColor : IColor<TDepth>, new() where TDepth : struct; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
// <copyright file="ImageProcessor.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace GenericImage |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Threading; |
||||
|
|
||||
|
using GenericImage.PackedVectors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Allows the application of processors to images.
|
||||
|
/// </summary>
|
||||
|
public abstract class ImageProcessor : IImageProcessor |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public event ProgressEventHandler OnProgress; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The number of rows processed by a derived class.
|
||||
|
/// </summary>
|
||||
|
private int numRowsProcessed; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The total number of rows that will be processed by a derived class.
|
||||
|
/// </summary>
|
||||
|
private int totalRows; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle sourceRectangle) |
||||
|
where TColor : IColor<TDepth>, 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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, int width, int height, Rectangle targetRectangle, Rectangle sourceRectangle) where TColor : IColor<TDepth>, 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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method is called before the process is applied to prepare the processor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
/// <param name="source">The source image. Cannot be null.</param>
|
||||
|
/// <param name="targetRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
|
||||
|
/// The image is scaled to fit the rectangle.
|
||||
|
/// </param>
|
||||
|
/// <param name="sourceRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
||||
|
/// </param>
|
||||
|
protected virtual void OnApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
||||
|
where TColor : IColor<TDepth>, new() where TDepth : struct |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies the process to the specified portion of the specified <see cref="ImageBase{T}"/> at the specified location
|
||||
|
/// and with the specified size.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
/// <param name="source">The source image. Cannot be null.</param>
|
||||
|
/// <param name="targetRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
|
||||
|
/// The image is scaled to fit the rectangle.
|
||||
|
/// </param>
|
||||
|
/// <param name="sourceRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
||||
|
/// </param>
|
||||
|
/// <param name="startY">The index of the row within the source image to start processing.</param>
|
||||
|
/// <param name="endY">The index of the row within the source image to end processing.</param>
|
||||
|
/// <remarks>
|
||||
|
/// The method keeps the source image unchanged and returns the
|
||||
|
/// the result of image process as new image.
|
||||
|
/// </remarks>
|
||||
|
protected abstract void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
||||
|
where TColor : IColor<TDepth>, new() where TDepth : struct; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method is called after the process is applied to prepare the processor.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of pixels contained within the image.</typeparam>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
/// <param name="source">The source image. Cannot be null.</param>
|
||||
|
/// <param name="targetRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the location and size of the drawn image.
|
||||
|
/// The image is scaled to fit the rectangle.
|
||||
|
/// </param>
|
||||
|
/// <param name="sourceRectangle">
|
||||
|
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
|
||||
|
/// </param>
|
||||
|
protected virtual void AfterApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
||||
|
where TColor : IColor<TDepth>, new() where TDepth : struct |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Must be called by derived classes after processing a single row.
|
||||
|
/// </summary>
|
||||
|
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 }); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
namespace GenericImage.PackedVectors |
||||
|
{ |
||||
|
public struct Bgra<TDepth> : IColor4<TDepth> |
||||
|
where TDepth : struct |
||||
|
{ |
||||
|
public TDepth X { get; set; } |
||||
|
|
||||
|
public TDepth Y { get; set; } |
||||
|
|
||||
|
public TDepth Z { get; set; } |
||||
|
|
||||
|
public TDepth W { get; set; } |
||||
|
|
||||
|
public void Add<TColor>(TColor value) where TColor : IColor<TDepth> |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Multiply<TColor>(TColor value) where TColor : IColor<TDepth> |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Multiply<TColor>(float value) where TColor : IColor<TDepth> |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Divide<TColor>(TColor value) where TColor : IColor<TDepth> |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void PackVector(Vector4 vector) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public Vector4 ToVector() |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public byte[] ToBytes() |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
namespace GenericImage.PackedVectors |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
public interface IColor4<T> : IColor<T> |
||||
|
where T : struct |
||||
|
{ |
||||
|
T X { get; set; } |
||||
|
|
||||
|
T Y { get; set; } |
||||
|
|
||||
|
T Z { get; set; } |
||||
|
|
||||
|
T W { get; set; } |
||||
|
} |
||||
|
|
||||
|
public interface IColor<TDepth> : IColor |
||||
|
where TDepth : struct |
||||
|
{ |
||||
|
void Add<TColor>(TColor value) where TColor : IColor<TDepth>; |
||||
|
|
||||
|
void Multiply<TColor>(TColor value) where TColor : IColor<TDepth>; |
||||
|
|
||||
|
void Multiply<TColor>(float value) where TColor : IColor<TDepth>; |
||||
|
|
||||
|
void Divide<TColor>(TColor value) where TColor : IColor<TDepth>; |
||||
|
} |
||||
|
|
||||
|
public interface IColor |
||||
|
{ |
||||
|
void PackVector(Vector4 vector); |
||||
|
|
||||
|
Vector4 ToVector(); |
||||
|
|
||||
|
byte[] ToBytes(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// <copyright file="ProgressEventArgs.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace GenericImage |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains event data related to the progress made processing an image.
|
||||
|
/// </summary>
|
||||
|
public class ProgressEventArgs : System.EventArgs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the number of rows processed.
|
||||
|
/// </summary>
|
||||
|
public int RowsProcessed { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the total number of rows.
|
||||
|
/// </summary>
|
||||
|
public int TotalRows { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,393 @@ |
|||||
|
// <copyright file="ResizeProcessor.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageProcessorCore.Processors |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
using GenericImage; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods that allow the resizing of images using various algorithms.
|
||||
|
/// </summary>
|
||||
|
public class ResizeProcessor : ImageProcessor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ResizeProcessor"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sampler">
|
||||
|
/// The sampler to perform the resize operation.
|
||||
|
/// </param>
|
||||
|
public ResizeProcessor(IResampler sampler) |
||||
|
{ |
||||
|
Guard.NotNull(sampler, nameof(sampler)); |
||||
|
|
||||
|
this.Sampler = sampler; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the sampler to perform the resize operation.
|
||||
|
/// </summary>
|
||||
|
public IResampler Sampler { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the horizontal weights.
|
||||
|
/// </summary>
|
||||
|
protected Weights[] HorizontalWeights { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the vertical weights.
|
||||
|
/// </summary>
|
||||
|
protected Weights[] VerticalWeights { get; set; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void OnApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
||||
|
{ |
||||
|
if (!(this.Sampler is NearestNeighborResampler)) |
||||
|
{ |
||||
|
this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); |
||||
|
this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void Apply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
||||
|
{ |
||||
|
// Jump out, we'll deal with that later.
|
||||
|
if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int width = target.Width; |
||||
|
int height = target.Height; |
||||
|
int sourceHeight = sourceRectangle.Height; |
||||
|
int targetX = target.Bounds.X; |
||||
|
int targetY = target.Bounds.Y; |
||||
|
int targetRight = target.Bounds.Right; |
||||
|
int targetBottom = target.Bounds.Bottom; |
||||
|
int startX = targetRectangle.X; |
||||
|
int endX = targetRectangle.Right; |
||||
|
bool compand = this.Compand; |
||||
|
|
||||
|
if (this.Sampler is NearestNeighborResampler) |
||||
|
{ |
||||
|
// Scaling factors
|
||||
|
float widthFactor = sourceRectangle.Width / (float)targetRectangle.Width; |
||||
|
float heightFactor = sourceRectangle.Height / (float)targetRectangle.Height; |
||||
|
|
||||
|
using (IPixelAccessor<TColor> sourcePixels = source.Lock()) |
||||
|
using (IPixelAccessor<TColor> targetPixels = target.Lock()) |
||||
|
{ |
||||
|
Parallel.For( |
||||
|
startY, |
||||
|
endY, |
||||
|
y => |
||||
|
{ |
||||
|
if (targetY <= y && y < targetBottom) |
||||
|
{ |
||||
|
// Y coordinates of source points
|
||||
|
int originY = (int)((y - startY) * heightFactor); |
||||
|
|
||||
|
for (int x = startX; x < endX; x++) |
||||
|
{ |
||||
|
if (targetX <= x && x < targetRight) |
||||
|
{ |
||||
|
// X coordinates of source points
|
||||
|
int originX = (int)((x - startX) * widthFactor); |
||||
|
targetPixels[x, y] = sourcePixels[originX, originY]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.OnRowProcessed(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// Break out now.
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Interpolate the image using the calculated weights.
|
||||
|
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
|
||||
|
// First process the columns. Since we are not using multiple threads startY and endY
|
||||
|
// are the upper and lower bounds of the source rectangle.
|
||||
|
Image<TColor, TDepth> firstPass = new Image<TColor, TDepth>(target.Width, source.Height); |
||||
|
using (IPixelAccessor<TColor> sourcePixels = source.Lock()) |
||||
|
using (IPixelAccessor<TColor> firstPassPixels = firstPass.Lock()) |
||||
|
using (IPixelAccessor<TColor> targetPixels = target.Lock()) |
||||
|
{ |
||||
|
Parallel.For( |
||||
|
0, |
||||
|
sourceHeight, |
||||
|
y => |
||||
|
{ |
||||
|
for (int x = startX; x < endX; x++) |
||||
|
{ |
||||
|
if (x >= 0 && x < width) |
||||
|
{ |
||||
|
// Ensure offsets are normalised for cropping and padding.
|
||||
|
int offsetX = x - startX; |
||||
|
float sum = this.HorizontalWeights[offsetX].Sum; |
||||
|
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values; |
||||
|
|
||||
|
// Destination color components
|
||||
|
//Color destination = new Color();
|
||||
|
|
||||
|
//for (int i = 0; i < sum; i++)
|
||||
|
//{
|
||||
|
// Weight xw = horizontalValues[i];
|
||||
|
// int originX = xw.Index;
|
||||
|
// Color sourceColor = compand
|
||||
|
// ? Color.Expand(sourcePixels[originX, y])
|
||||
|
// : sourcePixels[originX, y];
|
||||
|
|
||||
|
// destination += sourceColor * xw.Value;
|
||||
|
//}
|
||||
|
|
||||
|
//if (compand)
|
||||
|
//{
|
||||
|
// destination = Color.Compress(destination);
|
||||
|
//}
|
||||
|
|
||||
|
//firstPassPixels[x, y] = destination;
|
||||
|
TColor sourceColor; |
||||
|
TColor destination = default(TColor); |
||||
|
|
||||
|
for (int i = 0; i < sum; i++) |
||||
|
{ |
||||
|
Weight xw = horizontalValues[i]; |
||||
|
int originX = xw.Index; |
||||
|
sourceColor = sourcePixels[originX, y]; |
||||
|
//Color sourceColor = compand
|
||||
|
// ? Color.Expand(sourcePixels[originX, y])
|
||||
|
// : sourcePixels[originX, y];
|
||||
|
//sourceColor.Multiply(xw.Value);
|
||||
|
//destination.Add(sourceColor);
|
||||
|
//destination += sourceColor * xw.Value;
|
||||
|
|
||||
|
sourceColor.Multiply<TColor>(xw.Value); |
||||
|
destination.Add(sourceColor); |
||||
|
} |
||||
|
|
||||
|
//if (compand)
|
||||
|
//{
|
||||
|
// destination = Color.Compress(destination);
|
||||
|
//}
|
||||
|
//T packed = default(T);
|
||||
|
//packed.PackVector(destination);
|
||||
|
|
||||
|
firstPassPixels[x, y] = destination; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Now process the rows.
|
||||
|
Parallel.For( |
||||
|
startY, |
||||
|
endY, |
||||
|
y => |
||||
|
{ |
||||
|
if (y >= 0 && y < height) |
||||
|
{ |
||||
|
// Ensure offsets are normalised for cropping and padding.
|
||||
|
int offsetY = y - startY; |
||||
|
float sum = this.VerticalWeights[offsetY].Sum; |
||||
|
Weight[] verticalValues = this.VerticalWeights[offsetY].Values; |
||||
|
|
||||
|
for (int x = 0; x < width; x++) |
||||
|
{ |
||||
|
// Destination color components
|
||||
|
TColor sourceColor; |
||||
|
TColor destination = default(TColor); |
||||
|
|
||||
|
for (int i = 0; i < sum; i++) |
||||
|
{ |
||||
|
Weight yw = verticalValues[i]; |
||||
|
int originY = yw.Index; |
||||
|
sourceColor = firstPassPixels[x, originY]; |
||||
|
//Color sourceColor = compand
|
||||
|
// ? Color.Expand(firstPassPixels[x, originY])
|
||||
|
// : firstPassPixels[x, originY];
|
||||
|
//Vector4 sourceColor = firstPassPixels[x, originY].ToVector4();
|
||||
|
//destination += sourceColor * yw.Value;
|
||||
|
|
||||
|
sourceColor.Multiply<TColor>(yw.Value); |
||||
|
destination.Add(sourceColor); |
||||
|
} |
||||
|
|
||||
|
//if (compand)
|
||||
|
//{
|
||||
|
// destination = Color.Compress(destination);
|
||||
|
//}
|
||||
|
|
||||
|
//T packed = default(T);
|
||||
|
//packed.PackVector(destination);
|
||||
|
|
||||
|
targetPixels[x, y] = destination; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.OnRowProcessed(); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void AfterApply<TColor, TDepth>(ImageBase<TColor, TDepth> target, ImageBase<TColor, TDepth> source, Rectangle targetRectangle, Rectangle sourceRectangle) |
||||
|
{ |
||||
|
// Copy the pixels over.
|
||||
|
if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) |
||||
|
{ |
||||
|
target.ClonePixels(target.Width, target.Height, source.Pixels); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the weights to apply at each pixel when resizing.
|
||||
|
/// </summary>
|
||||
|
/// <param name="destinationSize">The destination section size.</param>
|
||||
|
/// <param name="sourceSize">The source section size.</param>
|
||||
|
/// <returns>
|
||||
|
/// The <see cref="T:Weights[]"/>.
|
||||
|
/// </returns>
|
||||
|
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize) |
||||
|
{ |
||||
|
float scale = (float)destinationSize / sourceSize; |
||||
|
IResampler sampler = this.Sampler; |
||||
|
float radius = sampler.Radius; |
||||
|
double left; |
||||
|
double right; |
||||
|
float weight; |
||||
|
int index; |
||||
|
int sum; |
||||
|
|
||||
|
Weights[] result = new Weights[destinationSize]; |
||||
|
|
||||
|
// When shrinking, broaden the effective kernel support so that we still
|
||||
|
// visit every source pixel.
|
||||
|
if (scale < 1) |
||||
|
{ |
||||
|
float width = radius / scale; |
||||
|
float filterScale = 1 / scale; |
||||
|
|
||||
|
// Make the weights slices, one source for each column or row.
|
||||
|
for (int i = 0; i < destinationSize; i++) |
||||
|
{ |
||||
|
float centre = i / scale; |
||||
|
left = Math.Ceiling(centre - width); |
||||
|
right = Math.Floor(centre + width); |
||||
|
|
||||
|
result[i] = new Weights |
||||
|
{ |
||||
|
Values = new Weight[(int)(right - left + 1)] |
||||
|
}; |
||||
|
|
||||
|
for (double j = left; j <= right; j++) |
||||
|
{ |
||||
|
weight = sampler.GetValue((float)((centre - j) / filterScale)) / filterScale; |
||||
|
if (j < 0) |
||||
|
{ |
||||
|
index = (int)-j; |
||||
|
} |
||||
|
else if (j >= sourceSize) |
||||
|
{ |
||||
|
index = (int)((sourceSize - j) + sourceSize - 1); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
index = (int)j; |
||||
|
} |
||||
|
|
||||
|
sum = (int)result[i].Sum++; |
||||
|
result[i].Values[sum] = new Weight(index, weight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Make the weights slices, one source for each column or row.
|
||||
|
for (int i = 0; i < destinationSize; i++) |
||||
|
{ |
||||
|
float centre = i / scale; |
||||
|
left = Math.Ceiling(centre - radius); |
||||
|
right = Math.Floor(centre + radius); |
||||
|
result[i] = new Weights |
||||
|
{ |
||||
|
Values = new Weight[(int)(right - left + 1)] |
||||
|
}; |
||||
|
|
||||
|
for (double j = left; j <= right; j++) |
||||
|
{ |
||||
|
weight = sampler.GetValue((float)(centre - j)); |
||||
|
if (j < 0) |
||||
|
{ |
||||
|
index = (int)-j; |
||||
|
} |
||||
|
else if (j >= sourceSize) |
||||
|
{ |
||||
|
index = (int)((sourceSize - j) + sourceSize - 1); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
index = (int)j; |
||||
|
} |
||||
|
|
||||
|
sum = (int)result[i].Sum++; |
||||
|
result[i].Values[sum] = new Weight(index, weight); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents the weight to be added to a scaled pixel.
|
||||
|
/// </summary>
|
||||
|
protected struct Weight |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Weight"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The index.</param>
|
||||
|
/// <param name="value">The value.</param>
|
||||
|
public Weight(int index, float value) |
||||
|
{ |
||||
|
this.Index = index; |
||||
|
this.Value = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the pixel index.
|
||||
|
/// </summary>
|
||||
|
public int Index { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the result of the interpolation algorithm.
|
||||
|
/// </summary>
|
||||
|
public float Value { get; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a collection of weights and their sum.
|
||||
|
/// </summary>
|
||||
|
protected class Weights |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the values.
|
||||
|
/// </summary>
|
||||
|
public Weight[] Values { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the sum.
|
||||
|
/// </summary>
|
||||
|
public float Sum { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue