Browse Source

Add Exr compressor factory

pull/3096/head
Brian Popow 2 months ago
parent
commit
137ebbba06
  1. 8
      src/ImageSharp/Formats/Exr/Compression/Compressors/NoCompressor.cs
  2. 47
      src/ImageSharp/Formats/Exr/Compression/Compressors/ZipCompressor.cs
  3. 4
      src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs
  4. 6
      src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
  5. 6
      src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs
  6. 6
      src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
  7. 23
      src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
  8. 4
      src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
  9. 26
      src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
  10. 23
      src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
  11. 7
      src/ImageSharp/Formats/Exr/Constants/ExrCompression.cs
  12. 43
      src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
  13. 26
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  14. 5
      src/ImageSharp/Formats/Exr/ExrEncoder.cs
  15. 5
      src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
  16. 5
      src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs
  17. 11
      src/ImageSharp/Formats/Exr/ExrThrowHelper.cs

8
src/ImageSharp/Formats/Exr/Compression/Compressors/NoCompressor.cs

@ -0,0 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
internal class NoCompressor
{
}

47
src/ImageSharp/Formats/Exr/Compression/Compressors/ZipCompressor.cs

@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
internal class ZipCompressor : ExrBaseCompressor
{
private readonly DeflateCompressionLevel compressionLevel;
private readonly MemoryStream memoryStream = new();
public ZipCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, DeflateCompressionLevel compressionLevel)
: base(output, allocator, bytesPerBlock)
=> this.compressionLevel = compressionLevel;
/// <inheritdoc/>
public override ExrCompression Method => ExrCompression.Zip;
/// <inheritdoc/>
public override void Initialize(int rowsPerStrip)
{
}
/// <inheritdoc/>
public override void CompressStrip(Span<byte> rows, int height)
{
this.memoryStream.Seek(0, SeekOrigin.Begin);
using (ZlibDeflateStream stream = new(this.Allocator, this.memoryStream, this.compressionLevel))
{
stream.Write(rows);
stream.Flush();
}
int size = (int)this.memoryStream.Position;
byte[] buffer = this.memoryStream.GetBuffer();
this.Output.Write(buffer, 0, size);
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
}
}

4
src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs

@ -24,8 +24,8 @@ internal class B44Compression : ExrBaseDecompressor
private IMemoryOwner<ushort> tmpBuffer; private IMemoryOwner<ushort> tmpBuffer;
public B44Compression(MemoryAllocator allocator, uint uncompressedBytes, int width, int height, uint rowsPerBlock, int channelCount) public B44Compression(MemoryAllocator allocator, uint bytesPerBlock, int width, int height, uint rowsPerBlock, int channelCount)
: base(allocator, uncompressedBytes) : base(allocator, bytesPerBlock)
{ {
this.width = width; this.width = width;
this.height = height; this.height = height;

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

@ -8,13 +8,13 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
internal class NoneExrCompression : ExrBaseDecompressor internal class NoneExrCompression : ExrBaseDecompressor
{ {
public NoneExrCompression(MemoryAllocator allocator, uint uncompressedBytes) public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock)
: base(allocator, uncompressedBytes) : base(allocator, bytesPerBlock)
{ {
} }
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer) public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
=> stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.UncompressedBytes)); => stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.BytesPerBlock));
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {

6
src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs

@ -19,7 +19,7 @@ internal class RunLengthCompression : ExrBaseDecompressor
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer) public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{ {
Span<byte> uncompressed = this.tmpBuffer.GetSpan(); Span<byte> uncompressed = this.tmpBuffer.GetSpan();
int maxLength = (int)this.UncompressedBytes; int maxLength = (int)this.BytesPerBlock;
int offset = 0; int offset = 0;
while (compressedBytes > 0) while (compressedBytes > 0)
{ {
@ -63,8 +63,8 @@ internal class RunLengthCompression : ExrBaseDecompressor
} }
} }
Reconstruct(uncompressed, this.UncompressedBytes); Reconstruct(uncompressed, this.BytesPerBlock);
Interleave(uncompressed, this.UncompressedBytes, buffer); Interleave(uncompressed, this.BytesPerBlock, buffer);
} }
private static byte ReadNextByte(BufferedReadStream stream) private static byte ReadNextByte(BufferedReadStream stream)

6
src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs

@ -13,8 +13,8 @@ internal class ZipExrCompression : ExrBaseDecompressor
{ {
private readonly IMemoryOwner<byte> tmpBuffer; private readonly IMemoryOwner<byte> tmpBuffer;
public ZipExrCompression(MemoryAllocator allocator, uint uncompressedBytes) public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock)
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes); : base(allocator, bytesPerBlock) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer) public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{ {
@ -28,7 +28,7 @@ internal class ZipExrCompression : ExrBaseDecompressor
int left = (int)(compressedBytes - (stream.Position - pos)); int left = (int)(compressedBytes - (stream.Position - pos));
return left > 0 ? left : 0; return left > 0 ? left : 0;
}); });
inflateStream.AllocateNewBytes((int)this.UncompressedBytes, true); inflateStream.AllocateNewBytes((int)this.BytesPerBlock, true);
DeflateStream dataStream = inflateStream.CompressedStream!; DeflateStream dataStream = inflateStream.CompressedStream!;
int totalRead = 0; int totalRead = 0;

23
src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs

@ -9,10 +9,10 @@ internal abstract class ExrBaseCompression : IDisposable
{ {
private bool isDisposed; private bool isDisposed;
protected ExrBaseCompression(MemoryAllocator allocator, uint bytePerRow) protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock)
{ {
this.Allocator = allocator; this.Allocator = allocator;
this.UncompressedBytes = bytePerRow; this.BytesPerBlock = bytesPerBlock;
} }
/// <summary> /// <summary>
@ -21,9 +21,24 @@ internal abstract class ExrBaseCompression : IDisposable
protected MemoryAllocator Allocator { get; } protected MemoryAllocator Allocator { get; }
/// <summary> /// <summary>
/// Gets the uncompressed bytes. /// Gets the bits per pixel.
/// </summary> /// </summary>
public uint UncompressedBytes { get; } public int BitsPerPixel { get; }
/// <summary>
/// Gets the bytes per row.
/// </summary>
public int BytesPerRow { get; }
/// <summary>
/// Gets the uncompressed bytes per block.
/// </summary>
public uint BytesPerBlock { get; }
/// <summary>
/// Gets the image width.
/// </summary>
public int Width { get; }
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public void Dispose()

4
src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs

@ -8,8 +8,8 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal abstract class ExrBaseDecompressor : ExrBaseCompression internal abstract class ExrBaseDecompressor : ExrBaseCompression
{ {
protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytePerRow) protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock)
: base(allocator, bytePerRow) : base(allocator, bytesPerBlock)
{ {
} }

26
src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal static class ExrCompressorFactory
{
public static ExrBaseCompressor Create(
ExrCompression method,
Stream output,
MemoryAllocator allocator,
int width,
DeflateCompressionLevel compressionLevel)
{
switch (method)
{
default:
throw ExrThrowHelper.NotSupportedCompressor(method.ToString());
}
}
}

23
src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs

@ -2,26 +2,27 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression; namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal static class ExrDecompressorFactory internal static class ExrDecompressorFactory
{ {
public static ExrBaseDecompressor Create(ExrCompressionType method, MemoryAllocator memoryAllocator, uint uncompressedBytes, int width, int height, uint rowsPerBlock, int channelCount) public static ExrBaseDecompressor Create(ExrCompression method, MemoryAllocator memoryAllocator, uint bytesPerBlock, int width, int height, uint rowsPerBlock, int channelCount)
{ {
switch (method) switch (method)
{ {
case ExrCompressionType.None: case ExrCompression.None:
return new NoneExrCompression(memoryAllocator, uncompressedBytes); return new NoneExrCompression(memoryAllocator, bytesPerBlock);
case ExrCompressionType.Zips: case ExrCompression.Zips:
return new ZipExrCompression(memoryAllocator, uncompressedBytes); return new ZipExrCompression(memoryAllocator, bytesPerBlock);
case ExrCompressionType.Zip: case ExrCompression.Zip:
return new ZipExrCompression(memoryAllocator, uncompressedBytes); return new ZipExrCompression(memoryAllocator, bytesPerBlock);
case ExrCompressionType.RunLengthEncoded: case ExrCompression.RunLengthEncoded:
return new RunLengthCompression(memoryAllocator, uncompressedBytes); return new RunLengthCompression(memoryAllocator, bytesPerBlock);
case ExrCompressionType.B44: case ExrCompression.B44:
return new B44Compression(memoryAllocator, uncompressedBytes, width, height, rowsPerBlock, channelCount); return new B44Compression(memoryAllocator, bytesPerBlock, width, height, rowsPerBlock, channelCount);
default: default:
throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)); throw ExrThrowHelper.NotSupportedDecompressor(nameof(method));
} }

7
src/ImageSharp/Formats/Exr/Compression/ExrCompressionType.cs → src/ImageSharp/Formats/Exr/Constants/ExrCompression.cs

@ -1,9 +1,12 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Exr.Compression; namespace SixLabors.ImageSharp.Formats.Exr.Constants;
internal enum ExrCompressionType /// <summary>
/// Enumeration representing the compression formats defined by the EXR file-format.
/// </summary>
public enum ExrCompression
{ {
/// <summary> /// <summary>
/// Pixel data is not compressed. /// Pixel data is not compressed.

43
src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs

@ -0,0 +1,43 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal abstract class ExrBaseCompressor : ExrBaseCompression
{
/// <summary>
/// Initializes a new instance of the <see cref="ExrBaseCompressor"/> class.
/// </summary>
/// <param name="output">The output stream to write the compressed image to.</param>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">Bytes per block.</param>
protected ExrBaseCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock)
: base(allocator, bytesPerBlock)
=> this.Output = output;
/// <summary>
/// Gets the compression method to use.
/// </summary>
public abstract ExrCompression Method { get; }
/// <summary>
/// Gets the output stream to write the compressed image to.
/// </summary>
public Stream Output { get; }
/// <summary>
/// Does any initialization required for the compression.
/// </summary>
/// <param name="rowsPerStrip">The number of rows per strip.</param>
public abstract void Initialize(int rowsPerStrip);
/// <summary>
/// Compresses a strip of the image.
/// </summary>
/// <param name="rows">Image rows to compress.</param>
/// <param name="height">Image height.</param>
public abstract void CompressStrip(Span<byte> rows, int height);
}

26
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -79,7 +79,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// <summary> /// <summary>
/// Gets or sets the compression method. /// Gets or sets the compression method.
/// </summary> /// </summary>
private ExrCompressionType Compression { get; set; } private ExrCompression Compression { get; set; }
/// <summary> /// <summary>
/// Gets or sets the image data type, either RGB, RGBA or gray. /// Gets or sets the image data type, either RGB, RGBA or gray.
@ -453,7 +453,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
IList<ExrChannelInfo> channels = null; IList<ExrChannelInfo> channels = null;
ExrBox2i? dataWindow = null; ExrBox2i? dataWindow = null;
ExrCompressionType? compression = null; ExrCompression? compression = null;
ExrBox2i? displayWindow = null; ExrBox2i? displayWindow = null;
ExrLineOrder? lineOrder = null; ExrLineOrder? lineOrder = null;
float? aspectRatio = null; float? aspectRatio = null;
@ -471,7 +471,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
channels = this.ReadChannelList(stream, attribute.Length); channels = this.ReadChannelList(stream, attribute.Length);
break; break;
case ExrConstants.AttributeNames.Compression: case ExrConstants.AttributeNames.Compression:
compression = (ExrCompressionType)stream.ReadByte(); compression = (ExrCompression)stream.ReadByte();
break; break;
case ExrConstants.AttributeNames.DataWindow: case ExrConstants.AttributeNames.DataWindow:
dataWindow = this.ReadBoxInteger(stream); dataWindow = this.ReadBoxInteger(stream);
@ -648,11 +648,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
{ {
switch (this.Compression) switch (this.Compression)
{ {
case ExrCompressionType.None: case ExrCompression.None:
case ExrCompressionType.Zip: case ExrCompression.Zip:
case ExrCompressionType.Zips: case ExrCompression.Zips:
case ExrCompressionType.RunLengthEncoded: case ExrCompression.RunLengthEncoded:
case ExrCompressionType.B44: case ExrCompression.B44:
return true; return true;
} }
@ -757,12 +757,12 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
{ {
switch (this.Compression) switch (this.Compression)
{ {
case ExrCompressionType.Zip: case ExrCompression.Zip:
case ExrCompressionType.Pxr24: case ExrCompression.Pxr24:
return 16; return 16;
case ExrCompressionType.B44: case ExrCompression.B44:
case ExrCompressionType.B44A: case ExrCompression.B44A:
case ExrCompressionType.Piz: case ExrCompression.Piz:
return 32; return 32;
default: default:

5
src/ImageSharp/Formats/Exr/ExrEncoder.cs

@ -15,6 +15,11 @@ public sealed class ExrEncoder : ImageEncoder
/// </summary> /// </summary>
public ExrPixelType? PixelType { get; set; } public ExrPixelType? PixelType { get; set; }
/// <summary>
/// Gets the compression type to use.
/// </summary>
public ExrCompression? Compression { get; init; }
/// <inheritdoc /> /// <inheritdoc />
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{ {

5
src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

@ -6,7 +6,6 @@ using System.Buffers.Binary;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Channels; using System.Threading.Channels;
using SixLabors.ImageSharp.Formats.Exr.Compression;
using SixLabors.ImageSharp.Formats.Exr.Constants; using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -77,7 +76,7 @@ internal sealed class ExrEncoderCore
this.pixelType ??= exrMetadata.PixelType; this.pixelType ??= exrMetadata.PixelType;
int width = image.Width; int width = image.Width;
int height = image.Height; int height = image.Height;
ExrCompressionType compression = ExrCompressionType.None; ExrCompression compression = ExrCompression.None;
float aspectRatio = 1.0f; float aspectRatio = 1.0f;
ExrBox2i dataWindow = new(0, 0, width - 1, height - 1); ExrBox2i dataWindow = new(0, 0, width - 1, height - 1);
ExrBox2i displayWindow = new(0, 0, width - 1, height - 1); ExrBox2i displayWindow = new(0, 0, width - 1, height - 1);
@ -311,7 +310,7 @@ internal sealed class ExrEncoderCore
stream.WriteByte(0); stream.WriteByte(0);
} }
private void WriteCompression(Stream stream, ExrCompressionType compression) private void WriteCompression(Stream stream, ExrCompression compression)
{ {
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1); this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1);
stream.WriteByte((byte)compression); stream.WriteByte((byte)compression);

5
src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Exr.Compression;
using SixLabors.ImageSharp.Formats.Exr.Constants; using SixLabors.ImageSharp.Formats.Exr.Constants;
namespace SixLabors.ImageSharp.Formats.Exr; namespace SixLabors.ImageSharp.Formats.Exr;
@ -10,7 +9,7 @@ internal class ExrHeaderAttributes
{ {
public ExrHeaderAttributes( public ExrHeaderAttributes(
IList<ExrChannelInfo> channels, IList<ExrChannelInfo> channels,
ExrCompressionType compression, ExrCompression compression,
ExrBox2i dataWindow, ExrBox2i dataWindow,
ExrBox2i displayWindow, ExrBox2i displayWindow,
ExrLineOrder lineOrder, ExrLineOrder lineOrder,
@ -36,7 +35,7 @@ internal class ExrHeaderAttributes
public IList<ExrChannelInfo> Channels { get; set; } public IList<ExrChannelInfo> Channels { get; set; }
public ExrCompressionType Compression { get; set; } public ExrCompression Compression { get; set; }
public ExrBox2i DataWindow { get; set; } public ExrBox2i DataWindow { get; set; }

11
src/ImageSharp/Formats/Exr/ExrThrowHelper.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
namespace SixLabors.ImageSharp.Formats.Exr; namespace SixLabors.ImageSharp.Formats.Exr;
/// <summary> /// <summary>
@ -8,15 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Exr;
/// </summary> /// </summary>
internal static class ExrThrowHelper internal static class ExrThrowHelper
{ {
[DoesNotReturn]
public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}");
[DoesNotReturn]
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
[DoesNotReturn]
public static void ThrowNotSupportedVersion() => throw new NotSupportedException("Unsupported EXR version"); public static void ThrowNotSupportedVersion() => throw new NotSupportedException("Unsupported EXR version");
[DoesNotReturn]
public static void ThrowNotSupported(string msg) => throw new NotSupportedException(msg); public static void ThrowNotSupported(string msg) => throw new NotSupportedException(msg);
[DoesNotReturn]
public static void ThrowInvalidImageHeader() => throw new InvalidImageContentException("Invalid EXR image header"); public static void ThrowInvalidImageHeader() => throw new InvalidImageContentException("Invalid EXR image header");
[DoesNotReturn]
public static void ThrowInvalidImageHeader(string msg) => throw new InvalidImageContentException(msg); public static void ThrowInvalidImageHeader(string msg) => throw new InvalidImageContentException(msg);
[DoesNotReturn]
public static Exception NotSupportedCompressor(string compressionType) => throw new NotSupportedException($"Not supported encoder compression method: {compressionType}");
} }

Loading…
Cancel
Save