|
|
|
@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
using SixLabors.ImageSharp.Advanced; |
|
|
|
using SixLabors.ImageSharp.ColorSpaces.Companding; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
using SixLabors.ImageSharp.ParallelUtils; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
@ -216,27 +215,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
workingRect, |
|
|
|
configuration, |
|
|
|
rows => |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
// Y coordinates of source points
|
|
|
|
Span<TPixel> sourceRow = |
|
|
|
source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); |
|
|
|
Span<TPixel> targetRow = destination.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
for (int x = minX; x < maxX; x++) |
|
|
|
{ |
|
|
|
// Y coordinates of source points
|
|
|
|
Span<TPixel> sourceRow = |
|
|
|
source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); |
|
|
|
Span<TPixel> targetRow = destination.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
for (int x = minX; x < maxX; x++) |
|
|
|
{ |
|
|
|
// X coordinates of source points
|
|
|
|
targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; |
|
|
|
} |
|
|
|
// X coordinates of source points
|
|
|
|
targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
int sourceHeight = source.Height; |
|
|
|
|
|
|
|
PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply; |
|
|
|
if (this.Compand) |
|
|
|
{ |
|
|
|
conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand; |
|
|
|
} |
|
|
|
|
|
|
|
// 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
|
|
|
|
@ -251,30 +256,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
processColsRect, |
|
|
|
configuration, |
|
|
|
(rows, tempRowBuffer) => |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); |
|
|
|
Span<Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX); |
|
|
|
|
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan); |
|
|
|
Vector4Utils.Premultiply(tempRowSpan); |
|
|
|
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); |
|
|
|
Span<Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX); |
|
|
|
|
|
|
|
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; |
|
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); |
|
|
|
|
|
|
|
if (this.Compand) |
|
|
|
{ |
|
|
|
SRgbCompanding.Expand(tempRowSpan); |
|
|
|
} |
|
|
|
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; |
|
|
|
|
|
|
|
for (int x = minX; x < maxX; x++) |
|
|
|
{ |
|
|
|
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); |
|
|
|
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = |
|
|
|
kernel.Convolve(tempRowSpan); |
|
|
|
} |
|
|
|
for (int x = minX; x < maxX; x++) |
|
|
|
{ |
|
|
|
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); |
|
|
|
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); |
|
|
|
|
|
|
|
@ -283,35 +281,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
|
|
processRowsRect, |
|
|
|
configuration, |
|
|
|
(rows, tempRowBuffer) => |
|
|
|
{ |
|
|
|
Span<Vector4> tempRowSpan = tempRowBuffer.Span; |
|
|
|
|
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
// Ensure offsets are normalized for cropping and padding.
|
|
|
|
ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); |
|
|
|
{ |
|
|
|
Span<Vector4> tempRowSpan = tempRowBuffer.Span; |
|
|
|
|
|
|
|
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); |
|
|
|
for (int y = rows.Min; y < rows.Max; y++) |
|
|
|
{ |
|
|
|
// Ensure offsets are normalized for cropping and padding.
|
|
|
|
ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); |
|
|
|
|
|
|
|
for (int x = 0; x < width; x++) |
|
|
|
{ |
|
|
|
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); |
|
|
|
ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); |
|
|
|
|
|
|
|
// Destination color components
|
|
|
|
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); |
|
|
|
} |
|
|
|
for (int x = 0; x < width; x++) |
|
|
|
{ |
|
|
|
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); |
|
|
|
|
|
|
|
Vector4Utils.UnPremultiply(tempRowSpan); |
|
|
|
// Destination color components
|
|
|
|
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.Compand) |
|
|
|
{ |
|
|
|
SRgbCompanding.Compress(tempRowSpan); |
|
|
|
} |
|
|
|
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan); |
|
|
|
} |
|
|
|
}); |
|
|
|
PixelOperations<TPixel>.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan, conversionModifiers); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|