Skip to content

Commit a7b9258

Browse files
Merge branch 'main' into dependabot/github_actions/NuGet/setup-nuget-4
2 parents bace120 + 936a65b commit a7b9258

50 files changed

Lines changed: 521 additions & 665 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ImageSharp/Advanced/AdvancedImageExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ public static void AcceptVisitor(this Image source, IImageVisitor visitor)
7676
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor, CancellationToken cancellationToken = default)
7777
=> source.AcceptAsync(visitor, cancellationToken);
7878

79+
/// <summary>
80+
/// Accepts a <see cref="IImageVisitor"/> to implement a double-dispatch pattern in order to
81+
/// apply pixel-specific operations on non-generic <see cref="Image"/> instances
82+
/// </summary>
83+
/// <param name="source">The source image frame.</param>
84+
/// <param name="visitor">The image visitor.</param>
85+
public static void AcceptVisitor(this ImageFrame source, IImageFrameVisitor visitor)
86+
=> source.Accept(visitor);
87+
7988
/// <summary>
8089
/// Gets the representation of the pixels as a <see cref="IMemoryGroup{T}"/> containing the backing pixel data of the image
8190
/// stored in row major order, as a list of contiguous <see cref="Memory{T}"/> blocks in the source image's pixel format.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.PixelFormats;
5+
6+
namespace SixLabors.ImageSharp.Advanced;
7+
8+
/// <summary>
9+
/// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations
10+
/// on non-generic <see cref="ImageFrame"/> instances.
11+
/// </summary>
12+
public interface IImageFrameVisitor
13+
{
14+
/// <summary>
15+
/// Provides a pixel-specific implementation for a given operation.
16+
/// </summary>
17+
/// <param name="frame">The image frame.</param>
18+
/// <typeparam name="TPixel">The pixel type.</typeparam>
19+
public void Visit<TPixel>(ImageFrame<TPixel> frame)
20+
where TPixel : unmanaged, IPixel<TPixel>;
21+
}

src/ImageSharp/Advanced/IImageVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public interface IImageVisitor
1616
/// </summary>
1717
/// <param name="image">The image.</param>
1818
/// <typeparam name="TPixel">The pixel type.</typeparam>
19-
void Visit<TPixel>(Image<TPixel> image)
19+
public void Visit<TPixel>(Image<TPixel> image)
2020
where TPixel : unmanaged, IPixel<TPixel>;
2121
}
2222

@@ -33,6 +33,6 @@ public interface IImageVisitorAsync
3333
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
3434
/// <typeparam name="TPixel">The pixel type.</typeparam>
3535
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
36-
Task VisitAsync<TPixel>(Image<TPixel> image, CancellationToken cancellationToken)
36+
public Task VisitAsync<TPixel>(Image<TPixel> image, CancellationToken cancellationToken)
3737
where TPixel : unmanaged, IPixel<TPixel>;
3838
}

src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ internal class NoneExrCompressor : ExrBaseCompressor
1717
/// <param name="allocator">The memory allocator.</param>
1818
/// <param name="bytesPerBlock">Bytes per row block.</param>
1919
/// <param name="bytesPerRow">Bytes per pixel row.</param>
20-
public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
21-
: base(output, allocator, bytesPerBlock, bytesPerRow)
20+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
21+
/// <param name="width">The witdh of one row in pixels.</param>
22+
public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
23+
: base(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
2224
{
2325
}
2426

src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ internal class ZipExrCompressor : ExrBaseCompressor
2424
/// <param name="allocator">The memory allocator.</param>
2525
/// <param name="bytesPerBlock">The bytes per block.</param>
2626
/// <param name="bytesPerRow">The bytes per row.</param>
27+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
28+
/// <param name="width">The witdh of one row in pixels.</param>
2729
/// <param name="compressionLevel">The compression level for deflate compression.</param>
28-
public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, DeflateCompressionLevel compressionLevel)
29-
: base(output, allocator, bytesPerBlock, bytesPerRow)
30+
public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, DeflateCompressionLevel compressionLevel)
31+
: base(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
3032
{
3133
this.compressionLevel = compressionLevel;
3234
this.buffer = allocator.Allocate<byte>((int)bytesPerBlock);

src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
1313
/// </summary>
1414
internal class B44ExrCompression : ExrBaseDecompressor
1515
{
16-
private readonly int width;
17-
18-
private readonly uint rowsPerBlock;
19-
2016
private readonly int channelCount;
2117

2218
private readonly byte[] scratch = new byte[14];
@@ -31,14 +27,12 @@ internal class B44ExrCompression : ExrBaseDecompressor
3127
/// <param name="allocator">The memory allocator.</param>
3228
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
3329
/// <param name="bytesPerRow">The bytes per row.</param>
34-
/// <param name="rowsPerBlock">The rows per block.</param>
30+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
3531
/// <param name="width">The width of a pixel row in pixels.</param>
3632
/// <param name="channelCount">The number of channels of the image.</param>
3733
public B44ExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount)
38-
: base(allocator, bytesPerBlock, bytesPerRow)
34+
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
3935
{
40-
this.width = width;
41-
this.rowsPerBlock = rowsPerBlock;
4236
this.channelCount = channelCount;
4337
this.tmpBuffer = allocator.Allocate<ushort>((int)(width * rowsPerBlock * channelCount));
4438
}
@@ -52,26 +46,27 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
5246
int bytesLeft = (int)compressedBytes;
5347
for (int i = 0; i < this.channelCount && bytesLeft > 0; i++)
5448
{
55-
for (int y = 0; y < this.rowsPerBlock; y += 4)
49+
for (int y = 0; y < this.RowsPerBlock; y += 4)
5650
{
57-
Span<ushort> row0 = decompressed.Slice(outputOffset, this.width);
58-
outputOffset += this.width;
59-
Span<ushort> row1 = decompressed.Slice(outputOffset, this.width);
60-
outputOffset += this.width;
61-
Span<ushort> row2 = decompressed.Slice(outputOffset, this.width);
62-
outputOffset += this.width;
63-
Span<ushort> row3 = decompressed.Slice(outputOffset, this.width);
64-
outputOffset += this.width;
51+
Span<ushort> row0 = decompressed.Slice(outputOffset, this.Width);
52+
outputOffset += this.Width;
53+
Span<ushort> row1 = decompressed.Slice(outputOffset, this.Width);
54+
outputOffset += this.Width;
55+
Span<ushort> row2 = decompressed.Slice(outputOffset, this.Width);
56+
outputOffset += this.Width;
57+
Span<ushort> row3 = decompressed.Slice(outputOffset, this.Width);
58+
outputOffset += this.Width;
6559

6660
int rowOffset = 0;
67-
for (int x = 0; x < this.width && bytesLeft > 0; x += 4)
61+
for (int x = 0; x < this.Width && bytesLeft > 0; x += 4)
6862
{
6963
int bytesRead = stream.Read(this.scratch, 0, 3);
7064
if (bytesRead == 0)
7165
{
7266
ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream!");
7367
}
7468

69+
// Check if 3-byte encoded flat field.
7570
if (this.scratch[2] >= 13 << 2)
7671
{
7772
Unpack3(this.scratch, this.s);
@@ -89,8 +84,8 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
8984
bytesLeft -= 14;
9085
}
9186

92-
int n = x + 3 < this.width ? 4 : this.width - x;
93-
if (y + 3 < this.rowsPerBlock)
87+
int n = x + 3 < this.Width ? 4 : this.Width - x;
88+
if (y + 3 < this.RowsPerBlock)
9489
{
9590
this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]);
9691
this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]);
@@ -100,12 +95,12 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
10095
else
10196
{
10297
this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]);
103-
if (y + 1 < this.rowsPerBlock)
98+
if (y + 1 < this.RowsPerBlock)
10499
{
105100
this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]);
106101
}
107102

108-
if (y + 2 < this.rowsPerBlock)
103+
if (y + 2 < this.RowsPerBlock)
109104
{
110105
this.s.AsSpan(8, n).CopyTo(row2[rowOffset..]);
111106
}
@@ -124,16 +119,16 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
124119
// Rearrange the decompressed data such that the data for each scan line form a contiguous block.
125120
int offsetDecompressed = 0;
126121
int offsetOutput = 0;
127-
int blockSize = (int)(this.width * this.rowsPerBlock);
128-
for (int y = 0; y < this.rowsPerBlock; y++)
122+
int blockSize = (int)(this.Width * this.RowsPerBlock);
123+
for (int y = 0; y < this.RowsPerBlock; y++)
129124
{
130125
for (int i = 0; i < this.channelCount; i++)
131126
{
132-
decompressed.Slice(offsetDecompressed + (i * blockSize), this.width).CopyTo(outputBuffer[offsetOutput..]);
133-
offsetOutput += this.width;
127+
decompressed.Slice(offsetDecompressed + (i * blockSize), this.Width).CopyTo(outputBuffer[offsetOutput..]);
128+
offsetOutput += this.Width;
134129
}
135130

136-
offsetDecompressed += this.width;
131+
offsetDecompressed += this.Width;
137132
}
138133
}
139134

src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ internal class NoneExrCompression : ExrBaseDecompressor
1717
/// <param name="allocator">The memory allocator.</param>
1818
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
1919
/// <param name="bytesPerRow">The bytes per pixel row.</param>
20-
public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
21-
: base(allocator, bytesPerBlock, bytesPerRow)
20+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
21+
/// <param name="width">The number of pixels per row.</param>
22+
public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
23+
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
2224
{
2325
}
2426

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers;
5+
using System.Runtime.InteropServices;
6+
using SixLabors.ImageSharp.Formats.Exr.Constants;
7+
using SixLabors.ImageSharp.IO;
8+
using SixLabors.ImageSharp.Memory;
9+
10+
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
11+
12+
/// <summary>
13+
/// Implementation of PXR24 decompressor for EXR image data.
14+
/// </summary>
15+
internal class Pxr24Compression : ExrBaseDecompressor
16+
{
17+
private readonly IMemoryOwner<byte> tmpBuffer;
18+
19+
private readonly int channelCount;
20+
21+
private readonly ExrPixelType pixelType;
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="Pxr24Compression" /> class.
25+
/// </summary>
26+
/// <param name="allocator">The memory allocator.</param>
27+
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
28+
/// <param name="bytesPerRow">The bytes per pixel row.</param>
29+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
30+
/// <param name="width">The witdh of one row in pixels.</param>
31+
/// <param name="channelCount">The number of channels for a pixel.</param>
32+
/// <param name="pixelType">The pixel type.</param>
33+
public Pxr24Compression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount, ExrPixelType pixelType)
34+
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
35+
{
36+
this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
37+
this.channelCount = channelCount;
38+
this.pixelType = pixelType;
39+
}
40+
41+
/// <inheritdoc/>
42+
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
43+
{
44+
Span<byte> uncompressed = this.tmpBuffer.GetSpan();
45+
Span<ushort> outputBufferHalf = MemoryMarshal.Cast<byte, ushort>(buffer);
46+
Span<uint> outputBufferFloat = MemoryMarshal.Cast<byte, uint>(buffer);
47+
Span<uint> outputBufferUint = MemoryMarshal.Cast<byte, uint>(buffer);
48+
49+
uint uncompressedBytes = this.BytesPerBlock;
50+
UndoZipCompression(stream, compressedBytes, uncompressed, uncompressedBytes);
51+
52+
int lastIn = 0;
53+
int outputOffset = 0;
54+
for (int y = 0; y < this.RowsPerBlock; y++)
55+
{
56+
for (int c = 0; c < this.channelCount; c++)
57+
{
58+
switch (this.pixelType)
59+
{
60+
case ExrPixelType.UnsignedInt:
61+
{
62+
int offsetT0 = lastIn;
63+
lastIn += this.Width;
64+
int offsetT1 = lastIn;
65+
lastIn += this.Width;
66+
int offsetT2 = lastIn;
67+
lastIn += this.Width;
68+
int offsetT3 = lastIn;
69+
lastIn += this.Width;
70+
71+
uint pixel = 0;
72+
for (int x = 0; x < this.Width; x++)
73+
{
74+
uint t0 = uncompressed[offsetT0];
75+
uint t1 = uncompressed[offsetT1];
76+
uint t2 = uncompressed[offsetT2];
77+
uint t3 = uncompressed[offsetT3];
78+
uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8) | t3;
79+
80+
pixel += diff;
81+
outputBufferUint[outputOffset] = pixel;
82+
83+
offsetT0++;
84+
offsetT1++;
85+
offsetT2++;
86+
offsetT3++;
87+
outputOffset++;
88+
}
89+
90+
break;
91+
}
92+
93+
case ExrPixelType.Half:
94+
{
95+
int offsetT0 = lastIn;
96+
lastIn += this.Width;
97+
int offsetT1 = lastIn;
98+
lastIn += this.Width;
99+
100+
uint pixel = 0;
101+
for (int x = 0; x < this.Width; x++)
102+
{
103+
uint t0 = uncompressed[offsetT0];
104+
uint t1 = uncompressed[offsetT1];
105+
uint diff = (t0 << 8) | t1;
106+
107+
pixel += diff;
108+
outputBufferHalf[outputOffset] = (ushort)pixel;
109+
110+
offsetT0++;
111+
offsetT1++;
112+
outputOffset++;
113+
}
114+
115+
break;
116+
}
117+
118+
case ExrPixelType.Float:
119+
{
120+
int offsetT0 = lastIn;
121+
lastIn += this.Width;
122+
int offsetT1 = lastIn;
123+
lastIn += this.Width;
124+
int offsetT2 = lastIn;
125+
lastIn += this.Width;
126+
127+
uint pixel = 0;
128+
for (int x = 0; x < this.Width; x++)
129+
{
130+
uint t0 = uncompressed[offsetT0];
131+
uint t1 = uncompressed[offsetT1];
132+
uint t2 = uncompressed[offsetT2];
133+
uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8);
134+
135+
pixel += diff;
136+
outputBufferFloat[outputOffset] = pixel;
137+
138+
offsetT0++;
139+
offsetT1++;
140+
offsetT2++;
141+
outputOffset++;
142+
}
143+
144+
break;
145+
}
146+
}
147+
}
148+
}
149+
}
150+
151+
/// <inheritdoc/>
152+
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose();
153+
}

src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ internal class RunLengthExrCompression : ExrBaseDecompressor
1414
{
1515
private readonly IMemoryOwner<byte> tmpBuffer;
1616

17-
private readonly ushort[] s = new ushort[16];
18-
1917
/// <summary>
2018
/// Initializes a new instance of the <see cref="RunLengthExrCompression" /> class.
2119
/// </summary>
2220
/// <param name="allocator">The memory allocator.</param>
2321
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
2422
/// <param name="bytesPerRow">The bytes per row.</param>
25-
public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
26-
: base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
23+
/// <param name="rowsPerBlock">The pixel rows per block.</param>
24+
/// <param name="width">The witdh of one row in pixels.</param>
25+
public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
26+
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
2727

2828
/// <inheritdoc/>
2929
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)

0 commit comments

Comments
 (0)