Skip to content
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f455dad
PixelConversionModifierExtensions.ApplyCompanding() + tests
antonfirsov Apr 7, 2019
9861084
drop FileTestBase usage in ResizeTests
antonfirsov Apr 7, 2019
985357c
introduce [WithBasicTestPatternImages]
antonfirsov Apr 7, 2019
8695c39
drop parallelism in ResizeProcessor for simplicity
antonfirsov Apr 7, 2019
701343a
ResizeRectangle -> TargetRectangle
antonfirsov Apr 7, 2019
285b892
Merge remote-tracking branch 'origin/master' into af/resize-sandbox
antonfirsov Apr 13, 2019
4d38d7c
ResizeWindowOld
antonfirsov Apr 14, 2019
f81939e
ResizeWindow refactor 1
antonfirsov Apr 14, 2019
7079410
ResizeWindow refactor 2
antonfirsov Apr 14, 2019
545abf2
ResizeWindow refactor 3
antonfirsov Apr 14, 2019
20ddd11
ResizeWindow refactor 4
antonfirsov Apr 14, 2019
86cc83d
basic sliding window implementation (semi-stable state)
antonfirsov Apr 14, 2019
56de415
fix ResizeWithCropHeightMode
antonfirsov Apr 14, 2019
8e607ae
reference output for Resize_BasicSmall
antonfirsov Apr 14, 2019
682e521
minor optimization
antonfirsov Apr 14, 2019
4a0839a
refactor
antonfirsov Apr 18, 2019
b3495e8
refactor stuff + implement CalculateResizeWorkerWindowCount()
antonfirsov Apr 18, 2019
40aea16
utilize CalculateResizeWorkerHeightInWindowBands()
antonfirsov Apr 19, 2019
15b3b2a
improve benchmark: ArrayCopy -> CopyBuffers
antonfirsov Apr 19, 2019
6db0701
moar RowInterval stuff
antonfirsov Apr 19, 2019
19766df
buffer.CopyColumns(...)
antonfirsov Apr 19, 2019
d9e4d6c
simplify ResizeWorker logic
antonfirsov Apr 19, 2019
51cc659
WorkingBufferSizeHintInBytes_IsAppliedCorrectly
antonfirsov Apr 19, 2019
3860fb6
more robust tests
antonfirsov Apr 19, 2019
1fd135f
ResizeTests.LargeImage
antonfirsov Apr 19, 2019
f6c7784
optimized sliding works!
antonfirsov Apr 19, 2019
f7e0a94
reapply unsafe optimizations
antonfirsov Apr 19, 2019
2168071
moar unsafe optimization
antonfirsov Apr 19, 2019
a7c5295
benchmark WorkingBufferSizeHint effects
antonfirsov Apr 19, 2019
d882d91
memory profiling with Sandbox46
antonfirsov Apr 19, 2019
0c684ab
add ResizeWorker.pptx
antonfirsov Apr 19, 2019
1ab291c
refine ResizeWorker.pptx
antonfirsov Apr 19, 2019
94b52bf
xmldoc for ResizeWorker
antonfirsov Apr 20, 2019
d2e1a8d
update ResizeWorker.pptx [skip CI]
antonfirsov Apr 20, 2019
b7c4c9c
Merge remote-tracking branch 'origin/master' into af/resize-sandbox
antonfirsov Apr 20, 2019
c87a0f9
fix tests
antonfirsov Apr 20, 2019
026df1e
fix license text in CopyBuffers benchmark
antonfirsov Apr 20, 2019
1430336
extend the CopyBuffers benchmark
antonfirsov Apr 22, 2019
a359b19
use HashCode.Combine()
antonfirsov Apr 22, 2019
58f8d63
Merge branch 'master' into af/resize-sandbox
antonfirsov Apr 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ public int MaxDegreeOfParallelism
/// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();

/// <summary>
/// Gets or sets the working buffer size hint for image processors.
/// The default value is 1MB.
/// </summary>
/// <remarks>
/// Currently only used by Resize.
/// </remarks>
internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024;

/// <summary>
/// Gets or sets the image operations provider factory.
/// </summary>
Expand All @@ -118,9 +127,9 @@ public void Configure(IConfigurationModule configuration)
}

/// <summary>
/// Creates a shallow copy of the <see cref="Configuration"/>
/// Creates a shallow copy of the <see cref="Configuration"/>.
/// </summary>
/// <returns>A new configuration instance</returns>
/// <returns>A new configuration instance.</returns>
public Configuration Clone()
{
return new Configuration
Expand All @@ -130,18 +139,19 @@ public Configuration Clone()
MemoryAllocator = this.MemoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin,
FileSystem = this.FileSystem
FileSystem = this.FileSystem,
WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes,
};
}

/// <summary>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <para><see cref="PngConfigurationModule"/></para>
/// <para><see cref="JpegConfigurationModule"/></para>
/// <para><see cref="GifConfigurationModule"/></para>
/// <para><see cref="BmpConfigurationModule"/></para>
/// <see cref="PngConfigurationModule"/>
/// <see cref="JpegConfigurationModule"/>
/// <see cref="GifConfigurationModule"/>
/// <see cref="BmpConfigurationModule"/>.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/></returns>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance()
{
return new Configuration(
Expand Down
160 changes: 111 additions & 49 deletions src/ImageSharp/Memory/Buffer2DExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using SixLabors.Primitives;

Expand All @@ -14,55 +17,135 @@ namespace SixLabors.ImageSharp.Memory
internal static class Buffer2DExtensions
{
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// Copy <paramref name="columnCount"/> columns of <paramref name="buffer"/> inplace,
/// from positions starting at <paramref name="sourceIndex"/> to positions at <paramref name="destIndex"/>.
/// </summary>
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
public static unsafe void CopyColumns<T>(
this Buffer2D<T> buffer,
int sourceIndex,
int destIndex,
int columnCount)
where T : struct
{
return buffer.MemorySource.GetSpan();
DebugGuard.NotNull(buffer, nameof(buffer));
DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex));
DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, 0, nameof(sourceIndex));
CheckColumnRegionsDoNotOverlap(buffer, sourceIndex, destIndex, columnCount);

int elementSize = Unsafe.SizeOf<T>();
int width = buffer.Width * elementSize;
int sOffset = sourceIndex * elementSize;
int dOffset = destIndex * elementSize;
long count = columnCount * elementSize;

Span<byte> span = MemoryMarshal.AsBytes(buffer.Memory.Span);

fixed (byte* ptr = span)
{
byte* basePtr = (byte*)ptr;
for (int y = 0; y < buffer.Height; y++)
{
byte* sPtr = basePtr + sOffset;
byte* dPtr = basePtr + dOffset;

Buffer.MemoryCopy(sPtr, dPtr, count, count);

basePtr += width;
}
}
}

/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these methods here are very nice!

where T : struct
{
return new Rectangle(0, 0, buffer.Width, buffer.Height);
}

/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the subarea represented by 'rectangle'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct =>
new BufferArea<T>(buffer, rectangle);

public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct =>
new BufferArea<T>(buffer, new Rectangle(x, y, width, height));

/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct =>
new BufferArea<T>(buffer);

public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct =>
new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));

/// <summary>
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
/// </summary>
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows)
where T : struct
{
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width);
}

/// <summary>
/// Gets a <see cref="Memory{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width);
}

/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
where T : struct
{
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
}

/// <summary>
/// Gets a <see cref="Memory{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> buffer, int y)
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width);
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
}

/// <summary>
Expand All @@ -78,49 +161,28 @@ public static Size Size<T>(this Buffer2D<T> buffer)
}

/// <summary>
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return new Rectangle(0, 0, buffer.Width, buffer.Height);
return buffer.MemorySource.GetSpan();
}

/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the subarea represented by 'rectangle'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct => new BufferArea<T>(buffer, rectangle);

public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct => new BufferArea<T>(buffer, new Rectangle(x, y, width, height));

public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct => new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));

/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct => new BufferArea<T>(buffer);

/// <summary>
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
/// </summary>
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows)
[Conditional("DEBUG")]
private static void CheckColumnRegionsDoNotOverlap<T>(
Buffer2D<T> buffer,
int sourceIndex,
int destIndex,
int columnCount)
where T : struct
{
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width);
int minIndex = Math.Min(sourceIndex, destIndex);
int maxIndex = Math.Max(sourceIndex, destIndex);
if (maxIndex < minIndex + columnCount || maxIndex > buffer.Width - columnCount)
{
throw new InvalidOperationException("Column regions should not overlap!");
}
}
}
}
36 changes: 35 additions & 1 deletion src/ImageSharp/Memory/RowInterval.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;

using SixLabors.Primitives;

namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Represents an interval of rows in a <see cref="Rectangle"/> and/or <see cref="Buffer2D{T}"/>
/// </summary>
internal readonly struct RowInterval
internal readonly struct RowInterval : IEquatable<RowInterval>
{
/// <summary>
/// Initializes a new instance of the <see cref="RowInterval"/> struct.
Expand Down Expand Up @@ -36,7 +38,39 @@ public RowInterval(int min, int max)
/// </summary>
public int Height => this.Max - this.Min;

public static bool operator ==(RowInterval left, RowInterval right)
{
return left.Equals(right);
}

public static bool operator !=(RowInterval left, RowInterval right)
{
return !left.Equals(right);
}

/// <inheritdoc />
public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]";

public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max);

public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length);

public bool Equals(RowInterval other)
{
return this.Min == other.Min && this.Max == other.Max;
}

public override bool Equals(object obj)
{
return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other);
}

public override int GetHashCode()
{
unchecked
{
return (this.Min * 397) ^ this.Max;
}
}
}
}
18 changes: 18 additions & 0 deletions src/ImageSharp/PixelFormats/PixelConversionModifiersExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Extension and utility methods for <see cref="PixelConversionModifiers"/>.
/// </summary>
internal static class PixelConversionModifiersExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -16,5 +19,20 @@ public static PixelConversionModifiers Remove(
this PixelConversionModifiers modifiers,
PixelConversionModifiers removeThis) =>
modifiers & ~removeThis;

/// <summary>
/// Applies the union of <see cref="PixelConversionModifiers.Scale"/> and <see cref="PixelConversionModifiers.SRgbCompand"/>,
/// if <paramref name="compand"/> is true, returns unmodified <paramref name="originalModifiers"/> otherwise.
/// </summary>
/// <remarks>
/// <see cref="PixelConversionModifiers.Scale"/> and <see cref="PixelConversionModifiers.SRgbCompand"/>
/// should be always used together!
/// </remarks>
public static PixelConversionModifiers ApplyCompanding(
this PixelConversionModifiers originalModifiers,
bool compand) =>
compand
? originalModifiers | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! This solves the complexity issues

: originalModifiers;
}
}
Loading