Skip to content

Commit a9e10e2

Browse files
Merge pull request #1132 from SixLabors/feature/single-row-value-delegate-optin
Single row value delegate option
2 parents ac6b6a9 + 5626260 commit a9e10e2

33 files changed

+706
-537
lines changed

src/ImageSharp/Advanced/IRowIntervalOperation.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Six Labors and contributors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System;
5-
using System.Runtime.CompilerServices;
64
using SixLabors.ImageSharp.Memory;
75

86
namespace SixLabors.ImageSharp.Advanced

src/ImageSharp/Advanced/IRowIntervalOperation{TBuffer}.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5-
using System.Buffers;
6-
using System.Runtime.CompilerServices;
75
using SixLabors.ImageSharp.Memory;
86

97
namespace SixLabors.ImageSharp.Advanced
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) Six Labors and contributors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
namespace SixLabors.ImageSharp.Advanced
5+
{
6+
/// <summary>
7+
/// Defines the contract for an action that operates on a row.
8+
/// </summary>
9+
public interface IRowOperation
10+
{
11+
/// <summary>
12+
/// Invokes the method passing the row y coordinate.
13+
/// </summary>
14+
/// <param name="y">The row y coordinate.</param>
15+
void Invoke(int y);
16+
}
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Six Labors and contributors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
6+
namespace SixLabors.ImageSharp.Advanced
7+
{
8+
/// <summary>
9+
/// Defines the contract for an action that operates on a row with a temporary buffer.
10+
/// </summary>
11+
/// <typeparam name="TBuffer">The type of buffer elements.</typeparam>
12+
public interface IRowOperation<TBuffer>
13+
where TBuffer : unmanaged
14+
{
15+
/// <summary>
16+
/// Invokes the method passing the row and a buffer.
17+
/// </summary>
18+
/// <param name="y">The row y coordinate.</param>
19+
/// <param name="span">The contiguous region of memory.</param>
20+
void Invoke(int y, Span<TBuffer> span);
21+
}
22+
}

src/ImageSharp/Advanced/ParallelRowIterator.Wrappers.cs

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,130 @@ namespace SixLabors.ImageSharp.Advanced
1717
/// </content>
1818
public static partial class ParallelRowIterator
1919
{
20-
private readonly struct IterationParameters
20+
private readonly struct RowOperationWrapper<T>
21+
where T : struct, IRowOperation
2122
{
22-
public readonly int MinY;
23-
public readonly int MaxY;
24-
public readonly int StepY;
25-
public readonly int Width;
23+
private readonly int minY;
24+
private readonly int maxY;
25+
private readonly int stepY;
26+
private readonly T action;
2627

27-
public IterationParameters(int minY, int maxY, int stepY)
28-
: this(minY, maxY, stepY, 0)
28+
[MethodImpl(InliningOptions.ShortMethod)]
29+
public RowOperationWrapper(
30+
int minY,
31+
int maxY,
32+
int stepY,
33+
in T action)
2934
{
35+
this.minY = minY;
36+
this.maxY = maxY;
37+
this.stepY = stepY;
38+
this.action = action;
3039
}
3140

32-
public IterationParameters(int minY, int maxY, int stepY, int width)
41+
[MethodImpl(InliningOptions.ShortMethod)]
42+
public void Invoke(int i)
3343
{
34-
this.MinY = minY;
35-
this.MaxY = maxY;
36-
this.StepY = stepY;
37-
this.Width = width;
44+
int yMin = this.minY + (i * this.stepY);
45+
46+
if (yMin >= this.maxY)
47+
{
48+
return;
49+
}
50+
51+
int yMax = Math.Min(yMin + this.stepY, this.maxY);
52+
53+
for (int y = yMin; y < yMax; y++)
54+
{
55+
// Skip the safety copy when invoking a potentially impure method on a readonly field
56+
Unsafe.AsRef(this.action).Invoke(y);
57+
}
58+
}
59+
}
60+
61+
private readonly struct RowOperationWrapper<T, TBuffer>
62+
where T : struct, IRowOperation<TBuffer>
63+
where TBuffer : unmanaged
64+
{
65+
private readonly int minY;
66+
private readonly int maxY;
67+
private readonly int stepY;
68+
private readonly int width;
69+
private readonly MemoryAllocator allocator;
70+
private readonly T action;
71+
72+
[MethodImpl(InliningOptions.ShortMethod)]
73+
public RowOperationWrapper(
74+
int minY,
75+
int maxY,
76+
int stepY,
77+
int width,
78+
MemoryAllocator allocator,
79+
in T action)
80+
{
81+
this.minY = minY;
82+
this.maxY = maxY;
83+
this.stepY = stepY;
84+
this.width = width;
85+
this.allocator = allocator;
86+
this.action = action;
87+
}
88+
89+
[MethodImpl(InliningOptions.ShortMethod)]
90+
public void Invoke(int i)
91+
{
92+
int yMin = this.minY + (i * this.stepY);
93+
94+
if (yMin >= this.maxY)
95+
{
96+
return;
97+
}
98+
99+
int yMax = Math.Min(yMin + this.stepY, this.maxY);
100+
101+
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
102+
103+
Span<TBuffer> span = buffer.Memory.Span;
104+
105+
for (int y = yMin; y < yMax; y++)
106+
{
107+
Unsafe.AsRef(this.action).Invoke(y, span);
108+
}
38109
}
39110
}
40111

41112
private readonly struct RowIntervalOperationWrapper<T>
42113
where T : struct, IRowIntervalOperation
43114
{
44-
private readonly IterationParameters info;
115+
private readonly int minY;
116+
private readonly int maxY;
117+
private readonly int stepY;
45118
private readonly T operation;
46119

47120
[MethodImpl(InliningOptions.ShortMethod)]
48-
public RowIntervalOperationWrapper(in IterationParameters info, in T operation)
121+
public RowIntervalOperationWrapper(
122+
int minY,
123+
int maxY,
124+
int stepY,
125+
in T operation)
49126
{
50-
this.info = info;
127+
this.minY = minY;
128+
this.maxY = maxY;
129+
this.stepY = stepY;
51130
this.operation = operation;
52131
}
53132

54133
[MethodImpl(InliningOptions.ShortMethod)]
55134
public void Invoke(int i)
56135
{
57-
int yMin = this.info.MinY + (i * this.info.StepY);
136+
int yMin = this.minY + (i * this.stepY);
58137

59-
if (yMin >= this.info.MaxY)
138+
if (yMin >= this.maxY)
60139
{
61140
return;
62141
}
63142

64-
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
143+
int yMax = Math.Min(yMin + this.stepY, this.maxY);
65144
var rows = new RowInterval(yMin, yMax);
66145

67146
// Skip the safety copy when invoking a potentially impure method on a readonly field
@@ -73,35 +152,44 @@ private readonly struct RowIntervalOperationWrapper<T, TBuffer>
73152
where T : struct, IRowIntervalOperation<TBuffer>
74153
where TBuffer : unmanaged
75154
{
76-
private readonly IterationParameters info;
155+
private readonly int minY;
156+
private readonly int maxY;
157+
private readonly int stepY;
158+
private readonly int width;
77159
private readonly MemoryAllocator allocator;
78160
private readonly T operation;
79161

80162
[MethodImpl(InliningOptions.ShortMethod)]
81163
public RowIntervalOperationWrapper(
82-
in IterationParameters info,
164+
int minY,
165+
int maxY,
166+
int stepY,
167+
int width,
83168
MemoryAllocator allocator,
84169
in T operation)
85170
{
86-
this.info = info;
171+
this.minY = minY;
172+
this.maxY = maxY;
173+
this.stepY = stepY;
174+
this.width = width;
87175
this.allocator = allocator;
88176
this.operation = operation;
89177
}
90178

91179
[MethodImpl(InliningOptions.ShortMethod)]
92180
public void Invoke(int i)
93181
{
94-
int yMin = this.info.MinY + (i * this.info.StepY);
182+
int yMin = this.minY + (i * this.stepY);
95183

96-
if (yMin >= this.info.MaxY)
184+
if (yMin >= this.maxY)
97185
{
98186
return;
99187
}
100188

101-
int yMax = Math.Min(yMin + this.info.StepY, this.info.MaxY);
189+
int yMax = Math.Min(yMin + this.stepY, this.maxY);
102190
var rows = new RowInterval(yMin, yMax);
103191

104-
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.info.Width);
192+
using IMemoryOwner<TBuffer> buffer = this.allocator.Allocate<TBuffer>(this.width);
105193

106194
Unsafe.AsRef(in this.operation).Invoke(in rows, buffer.Memory.Span);
107195
}

0 commit comments

Comments
 (0)