Browse Source

Experiment with multipliers.

Former-commit-id: 084a071bbe2f57ed8eaa29fc48e77ebf24eb204b
Former-commit-id: cf8ca002917043c93bd18c10e21c0005999ef972
Former-commit-id: a7f231eb49f32fe8b3bc9bff312fd9995e2dabbc
af/merge-core
James Jackson-South 10 years ago
parent
commit
bd8112fc33
  1. 9
      GenericImage/IImageBase.cs
  2. 76
      GenericImage/IImageProcessor.cs
  3. 16
      GenericImage/IPixelAccessor.cs
  4. 40
      GenericImage/ImageBase.cs
  5. 165
      GenericImage/ImageProcessor.cs
  6. 52
      GenericImage/PackedVectors/Bgra.cs
  7. 37
      GenericImage/PackedVectors/IColor.cs
  8. 23
      GenericImage/ProgressEventArgs.cs
  9. 393
      GenericImage/ResizeProcessor.cs
  10. 4
      src/ImageProcessorCore/PackedVector/Bgra32.cs
  11. 22
      src/ImageProcessorCore/PackedVector/IPackedVector.cs

9
GenericImage/IImageBase.cs

@ -2,15 +2,16 @@
{
using GenericImage.PackedVectors;
public interface IImageBase<TPacked>
where TPacked : IPackedVector
public interface IImageBase<TColor, TDepth>
where TColor : IColor<TDepth>
where TDepth : struct
{
TPacked[] Pixels { get; }
TColor[] Pixels { get; }
int Width { get; }
int Height { get; }
IPixelAccessor Lock();
IPixelAccessor<TColor> Lock();
}
}

76
GenericImage/IImageProcessor.cs

@ -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;
}
}

16
GenericImage/IPixelAccessor.cs

@ -2,14 +2,22 @@
{
using System;
using GenericImage.PackedVectors;
public interface IPixelAccessor : IDisposable
public interface IPixelAccessor<TColor> : IDisposable
{
IPackedVector this[int x, int y]
TColor this[int x, int y]
{
get;
set;
}
/// <summary>
/// Gets the width.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height.
/// </summary>
int Height { get; }
}
}

40
GenericImage/ImageBase.cs

@ -8,21 +8,21 @@
/// Encapsulates the basic properties and methods required to manipulate images
/// in different pixel formats.
/// </summary>
/// <typeparam name="TPacked">
/// The packed vector pixels format.
/// </typeparam>
public abstract class ImageBase<TPacked>
where TPacked : IPackedVector
/// <typeparam name="TColor">The packed vector pixels format.</typeparam>
/// <typeparam name="TDepth">The bit depth of the image. byte, float, etc.</typeparam>
public abstract class ImageBase<TColor, TDepth> : IImageBase<TColor, TDepth>
where TColor : IColor<TDepth>
where TDepth : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPacked}"/> class.
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
protected ImageBase()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPacked}"/> class.
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
@ -36,19 +36,19 @@
this.Width = width;
this.Height = height;
this.Pixels = new TPacked[width * height];
this.Pixels = new TColor[width * height];
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TPacked}"/> class.
/// Initializes a new instance of the <see cref="ImageBase{TColor,TDepth}"/> class.
/// </summary>
/// <param name="other">
/// The other <see cref="ImageBase{TPacked}"/> to create this instance from.
/// The other <see cref="ImageBase{TColor,TDepth}"/> to create this instance from.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown if the given <see cref="ImageBase{TPacked}"/> is null.
/// Thrown if the given <see cref="ImageBase{TColor,TDepth}"/> is null.
/// </exception>
protected ImageBase(ImageBase<TPacked> other)
protected ImageBase(ImageBase<TColor, TDepth> other)
{
// Guard.NotNull(other, nameof(other), "Other image cannot be null.");
@ -58,14 +58,14 @@
this.FrameDelay = other.FrameDelay;
// Copy the pixels.
this.Pixels = new TPacked[this.Width * this.Height];
this.Pixels = new TColor[this.Width * this.Height];
Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length);
}
/// <summary>
/// Gets the pixels as an array of the given packed pixel format.
/// </summary>
public TPacked[] Pixels { get; private set; }
public TColor[] Pixels { get; private set; }
/// <summary>
/// Gets the width in pixels.
@ -100,6 +100,8 @@
/// </summary>
public int FrameDelay { get; set; }
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
@ -114,7 +116,7 @@
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
public void SetPixels(int width, int height, TPacked[] pixels)
public void SetPixels(int width, int height, TColor[] pixels)
{
if (width <= 0)
{
@ -151,7 +153,7 @@
/// <exception cref="ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
public void ClonePixels(int width, int height, TPacked[] pixels)
public void ClonePixels(int width, int height, TColor[] pixels)
{
if (width <= 0)
{
@ -172,7 +174,7 @@
this.Height = height;
// Copy the pixels.
this.Pixels = new TPacked[pixels.Length];
this.Pixels = new TColor[pixels.Length];
Array.Copy(pixels, this.Pixels, pixels.Length);
}
@ -182,7 +184,7 @@
/// It is imperative that the accessor is correctly disposed off after use.
/// </remarks>
/// </summary>
/// <returns>The <see cref="IPixelAccessor"/></returns>
public abstract IPixelAccessor Lock();
/// <returns>The <see cref="IPixelAccessor{TColor}"/></returns>
public abstract IPixelAccessor<TColor> Lock();
}
}

165
GenericImage/ImageProcessor.cs

@ -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 });
}
}
}
}

52
GenericImage/PackedVectors/Bgra.cs

@ -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();
}
}
}

37
GenericImage/PackedVectors/IColor.cs

@ -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();
}
}

23
GenericImage/ProgressEventArgs.cs

@ -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; }
}
}

393
GenericImage/ResizeProcessor.cs

@ -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; }
}
}
}

4
src/ImageProcessorCore/PackedVector/Bgra32.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Bgra32 : IPackedVector, IEquatable<Bgra32>
public struct Bgra32 : IPackedVector<uint>, IEquatable<Bgra32>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgra32"/> struct.
@ -43,7 +43,7 @@ namespace ImageProcessorCore
// The maths are wrong here I just wanted to test the performance to see if the
// issues are caused by the Vector transform or something else.
public void Add(IPackedVector value)
public void Add(IPackedVector<uint> value)
{
}

22
src/ImageProcessorCore/PackedVector/IPackedVector.cs

@ -22,6 +22,8 @@ namespace ImageProcessorCore
/// Typically packed in least to greatest significance order.
/// </summary>
T PackedValue { get; set; }
}
/// <summary>
@ -29,18 +31,30 @@ namespace ImageProcessorCore
/// </summary>
public interface IPackedVector
{
void Add(IPackedVector value);
void Add<U>(U value);
void Subtract(IPackedVector value);
void Subtract<U>(U value);
void Multiply(IPackedVector value);
void Multiply<U>(U value);
void Multiply(float value);
void Divide(IPackedVector value);
void Divide<U>(U value);
void Divide(float value);
//void Add(IPackedVector value);
//void Subtract(IPackedVector value);
//void Multiply(IPackedVector value);
//void Multiply(float value);
//void Divide(IPackedVector value);
//void Divide(float value);
/// <summary>
/// Sets the packed representation from a <see cref="Vector4"/>.
/// </summary>

Loading…
Cancel
Save