mirror of https://github.com/SixLabors/ImageSharp
32 changed files with 1558 additions and 1690 deletions
@ -1,24 +1,22 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using SixLabors.ImageSharp.IO; |
using SixLabors.ImageSharp.IO; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
||||
|
|
||||
|
internal class NoneExrCompression : ExrBaseDecompressor |
||||
{ |
{ |
||||
internal class NoneExrCompression : ExrBaseDecompressor |
public NoneExrCompression(MemoryAllocator allocator, uint uncompressedBytes) |
||||
|
: base(allocator, uncompressedBytes) |
||||
{ |
{ |
||||
public NoneExrCompression(MemoryAllocator allocator, uint uncompressedBytes) |
} |
||||
: base(allocator, uncompressedBytes) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
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.UncompressedBytes)); |
||||
|
|
||||
protected override void Dispose(bool disposing) |
protected override void Dispose(bool disposing) |
||||
{ |
{ |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,94 +1,92 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
using System.Buffers; |
||||
using SixLabors.ImageSharp.IO; |
using SixLabors.ImageSharp.IO; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
||||
|
|
||||
|
internal class RunLengthCompression : ExrBaseDecompressor |
||||
{ |
{ |
||||
internal class RunLengthCompression : ExrBaseDecompressor |
private readonly IMemoryOwner<byte> tmpBuffer; |
||||
{ |
|
||||
private readonly IMemoryOwner<byte> tmpBuffer; |
|
||||
|
|
||||
public RunLengthCompression(MemoryAllocator allocator, uint uncompressedBytes) |
public RunLengthCompression(MemoryAllocator allocator, uint uncompressedBytes) |
||||
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes); |
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes); |
||||
|
|
||||
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(); |
||||
|
int maxLength = (int)this.UncompressedBytes; |
||||
|
int offset = 0; |
||||
|
while (compressedBytes > 0) |
||||
{ |
{ |
||||
Span<byte> uncompressed = this.tmpBuffer.GetSpan(); |
byte nextByte = ReadNextByte(stream); |
||||
int maxLength = (int)this.UncompressedBytes; |
|
||||
int offset = 0; |
sbyte input = (sbyte)nextByte; |
||||
while (compressedBytes > 0) |
if (input < 0) |
||||
{ |
{ |
||||
byte nextByte = ReadNextByte(stream); |
int count = -input; |
||||
|
compressedBytes -= (uint)(count + 1); |
||||
|
|
||||
sbyte input = (sbyte)nextByte; |
if ((maxLength -= count) < 0) |
||||
if (input < 0) |
|
||||
{ |
{ |
||||
int count = -input; |
return; |
||||
compressedBytes -= (uint)(count + 1); |
|
||||
|
|
||||
if ((maxLength -= count) < 0) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Check the input buffer is big enough to contain 'count' bytes of remaining data.
|
|
||||
if (compressedBytes < 0) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
for (int i = 0; i < count; i++) |
|
||||
{ |
|
||||
uncompressed[offset + i] = ReadNextByte(stream); |
|
||||
} |
|
||||
|
|
||||
offset += count; |
|
||||
} |
} |
||||
else |
|
||||
|
// Check the input buffer is big enough to contain 'count' bytes of remaining data.
|
||||
|
if (compressedBytes < 0) |
||||
{ |
{ |
||||
int count = input; |
return; |
||||
byte value = ReadNextByte(stream); |
|
||||
compressedBytes -= 2; |
|
||||
|
|
||||
if ((maxLength -= count + 1) < 0) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Check the input buffer is big enough to contain byte to be duplicated.
|
|
||||
if (compressedBytes < 0) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
for (int i = 0; i < count + 1; i++) |
|
||||
{ |
|
||||
uncompressed[offset + i] = value; |
|
||||
} |
|
||||
|
|
||||
offset += count + 1; |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
Reconstruct(uncompressed, this.UncompressedBytes); |
for (int i = 0; i < count; i++) |
||||
Interleave(uncompressed, this.UncompressedBytes, buffer); |
{ |
||||
} |
uncompressed[offset + i] = ReadNextByte(stream); |
||||
|
} |
||||
|
|
||||
private static byte ReadNextByte(BufferedReadStream stream) |
offset += count; |
||||
{ |
} |
||||
int nextByte = stream.ReadByte(); |
else |
||||
if (nextByte == -1) |
|
||||
{ |
{ |
||||
ExrThrowHelper.ThrowInvalidImageContentException("Not enough data to decompress RLE image!"); |
int count = input; |
||||
|
byte value = ReadNextByte(stream); |
||||
|
compressedBytes -= 2; |
||||
|
|
||||
|
if ((maxLength -= count + 1) < 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Check the input buffer is big enough to contain byte to be duplicated.
|
||||
|
if (compressedBytes < 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < count + 1; i++) |
||||
|
{ |
||||
|
uncompressed[offset + i] = value; |
||||
|
} |
||||
|
|
||||
|
offset += count + 1; |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
Reconstruct(uncompressed, this.UncompressedBytes); |
||||
|
Interleave(uncompressed, this.UncompressedBytes, buffer); |
||||
|
} |
||||
|
|
||||
return (byte)nextByte; |
private static byte ReadNextByte(BufferedReadStream stream) |
||||
|
{ |
||||
|
int nextByte = stream.ReadByte(); |
||||
|
if (nextByte == -1) |
||||
|
{ |
||||
|
ExrThrowHelper.ThrowInvalidImageContentException("Not enough data to decompress RLE image!"); |
||||
} |
} |
||||
|
|
||||
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose(); |
return (byte)nextByte; |
||||
} |
} |
||||
|
|
||||
|
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose(); |
||||
} |
} |
||||
|
|||||
@ -1,58 +1,56 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
using System.Buffers; |
||||
using System.IO.Compression; |
using System.IO.Compression; |
||||
using SixLabors.ImageSharp.Compression.Zlib; |
using SixLabors.ImageSharp.Compression.Zlib; |
||||
using SixLabors.ImageSharp.IO; |
using SixLabors.ImageSharp.IO; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
||||
|
|
||||
|
internal class ZipExrCompression : ExrBaseDecompressor |
||||
{ |
{ |
||||
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 uncompressedBytes) |
||||
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes); |
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes); |
||||
|
|
||||
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(); |
||||
|
|
||||
|
long pos = stream.Position; |
||||
|
using ZlibInflateStream deframeStream = new( |
||||
|
stream, |
||||
|
() => |
||||
|
{ |
||||
|
int left = (int)(compressedBytes - (stream.Position - pos)); |
||||
|
return left > 0 ? left : 0; |
||||
|
}); |
||||
|
deframeStream.AllocateNewBytes((int)this.UncompressedBytes, true); |
||||
|
DeflateStream dataStream = deframeStream.CompressedStream; |
||||
|
|
||||
|
int totalRead = 0; |
||||
|
while (totalRead < buffer.Length) |
||||
{ |
{ |
||||
Span<byte> uncompressed = this.tmpBuffer.GetSpan(); |
int bytesRead = dataStream.Read(uncompressed, totalRead, buffer.Length - totalRead); |
||||
|
if (bytesRead <= 0) |
||||
long pos = stream.Position; |
|
||||
using var deframeStream = new ZlibInflateStream( |
|
||||
stream, |
|
||||
() => |
|
||||
{ |
|
||||
int left = (int)(compressedBytes - (stream.Position - pos)); |
|
||||
return left > 0 ? left : 0; |
|
||||
}); |
|
||||
deframeStream.AllocateNewBytes((int)this.UncompressedBytes, true); |
|
||||
DeflateStream dataStream = deframeStream.CompressedStream; |
|
||||
|
|
||||
int totalRead = 0; |
|
||||
while (totalRead < buffer.Length) |
|
||||
{ |
{ |
||||
int bytesRead = dataStream.Read(uncompressed, totalRead, buffer.Length - totalRead); |
break; |
||||
if (bytesRead <= 0) |
|
||||
{ |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
totalRead += bytesRead; |
|
||||
} |
} |
||||
|
|
||||
if (totalRead == 0) |
totalRead += bytesRead; |
||||
{ |
} |
||||
ExrThrowHelper.ThrowInvalidImageContentException("Could not read zip compressed image data!"); |
|
||||
} |
|
||||
|
|
||||
Reconstruct(uncompressed, (uint)totalRead); |
if (totalRead == 0) |
||||
Interleave(uncompressed, (uint)totalRead, buffer); |
{ |
||||
|
ExrThrowHelper.ThrowInvalidImageContentException("Could not read zip compressed image data!"); |
||||
} |
} |
||||
|
|
||||
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose(); |
Reconstruct(uncompressed, (uint)totalRead); |
||||
|
Interleave(uncompressed, (uint)totalRead, buffer); |
||||
} |
} |
||||
|
|
||||
|
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose(); |
||||
} |
} |
||||
|
|||||
@ -1,43 +1,41 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
|
|
||||
|
internal abstract class ExrBaseCompression : IDisposable |
||||
{ |
{ |
||||
internal abstract class ExrBaseCompression : IDisposable |
private bool isDisposed; |
||||
{ |
|
||||
private bool isDisposed; |
|
||||
|
|
||||
protected ExrBaseCompression(MemoryAllocator allocator, uint bytePerRow) |
protected ExrBaseCompression(MemoryAllocator allocator, uint bytePerRow) |
||||
{ |
{ |
||||
this.Allocator = allocator; |
this.Allocator = allocator; |
||||
this.UncompressedBytes = bytePerRow; |
this.UncompressedBytes = bytePerRow; |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets the memory allocator.
|
/// Gets the memory allocator.
|
||||
/// </summary>
|
/// </summary>
|
||||
protected MemoryAllocator Allocator { get; } |
protected MemoryAllocator Allocator { get; } |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets the uncompressed bytes.
|
/// Gets the uncompressed bytes.
|
||||
/// </summary>
|
/// </summary>
|
||||
public uint UncompressedBytes { get; } |
public uint UncompressedBytes { get; } |
||||
|
|
||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||
public void Dispose() |
public void Dispose() |
||||
|
{ |
||||
|
if (this.isDisposed) |
||||
{ |
{ |
||||
if (this.isDisposed) |
return; |
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
this.isDisposed = true; |
|
||||
this.Dispose(true); |
|
||||
} |
} |
||||
|
|
||||
protected abstract void Dispose(bool disposing); |
this.isDisposed = true; |
||||
|
this.Dispose(true); |
||||
} |
} |
||||
|
|
||||
|
protected abstract void Dispose(bool disposing); |
||||
} |
} |
||||
|
|||||
@ -1,42 +1,40 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using SixLabors.ImageSharp.IO; |
using SixLabors.ImageSharp.IO; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
|
|
||||
|
internal abstract class ExrBaseDecompressor : ExrBaseCompression |
||||
{ |
{ |
||||
internal abstract class ExrBaseDecompressor : ExrBaseCompression |
protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytePerRow) |
||||
|
: base(allocator, bytePerRow) |
||||
{ |
{ |
||||
protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytePerRow) |
} |
||||
: base(allocator, bytePerRow) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer); |
public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer); |
||||
|
|
||||
protected static void Reconstruct(Span<byte> buffer, uint unCompressedBytes) |
protected static void Reconstruct(Span<byte> buffer, uint unCompressedBytes) |
||||
|
{ |
||||
|
int offset = 0; |
||||
|
for (int i = 0; i < unCompressedBytes - 1; i++) |
||||
{ |
{ |
||||
int offset = 0; |
byte d = (byte)(buffer[offset] + (buffer[offset + 1] - 128)); |
||||
for (int i = 0; i < unCompressedBytes - 1; i++) |
buffer[offset + 1] = d; |
||||
{ |
offset++; |
||||
byte d = (byte)(buffer[offset] + (buffer[offset + 1] - 128)); |
|
||||
buffer[offset + 1] = d; |
|
||||
offset++; |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
protected static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output) |
protected static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output) |
||||
|
{ |
||||
|
int sourceOffset = 0; |
||||
|
int offset0 = 0; |
||||
|
int offset1 = (int)((unCompressedBytes + 1) / 2); |
||||
|
while (sourceOffset < unCompressedBytes) |
||||
{ |
{ |
||||
int sourceOffset = 0; |
output[sourceOffset++] = source[offset0++]; |
||||
int offset0 = 0; |
output[sourceOffset++] = source[offset1++]; |
||||
int offset1 = (int)((unCompressedBytes + 1) / 2); |
|
||||
while (sourceOffset < unCompressedBytes) |
|
||||
{ |
|
||||
output[sourceOffset++] = source[offset0++]; |
|
||||
output[sourceOffset++] = source[offset1++]; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,61 +1,60 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
|
|
||||
|
internal enum ExrCompressionType |
||||
{ |
{ |
||||
internal enum ExrCompressionType |
/// <summary>
|
||||
{ |
/// Pixel data is not compressed.
|
||||
/// <summary>
|
/// </summary>
|
||||
/// Pixel data is not compressed.
|
None = 0, |
||||
/// </summary>
|
|
||||
None = 0, |
/// <summary>
|
||||
|
/// Differences between horizontally adjacent pixels are run-length encoded.
|
||||
/// <summary>
|
/// This method is fast, and works well for images with large flat areas, but for photographic images,
|
||||
/// Differences between horizontally adjacent pixels are run-length encoded.
|
/// the compressed file size is usually between 60 and 75 percent of the uncompressed size.
|
||||
/// This method is fast, and works well for images with large flat areas, but for photographic images,
|
/// Compression is lossless.
|
||||
/// the compressed file size is usually between 60 and 75 percent of the uncompressed size.
|
/// </summary>
|
||||
/// Compression is lossless.
|
RunLengthEncoded = 1, |
||||
/// </summary>
|
|
||||
RunLengthEncoded = 1, |
/// <summary>
|
||||
|
/// Uses the open source zlib library for compression. Unlike ZIP compression, this operates one scan line at a time.
|
||||
/// <summary>
|
/// Compression is lossless.
|
||||
/// Uses the open source zlib library for compression. Unlike ZIP compression, this operates one scan line at a time.
|
/// </summary>
|
||||
/// Compression is lossless.
|
Zips = 2, |
||||
/// </summary>
|
|
||||
Zips = 2, |
/// <summary>
|
||||
|
/// Differences between horizontally adjacent pixels are compressed using the open source zlib library.
|
||||
/// <summary>
|
/// Unlike ZIPS compression, this operates in in blocks of 16 scan lines.
|
||||
/// Differences between horizontally adjacent pixels are compressed using the open source zlib library.
|
/// Compression is lossless.
|
||||
/// Unlike ZIPS compression, this operates in in blocks of 16 scan lines.
|
/// </summary>
|
||||
/// Compression is lossless.
|
Zip = 3, |
||||
/// </summary>
|
|
||||
Zip = 3, |
/// <summary>
|
||||
|
/// A wavelet transform is applied to the pixel data, and the result is Huffman-encoded.
|
||||
/// <summary>
|
/// Compression is lossless.
|
||||
/// A wavelet transform is applied to the pixel data, and the result is Huffman-encoded.
|
/// </summary>
|
||||
/// Compression is lossless.
|
Piz = 4, |
||||
/// </summary>
|
|
||||
Piz = 4, |
/// <summary>
|
||||
|
/// After reducing 32-bit floating-point data to 24 bits by rounding, differences between horizontally adjacent pixels are compressed with zlib,
|
||||
/// <summary>
|
/// similar to ZIP. PXR24 compression preserves image channels of type HALF and UINT exactly, but the relative error of FLOAT data increases to about 3×10-5.
|
||||
/// After reducing 32-bit floating-point data to 24 bits by rounding, differences between horizontally adjacent pixels are compressed with zlib,
|
/// Compression is lossy.
|
||||
/// similar to ZIP. PXR24 compression preserves image channels of type HALF and UINT exactly, but the relative error of FLOAT data increases to about 3×10-5.
|
/// </summary>
|
||||
/// Compression is lossy.
|
Pxr24 = 5, |
||||
/// </summary>
|
|
||||
Pxr24 = 5, |
/// <summary>
|
||||
|
/// Channels of type HALF are split into blocks of four by four pixels or 32 bytes. Each block is then packed into 14 bytes,
|
||||
/// <summary>
|
/// reducing the data to 44 percent of their uncompressed size.
|
||||
/// Channels of type HALF are split into blocks of four by four pixels or 32 bytes. Each block is then packed into 14 bytes,
|
/// Compression is lossy.
|
||||
/// reducing the data to 44 percent of their uncompressed size.
|
/// </summary>
|
||||
/// Compression is lossy.
|
B44 = 6, |
||||
/// </summary>
|
|
||||
B44 = 6, |
/// <summary>
|
||||
|
/// Like B44, except for blocks of four by four pixels where all pixels have the same value, which are packed into 3 instead of 14 bytes.
|
||||
/// <summary>
|
/// For images with large uniform areas, B44A produces smaller files than B44 compression.
|
||||
/// Like B44, except for blocks of four by four pixels where all pixels have the same value, which are packed into 3 instead of 14 bytes.
|
/// Compression is lossy.
|
||||
/// For images with large uniform areas, B44A produces smaller files than B44 compression.
|
/// </summary>
|
||||
/// Compression is lossy.
|
B44A = 7 |
||||
/// </summary>
|
|
||||
B44A = 7 |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,28 +1,27 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
using SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression |
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
|
|
||||
|
internal static class ExrDecompressorFactory |
||||
{ |
{ |
||||
internal static class ExrDecompressorFactory |
public static ExrBaseDecompressor Create(ExrCompressionType method, MemoryAllocator memoryAllocator, uint uncompressedBytes) |
||||
{ |
{ |
||||
public static ExrBaseDecompressor Create(ExrCompressionType method, MemoryAllocator memoryAllocator, uint uncompressedBytes) |
switch (method) |
||||
{ |
{ |
||||
switch (method) |
case ExrCompressionType.None: |
||||
{ |
return new NoneExrCompression(memoryAllocator, uncompressedBytes); |
||||
case ExrCompressionType.None: |
case ExrCompressionType.Zips: |
||||
return new NoneExrCompression(memoryAllocator, uncompressedBytes); |
return new ZipExrCompression(memoryAllocator, uncompressedBytes); |
||||
case ExrCompressionType.Zips: |
case ExrCompressionType.Zip: |
||||
return new ZipExrCompression(memoryAllocator, uncompressedBytes); |
return new ZipExrCompression(memoryAllocator, uncompressedBytes); |
||||
case ExrCompressionType.Zip: |
case ExrCompressionType.RunLengthEncoded: |
||||
return new ZipExrCompression(memoryAllocator, uncompressedBytes); |
return new RunLengthCompression(memoryAllocator, uncompressedBytes); |
||||
case ExrCompressionType.RunLengthEncoded: |
default: |
||||
return new RunLengthCompression(memoryAllocator, uncompressedBytes); |
throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)); |
||||
default: |
|
||||
throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,26 +1,26 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License..
|
||||
|
|
||||
using System.Diagnostics; |
using System.Diagnostics; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
[DebuggerDisplay("Name: {Name}, Type: {Type}, Length: {Length}")] |
||||
|
internal class ExrAttribute |
||||
{ |
{ |
||||
[DebuggerDisplay("Name: {Name}, Type: {Type}, Length: {Length}")] |
public static readonly ExrAttribute EmptyAttribute = new(string.Empty, string.Empty, 0); |
||||
internal class ExrAttribute |
|
||||
{ |
|
||||
public static readonly ExrAttribute EmptyAttribute = new(string.Empty, string.Empty, 0); |
|
||||
|
|
||||
public ExrAttribute(string name, string type, int length) |
public ExrAttribute(string name, string type, int length) |
||||
{ |
{ |
||||
this.Name = name; |
this.Name = name; |
||||
this.Type = type; |
this.Type = type; |
||||
this.Length = length; |
this.Length = length; |
||||
} |
} |
||||
|
|
||||
public string Name { get; } |
public string Name { get; } |
||||
|
|
||||
public string Type { get; } |
public string Type { get; } |
||||
|
|
||||
public int Length { get; } |
public int Length { get; } |
||||
} |
|
||||
} |
} |
||||
|
|
||||
|
|||||
@ -1,27 +1,26 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Diagnostics; |
using System.Diagnostics; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
[DebuggerDisplay("xMin: {XMin}, yMin: {YMin}, xMax: {XMax}, yMax: {YMax}")] |
||||
|
internal struct ExrBox2i |
||||
{ |
{ |
||||
[DebuggerDisplay("xMin: {XMin}, yMin: {YMin}, xMax: {XMax}, yMax: {YMax}")] |
public ExrBox2i(int xMin, int yMin, int xMax, int yMax) |
||||
internal struct ExrBox2i |
|
||||
{ |
{ |
||||
public ExrBox2i(int xMin, int yMin, int xMax, int yMax) |
this.XMin = xMin; |
||||
{ |
this.YMin = yMin; |
||||
this.XMin = xMin; |
this.XMax = xMax; |
||||
this.YMin = yMin; |
this.YMax = yMax; |
||||
this.XMax = xMax; |
} |
||||
this.YMax = yMax; |
|
||||
} |
|
||||
|
|
||||
public int XMin { get; } |
public int XMin { get; } |
||||
|
|
||||
public int YMin { get; } |
public int YMin { get; } |
||||
|
|
||||
public int XMax { get; } |
public int XMax { get; } |
||||
|
|
||||
public int YMax { get; } |
public int YMax { get; } |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,32 +1,31 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Diagnostics; |
using System.Diagnostics; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
[DebuggerDisplay("Name: {ChannelName}, PixelType: {PixelType}")] |
||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)] |
||||
|
internal readonly struct ExrChannelInfo |
||||
{ |
{ |
||||
[DebuggerDisplay("Name: {ChannelName}, PixelType: {PixelType}")] |
public ExrChannelInfo(string channelName, ExrPixelType pixelType, byte pLinear, int xSampling, int ySampling) |
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)] |
|
||||
internal readonly struct ExrChannelInfo |
|
||||
{ |
{ |
||||
public ExrChannelInfo(string channelName, ExrPixelType pixelType, byte pLinear, int xSampling, int ySampling) |
this.ChannelName = channelName; |
||||
{ |
this.PixelType = pixelType; |
||||
this.ChannelName = channelName; |
this.PLinear = pLinear; |
||||
this.PixelType = pixelType; |
this.XSampling = xSampling; |
||||
this.PLinear = pLinear; |
this.YSampling = ySampling; |
||||
this.XSampling = xSampling; |
} |
||||
this.YSampling = ySampling; |
|
||||
} |
|
||||
|
|
||||
public string ChannelName { get; } |
public string ChannelName { get; } |
||||
|
|
||||
public ExrPixelType PixelType { get; } |
public ExrPixelType PixelType { get; } |
||||
|
|
||||
public byte PLinear { get; } |
public byte PLinear { get; } |
||||
|
|
||||
public int XSampling { get; } |
public int XSampling { get; } |
||||
|
|
||||
public int YSampling { get; } |
public int YSampling { get; } |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,19 +1,18 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers the image encoders, decoders and mime type detectors for the OpenExr format.
|
||||
|
/// </summary>
|
||||
|
public sealed class ExrConfigurationModule : IConfigurationModule |
||||
{ |
{ |
||||
/// <summary>
|
/// <inheritdoc/>
|
||||
/// Registers the image encoders, decoders and mime type detectors for the OpenExr format.
|
public void Configure(Configuration configuration) |
||||
/// </summary>
|
|
||||
public sealed class ExrConfigurationModule : IConfigurationModule |
|
||||
{ |
{ |
||||
/// <inheritdoc/>
|
configuration.ImageFormatsManager.SetEncoder(ExrFormat.Instance, new ExrEncoder()); |
||||
public void Configure(Configuration configuration) |
configuration.ImageFormatsManager.SetDecoder(ExrFormat.Instance, new ExrDecoder()); |
||||
{ |
configuration.ImageFormatsManager.AddImageFormatDetector(new ExrImageFormatDetector()); |
||||
configuration.ImageFormatsManager.SetEncoder(ExrFormat.Instance, new ExrEncoder()); |
|
||||
configuration.ImageFormatsManager.SetDecoder(ExrFormat.Instance, new ExrDecoder()); |
|
||||
configuration.ImageFormatsManager.AddImageFormatDetector(new ExrImageFormatDetector()); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,85 +1,82 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
/// <summary>
|
||||
|
/// Defines constants relating to OpenExr images.
|
||||
|
/// </summary>
|
||||
|
internal static class ExrConstants |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Defines constants relating to OpenExr images.
|
/// The list of mimetypes that equate to a OpenExr image.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal static class ExrConstants |
public static readonly IEnumerable<string> MimeTypes = new[] { "image/x-exr" }; |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The list of mimetypes that equate to a OpenExr image.
|
|
||||
/// </summary>
|
|
||||
public static readonly IEnumerable<string> MimeTypes = new[] { "image/x-exr" }; |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// The list of file extensions that equate to a OpenExr image.
|
/// The list of file extensions that equate to a OpenExr image.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static readonly IEnumerable<string> FileExtensions = new[] { "exr" }; |
public static readonly IEnumerable<string> FileExtensions = new[] { "exr" }; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// The magick bytes identifying an OpenExr image.
|
/// The magick bytes identifying an OpenExr image.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static readonly int MagickBytes = 20000630; |
public static readonly int MagickBytes = 20000630; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// EXR attribute names.
|
/// EXR attribute names.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal static class AttributeNames |
internal static class AttributeNames |
||||
{ |
{ |
||||
public const string Channels = "channels"; |
public const string Channels = "channels"; |
||||
|
|
||||
public const string Compression = "compression"; |
public const string Compression = "compression"; |
||||
|
|
||||
public const string DataWindow = "dataWindow"; |
public const string DataWindow = "dataWindow"; |
||||
|
|
||||
public const string DisplayWindow = "displayWindow"; |
public const string DisplayWindow = "displayWindow"; |
||||
|
|
||||
public const string LineOrder = "lineOrder"; |
public const string LineOrder = "lineOrder"; |
||||
|
|
||||
public const string PixelAspectRatio = "pixelAspectRatio"; |
public const string PixelAspectRatio = "pixelAspectRatio"; |
||||
|
|
||||
public const string ScreenWindowCenter = "screenWindowCenter"; |
public const string ScreenWindowCenter = "screenWindowCenter"; |
||||
|
|
||||
public const string ScreenWindowWidth = "screenWindowWidth"; |
public const string ScreenWindowWidth = "screenWindowWidth"; |
||||
|
|
||||
public const string Tiles = "tiles"; |
public const string Tiles = "tiles"; |
||||
|
|
||||
public const string ChunkCount = "chunkCount"; |
public const string ChunkCount = "chunkCount"; |
||||
} |
} |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// EXR attribute types.
|
/// EXR attribute types.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal static class AttibuteTypes |
internal static class AttibuteTypes |
||||
{ |
{ |
||||
public const string ChannelList = "chlist"; |
public const string ChannelList = "chlist"; |
||||
|
|
||||
public const string Compression = "compression"; |
public const string Compression = "compression"; |
||||
|
|
||||
public const string Float = "float"; |
public const string Float = "float"; |
||||
|
|
||||
public const string LineOrder = "lineOrder"; |
public const string LineOrder = "lineOrder"; |
||||
|
|
||||
public const string TwoFloat = "v2f"; |
public const string TwoFloat = "v2f"; |
||||
|
|
||||
public const string BoxInt = "box2i"; |
public const string BoxInt = "box2i"; |
||||
} |
} |
||||
|
|
||||
internal static class ChannelNames |
internal static class ChannelNames |
||||
{ |
{ |
||||
public const string Red = "R"; |
public const string Red = "R"; |
||||
|
|
||||
public const string Green = "G"; |
public const string Green = "G"; |
||||
|
|
||||
public const string Blue = "B"; |
public const string Blue = "B"; |
||||
|
|
||||
public const string Alpha = "A"; |
public const string Alpha = "A"; |
||||
|
|
||||
public const string Luminance = "Y"; |
public const string Luminance = "Y"; |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,61 +1,46 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Image decoder for generating an image out of a OpenExr stream.
|
||||
|
/// </summary>
|
||||
|
public class ExrDecoder : IImageDecoderSpecialized<ExrDecoderOptions> |
||||
{ |
{ |
||||
/// <summary>
|
/// <inheritdoc/>
|
||||
/// Image decoder for generating an image out of a OpenExr stream.
|
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) |
||||
/// </summary>
|
|
||||
public sealed class ExrDecoder : IImageDecoder, IExrDecoderOptions, IImageInfoDetector |
|
||||
{ |
{ |
||||
/// <inheritdoc/>
|
Guard.NotNull(options, nameof(options)); |
||||
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream) |
Guard.NotNull(stream, nameof(stream)); |
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
return new ExrDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken); |
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
|
|
||||
var decoder = new ExrDecoderCore(configuration, this); |
|
||||
return decoder.Decode<TPixel>(configuration, stream); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public Image Decode(Configuration configuration, Stream stream) |
|
||||
=> this.Decode<Rgba32>(configuration, stream); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken) |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
|
|
||||
var decoder = new ExrDecoderCore(configuration, this); |
|
||||
return decoder.DecodeAsync<TPixel>(configuration, stream, cancellationToken); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc />
|
|
||||
public async Task<Image> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) |
|
||||
=> await this.DecodeAsync<Rgba32>(configuration, stream, cancellationToken) |
|
||||
.ConfigureAwait(false); |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public IImageInfo Identify(Configuration configuration, Stream stream) |
|
||||
{ |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
|
|
||||
return new ExrDecoderCore(configuration, this).Identify(configuration, stream); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public Task<IImageInfo> IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) |
|
||||
{ |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
|
|
||||
return new ExrDecoderCore(configuration, this).IdentifyAsync(configuration, stream, cancellationToken); |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken) |
||||
|
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) |
||||
|
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
Image<TPixel> IImageDecoderSpecialized<ExrDecoderOptions>.Decode<TPixel>(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
Guard.NotNull(options, nameof(options)); |
||||
|
Guard.NotNull(stream, nameof(stream)); |
||||
|
|
||||
|
Image<TPixel> image = new ExrDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken); |
||||
|
|
||||
|
ImageDecoderUtilities.Resize(options.GeneralOptions, image); |
||||
|
|
||||
|
return image; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
Image IImageDecoderSpecialized<ExrDecoderOptions>.Decode(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken) |
||||
|
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode<Rgba32>(options, stream, cancellationToken); |
||||
} |
} |
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,13 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Image decoder options for decoding OpenExr streams.
|
||||
|
/// </summary>
|
||||
|
public sealed class ExrDecoderOptions : ISpecializedDecoderOptions |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public DecoderOptions GeneralOptions { get; set; } = new(); |
||||
|
} |
||||
@ -1,38 +1,34 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.IO; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using SixLabors.ImageSharp.Advanced; |
using SixLabors.ImageSharp.Advanced; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Image encoder for writing an image to a stream in the OpenExr Format.
|
||||
|
/// </summary>
|
||||
|
public sealed class ExrEncoder : IImageEncoder, IExrEncoderOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Image encoder for writing an image to a stream in the OpenExr Format.
|
/// Gets or sets the pixel type of the image.
|
||||
/// </summary>
|
/// </summary>
|
||||
public sealed class ExrEncoder : IImageEncoder, IExrEncoderOptions |
public ExrPixelType? PixelType { get; set; } |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the pixel type of the image.
|
|
||||
/// </summary>
|
|
||||
public ExrPixelType? PixelType { get; set; } |
|
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public void Encode<TPixel>(Image<TPixel> image, Stream stream) |
public void Encode<TPixel>(Image<TPixel> image, Stream stream) |
||||
where TPixel : unmanaged, IPixel<TPixel> |
where TPixel : unmanaged, IPixel<TPixel> |
||||
{ |
{ |
||||
var encoder = new ExrEncoderCore(this, image.GetMemoryAllocator()); |
ExrEncoderCore encoder = new(this, image.GetMemoryAllocator()); |
||||
encoder.Encode(image, stream); |
encoder.Encode(image, stream); |
||||
} |
} |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
||||
where TPixel : unmanaged, IPixel<TPixel> |
where TPixel : unmanaged, IPixel<TPixel> |
||||
{ |
{ |
||||
var encoder = new ExrEncoderCore(this, image.GetMemoryAllocator()); |
ExrEncoderCore encoder = new(this, image.GetMemoryAllocator()); |
||||
return encoder.EncodeAsync(image, stream, cancellationToken); |
return encoder.EncodeAsync(image, stream, cancellationToken); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,422 +1,417 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
using System.Buffers; |
||||
using System.Buffers.Binary; |
using System.Buffers.Binary; |
||||
using System.Collections.Generic; |
|
||||
using System.IO; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Threading; |
|
||||
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
using SixLabors.ImageSharp.Memory; |
using SixLabors.ImageSharp.Memory; |
||||
using SixLabors.ImageSharp.Metadata; |
using SixLabors.ImageSharp.Metadata; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Image encoder for writing an image to a stream in the OpenExr format.
|
||||
|
/// </summary>
|
||||
|
internal sealed class ExrEncoderCore : IImageEncoderInternals |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Image encoder for writing an image to a stream in the OpenExr format.
|
/// Reusable buffer.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal sealed class ExrEncoderCore : IImageEncoderInternals |
private readonly byte[] buffer = new byte[8]; |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Reusable buffer.
|
|
||||
/// </summary>
|
|
||||
private readonly byte[] buffer = new byte[8]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Used for allocating memory during processing operations.
|
|
||||
/// </summary>
|
|
||||
private readonly MemoryAllocator memoryAllocator; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The pixel type of the image.
|
|
||||
/// </summary>
|
|
||||
private ExrPixelType? pixelType; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="ExrEncoderCore"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="options">The encoder options.</param>
|
|
||||
/// <param name="memoryAllocator">The memory manager.</param>
|
|
||||
public ExrEncoderCore(IExrEncoderOptions options, MemoryAllocator memoryAllocator) |
|
||||
{ |
|
||||
this.memoryAllocator = memoryAllocator; |
|
||||
this.pixelType = options.PixelType; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
|
|
||||
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
|
|
||||
/// <param name="cancellationToken">The token to request cancellation.</param>
|
|
||||
public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
Guard.NotNull(image, nameof(image)); |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
|
|
||||
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer; |
/// <summary>
|
||||
|
/// Used for allocating memory during processing operations.
|
||||
ImageMetadata metadata = image.Metadata; |
/// </summary>
|
||||
ExrMetadata exrMetadata = metadata.GetExrMetadata(); |
private readonly MemoryAllocator memoryAllocator; |
||||
this.pixelType ??= exrMetadata.PixelType; |
|
||||
int width = image.Width; |
|
||||
int height = image.Height; |
|
||||
var header = new ExrHeaderAttributes() |
|
||||
{ |
|
||||
Compression = ExrCompressionType.None, |
|
||||
AspectRatio = 1.0f, |
|
||||
DataWindow = new ExrBox2i(0, 0, width - 1, height - 1), |
|
||||
DisplayWindow = new ExrBox2i(0, 0, width - 1, height - 1), |
|
||||
LineOrder = ExrLineOrder.IncreasingY, |
|
||||
ScreenWindowCenter = new PointF(0.0f, 0.0f), |
|
||||
ScreenWindowWidth = 1, |
|
||||
Channels = new List<ExrChannelInfo>() |
|
||||
{ |
|
||||
new(ExrConstants.ChannelNames.Blue, this.pixelType.Value, 0, 1, 1), |
|
||||
new(ExrConstants.ChannelNames.Green, this.pixelType.Value, 0, 1, 1), |
|
||||
new(ExrConstants.ChannelNames.Red, this.pixelType.Value, 0, 1, 1), |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// Write magick bytes.
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, ExrConstants.MagickBytes); |
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
|
|
||||
// Version number.
|
|
||||
this.buffer[0] = 2; |
|
||||
|
|
||||
// Second, third and fourth bytes store info about the image, set all to default: zero.
|
/// <summary>
|
||||
this.buffer[1] = 0; |
/// The pixel type of the image.
|
||||
this.buffer[2] = 0; |
/// </summary>
|
||||
this.buffer[3] = 0; |
private ExrPixelType? pixelType; |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
|
|
||||
// Write EXR header.
|
/// <summary>
|
||||
this.WriteHeader(stream, header); |
/// Initializes a new instance of the <see cref="ExrEncoderCore"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="options">The encoder options.</param>
|
||||
|
/// <param name="memoryAllocator">The memory manager.</param>
|
||||
|
public ExrEncoderCore(IExrEncoderOptions options, MemoryAllocator memoryAllocator) |
||||
|
{ |
||||
|
this.memoryAllocator = memoryAllocator; |
||||
|
this.pixelType = options.PixelType; |
||||
|
} |
||||
|
|
||||
// Write offsets to each pixel row.
|
/// <summary>
|
||||
int bytesPerChannel = this.pixelType == ExrPixelType.Half ? 2 : 4; |
/// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>.
|
||||
int numberOfChannels = 3; |
/// </summary>
|
||||
uint rowSizeBytes = (uint)(width * numberOfChannels * bytesPerChannel); |
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
this.WriteRowOffsets(stream, height, rowSizeBytes); |
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
|
||||
|
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
|
||||
|
/// <param name="cancellationToken">The token to request cancellation.</param>
|
||||
|
public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
Guard.NotNull(image, nameof(image)); |
||||
|
Guard.NotNull(stream, nameof(stream)); |
||||
|
|
||||
// Write pixel data.
|
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer; |
||||
switch (this.pixelType) |
|
||||
{ |
|
||||
case ExrPixelType.Half: |
|
||||
case ExrPixelType.Float: |
|
||||
this.EncodeFloatingPointPixelData(stream, pixels, width, height, rowSizeBytes); |
|
||||
break; |
|
||||
case ExrPixelType.UnsignedInt: |
|
||||
this.EncodeUnsignedIntPixelData(stream, pixels, width, height, rowSizeBytes); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void EncodeFloatingPointPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes) |
ImageMetadata metadata = image.Metadata; |
||||
where TPixel : unmanaged, IPixel<TPixel> |
ExrMetadata exrMetadata = metadata.GetExrMetadata(); |
||||
|
this.pixelType ??= exrMetadata.PixelType; |
||||
|
int width = image.Width; |
||||
|
int height = image.Height; |
||||
|
var header = new ExrHeaderAttributes() |
||||
{ |
{ |
||||
using IMemoryOwner<float> rgbBuffer = this.memoryAllocator.Allocate<float>(width * 3); |
Compression = ExrCompressionType.None, |
||||
Span<float> redBuffer = rgbBuffer.GetSpan().Slice(0, width); |
AspectRatio = 1.0f, |
||||
Span<float> greenBuffer = rgbBuffer.GetSpan().Slice(width, width); |
DataWindow = new ExrBox2i(0, 0, width - 1, height - 1), |
||||
Span<float> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width); |
DisplayWindow = new ExrBox2i(0, 0, width - 1, height - 1), |
||||
|
LineOrder = ExrLineOrder.IncreasingY, |
||||
for (int y = 0; y < height; y++) |
ScreenWindowCenter = new PointF(0.0f, 0.0f), |
||||
|
ScreenWindowWidth = 1, |
||||
|
Channels = new List<ExrChannelInfo>() |
||||
{ |
{ |
||||
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); |
new(ExrConstants.ChannelNames.Blue, this.pixelType.Value, 0, 1, 1), |
||||
|
new(ExrConstants.ChannelNames.Green, this.pixelType.Value, 0, 1, 1), |
||||
for (int x = 0; x < width; x++) |
new(ExrConstants.ChannelNames.Red, this.pixelType.Value, 0, 1, 1), |
||||
{ |
|
||||
var vector4 = pixelRowSpan[x].ToVector4(); |
|
||||
redBuffer[x] = vector4.X; |
|
||||
greenBuffer[x] = vector4.Y; |
|
||||
blueBuffer[x] = vector4.Z; |
|
||||
} |
|
||||
|
|
||||
// Write row index.
|
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y); |
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
|
|
||||
// Write pixel row data size.
|
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes); |
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
|
|
||||
switch (this.pixelType) |
|
||||
{ |
|
||||
case ExrPixelType.Float: |
|
||||
this.WriteSingleRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
|
||||
break; |
|
||||
case ExrPixelType.Half: |
|
||||
this.WriteHalfSingleRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
|
||||
break; |
|
||||
} |
|
||||
} |
} |
||||
} |
}; |
||||
|
|
||||
private void EncodeUnsignedIntPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes) |
|
||||
where TPixel : unmanaged, IPixel<TPixel> |
|
||||
{ |
|
||||
using IMemoryOwner<uint> rgbBuffer = this.memoryAllocator.Allocate<uint>(width * 3); |
|
||||
Span<uint> redBuffer = rgbBuffer.GetSpan().Slice(0, width); |
|
||||
Span<uint> greenBuffer = rgbBuffer.GetSpan().Slice(width, width); |
|
||||
Span<uint> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width); |
|
||||
|
|
||||
var rgb = default(Rgb96); |
|
||||
for (int y = 0; y < height; y++) |
|
||||
{ |
|
||||
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); |
|
||||
|
|
||||
for (int x = 0; x < width; x++) |
// Write magick bytes.
|
||||
{ |
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, ExrConstants.MagickBytes); |
||||
var vector4 = pixelRowSpan[x].ToVector4(); |
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
rgb.FromVector4(vector4); |
|
||||
|
|
||||
redBuffer[x] = rgb.R; |
// Version number.
|
||||
greenBuffer[x] = rgb.G; |
this.buffer[0] = 2; |
||||
blueBuffer[x] = rgb.B; |
|
||||
} |
|
||||
|
|
||||
// Write row index.
|
// Second, third and fourth bytes store info about the image, set all to default: zero.
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y); |
this.buffer[1] = 0; |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
this.buffer[2] = 0; |
||||
|
this.buffer[3] = 0; |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
// Write pixel row data size.
|
// Write EXR header.
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes); |
this.WriteHeader(stream, header); |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
|
|
||||
this.WriteUnsignedIntRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
// Write offsets to each pixel row.
|
||||
} |
int bytesPerChannel = this.pixelType == ExrPixelType.Half ? 2 : 4; |
||||
} |
int numberOfChannels = 3; |
||||
|
uint rowSizeBytes = (uint)(width * numberOfChannels * bytesPerChannel); |
||||
|
this.WriteRowOffsets(stream, height, rowSizeBytes); |
||||
|
|
||||
private void WriteHeader(Stream stream, ExrHeaderAttributes header) |
// Write pixel data.
|
||||
|
switch (this.pixelType) |
||||
{ |
{ |
||||
this.WriteChannels(stream, header.Channels); |
case ExrPixelType.Half: |
||||
this.WriteCompression(stream, header.Compression.Value); |
case ExrPixelType.Float: |
||||
this.WriteDataWindow(stream, header.DataWindow.Value); |
this.EncodeFloatingPointPixelData(stream, pixels, width, height, rowSizeBytes); |
||||
this.WriteDisplayWindow(stream, header.DisplayWindow.Value); |
break; |
||||
this.WritePixelAspectRatio(stream, header.AspectRatio.Value); |
case ExrPixelType.UnsignedInt: |
||||
this.WriteLineOrder(stream, header.LineOrder.Value); |
this.EncodeUnsignedIntPixelData(stream, pixels, width, height, rowSizeBytes); |
||||
this.WriteScreenWindowCenter(stream, header.ScreenWindowCenter.Value); |
break; |
||||
this.WriteScreenWindowWidth(stream, header.ScreenWindowWidth.Value); |
|
||||
stream.WriteByte(0); |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
private void EncodeFloatingPointPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
using IMemoryOwner<float> rgbBuffer = this.memoryAllocator.Allocate<float>(width * 3); |
||||
|
Span<float> redBuffer = rgbBuffer.GetSpan().Slice(0, width); |
||||
|
Span<float> greenBuffer = rgbBuffer.GetSpan().Slice(width, width); |
||||
|
Span<float> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width); |
||||
|
|
||||
private void WriteSingleRow(Stream stream, int width, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) |
for (int y = 0; y < height; y++) |
||||
{ |
{ |
||||
for (int x = 0; x < width; x++) |
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); |
||||
{ |
|
||||
this.WriteSingle(stream, blueBuffer[x]); |
|
||||
} |
|
||||
|
|
||||
for (int x = 0; x < width; x++) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteSingle(stream, greenBuffer[x]); |
var vector4 = pixelRowSpan[x].ToVector4(); |
||||
|
redBuffer[x] = vector4.X; |
||||
|
greenBuffer[x] = vector4.Y; |
||||
|
blueBuffer[x] = vector4.Z; |
||||
} |
} |
||||
|
|
||||
for (int x = 0; x < width; x++) |
// Write row index.
|
||||
|
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
|
// Write pixel row data size.
|
||||
|
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
|
switch (this.pixelType) |
||||
{ |
{ |
||||
this.WriteSingle(stream, redBuffer[x]); |
case ExrPixelType.Float: |
||||
|
this.WriteSingleRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
||||
|
break; |
||||
|
case ExrPixelType.Half: |
||||
|
this.WriteHalfSingleRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
||||
|
break; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
private void EncodeUnsignedIntPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
using IMemoryOwner<uint> rgbBuffer = this.memoryAllocator.Allocate<uint>(width * 3); |
||||
|
Span<uint> redBuffer = rgbBuffer.GetSpan().Slice(0, width); |
||||
|
Span<uint> greenBuffer = rgbBuffer.GetSpan().Slice(width, width); |
||||
|
Span<uint> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width); |
||||
|
|
||||
private void WriteHalfSingleRow(Stream stream, int width, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) |
var rgb = default(Rgb96); |
||||
|
for (int y = 0; y < height; y++) |
||||
{ |
{ |
||||
for (int x = 0; x < width; x++) |
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); |
||||
{ |
|
||||
this.WriteHalfSingle(stream, blueBuffer[x]); |
|
||||
} |
|
||||
|
|
||||
for (int x = 0; x < width; x++) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteHalfSingle(stream, greenBuffer[x]); |
var vector4 = pixelRowSpan[x].ToVector4(); |
||||
} |
rgb.FromVector4(vector4); |
||||
|
|
||||
for (int x = 0; x < width; x++) |
redBuffer[x] = rgb.R; |
||||
{ |
greenBuffer[x] = rgb.G; |
||||
this.WriteHalfSingle(stream, redBuffer[x]); |
blueBuffer[x] = rgb.B; |
||||
} |
} |
||||
} |
|
||||
|
|
||||
private void WriteUnsignedIntRow(Stream stream, int width, Span<uint> blueBuffer, Span<uint> greenBuffer, Span<uint> redBuffer) |
// Write row index.
|
||||
{ |
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y); |
||||
for (int x = 0; x < width; x++) |
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
{ |
|
||||
this.WriteUnsignedInt(stream, blueBuffer[x]); |
|
||||
} |
|
||||
|
|
||||
for (int x = 0; x < width; x++) |
// Write pixel row data size.
|
||||
{ |
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes); |
||||
this.WriteUnsignedInt(stream, greenBuffer[x]); |
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
} |
|
||||
|
|
||||
for (int x = 0; x < width; x++) |
this.WriteUnsignedIntRow(stream, width, blueBuffer, greenBuffer, redBuffer); |
||||
{ |
|
||||
this.WriteUnsignedInt(stream, redBuffer[x]); |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
private void WriteRowOffsets(Stream stream, int height, uint rowSizeBytes) |
private void WriteHeader(Stream stream, ExrHeaderAttributes header) |
||||
|
{ |
||||
|
this.WriteChannels(stream, header.Channels); |
||||
|
this.WriteCompression(stream, header.Compression.Value); |
||||
|
this.WriteDataWindow(stream, header.DataWindow.Value); |
||||
|
this.WriteDisplayWindow(stream, header.DisplayWindow.Value); |
||||
|
this.WritePixelAspectRatio(stream, header.AspectRatio.Value); |
||||
|
this.WriteLineOrder(stream, header.LineOrder.Value); |
||||
|
this.WriteScreenWindowCenter(stream, header.ScreenWindowCenter.Value); |
||||
|
this.WriteScreenWindowWidth(stream, header.ScreenWindowWidth.Value); |
||||
|
stream.WriteByte(0); |
||||
|
} |
||||
|
|
||||
|
private void WriteSingleRow(Stream stream, int width, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) |
||||
|
{ |
||||
|
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
ulong startOfPixelData = (ulong)stream.Position + (8 * (ulong)height); |
this.WriteSingle(stream, blueBuffer[x]); |
||||
ulong offset = startOfPixelData; |
|
||||
for (int i = 0; i < height; i++) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, offset); |
|
||||
stream.Write(this.buffer); |
|
||||
offset += 4 + 4 + rowSizeBytes; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
private void WriteChannels(Stream stream, IList<ExrChannelInfo> channels) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
int attributeSize = 0; |
this.WriteSingle(stream, greenBuffer[x]); |
||||
foreach (ExrChannelInfo channelInfo in channels) |
} |
||||
{ |
|
||||
attributeSize += channelInfo.ChannelName.Length + 1; |
|
||||
attributeSize += 16; |
|
||||
} |
|
||||
|
|
||||
// Last zero byte.
|
|
||||
attributeSize++; |
|
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Channels, ExrConstants.AttibuteTypes.ChannelList, attributeSize); |
|
||||
|
|
||||
foreach (ExrChannelInfo channelInfo in channels) |
|
||||
{ |
|
||||
this.WriteChannelInfo(stream, channelInfo); |
|
||||
} |
|
||||
|
|
||||
// Last byte should be zero.
|
for (int x = 0; x < width; x++) |
||||
stream.WriteByte(0); |
{ |
||||
|
this.WriteSingle(stream, redBuffer[x]); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
private void WriteCompression(Stream stream, ExrCompressionType compression) |
private void WriteHalfSingleRow(Stream stream, int width, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer) |
||||
|
{ |
||||
|
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1); |
this.WriteHalfSingle(stream, blueBuffer[x]); |
||||
stream.WriteByte((byte)compression); |
|
||||
} |
} |
||||
|
|
||||
private void WritePixelAspectRatio(Stream stream, float aspectRatio) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.PixelAspectRatio, ExrConstants.AttibuteTypes.Float, 4); |
this.WriteHalfSingle(stream, greenBuffer[x]); |
||||
this.WriteSingle(stream, aspectRatio); |
|
||||
} |
} |
||||
|
|
||||
private void WriteLineOrder(Stream stream, ExrLineOrder lineOrder) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.LineOrder, ExrConstants.AttibuteTypes.LineOrder, 1); |
this.WriteHalfSingle(stream, redBuffer[x]); |
||||
stream.WriteByte((byte)lineOrder); |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
private void WriteScreenWindowCenter(Stream stream, PointF screenWindowCenter) |
private void WriteUnsignedIntRow(Stream stream, int width, Span<uint> blueBuffer, Span<uint> greenBuffer, Span<uint> redBuffer) |
||||
|
{ |
||||
|
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowCenter, ExrConstants.AttibuteTypes.TwoFloat, 8); |
this.WriteUnsignedInt(stream, blueBuffer[x]); |
||||
this.WriteSingle(stream, screenWindowCenter.X); |
|
||||
this.WriteSingle(stream, screenWindowCenter.Y); |
|
||||
} |
} |
||||
|
|
||||
private void WriteScreenWindowWidth(Stream stream, float screenWindowWidth) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowWidth, ExrConstants.AttibuteTypes.Float, 4); |
this.WriteUnsignedInt(stream, greenBuffer[x]); |
||||
this.WriteSingle(stream, screenWindowWidth); |
|
||||
} |
} |
||||
|
|
||||
private void WriteDataWindow(Stream stream, ExrBox2i dataWindow) |
for (int x = 0; x < width; x++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DataWindow, ExrConstants.AttibuteTypes.BoxInt, 16); |
this.WriteUnsignedInt(stream, redBuffer[x]); |
||||
this.WriteBoxInteger(stream, dataWindow); |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
private void WriteDisplayWindow(Stream stream, ExrBox2i displayWindow) |
private void WriteRowOffsets(Stream stream, int height, uint rowSizeBytes) |
||||
|
{ |
||||
|
ulong startOfPixelData = (ulong)stream.Position + (8 * (ulong)height); |
||||
|
ulong offset = startOfPixelData; |
||||
|
for (int i = 0; i < height; i++) |
||||
{ |
{ |
||||
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DisplayWindow, ExrConstants.AttibuteTypes.BoxInt, 16); |
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, offset); |
||||
this.WriteBoxInteger(stream, displayWindow); |
stream.Write(this.buffer); |
||||
|
offset += 4 + 4 + rowSizeBytes; |
||||
} |
} |
||||
|
} |
||||
|
|
||||
private void WriteAttributeInformation(Stream stream, string name, string type, int size) |
private void WriteChannels(Stream stream, IList<ExrChannelInfo> channels) |
||||
|
{ |
||||
|
int attributeSize = 0; |
||||
|
foreach (ExrChannelInfo channelInfo in channels) |
||||
{ |
{ |
||||
// Write attribute name.
|
attributeSize += channelInfo.ChannelName.Length + 1; |
||||
this.WriteString(stream, name); |
attributeSize += 16; |
||||
|
} |
||||
|
|
||||
// Write attribute type.
|
// Last zero byte.
|
||||
this.WriteString(stream, type); |
attributeSize++; |
||||
|
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Channels, ExrConstants.AttibuteTypes.ChannelList, attributeSize); |
||||
|
|
||||
// Write attribute size.
|
foreach (ExrChannelInfo channelInfo in channels) |
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)size); |
{ |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
this.WriteChannelInfo(stream, channelInfo); |
||||
} |
} |
||||
|
|
||||
private void WriteChannelInfo(Stream stream, ExrChannelInfo channelInfo) |
// Last byte should be zero.
|
||||
{ |
stream.WriteByte(0); |
||||
this.WriteString(stream, channelInfo.ChannelName); |
} |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, (int)channelInfo.PixelType); |
private void WriteCompression(Stream stream, ExrCompressionType compression) |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
{ |
||||
|
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1); |
||||
|
stream.WriteByte((byte)compression); |
||||
|
} |
||||
|
|
||||
stream.WriteByte(channelInfo.PLinear); |
private void WritePixelAspectRatio(Stream stream, float aspectRatio) |
||||
|
{ |
||||
|
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.PixelAspectRatio, ExrConstants.AttibuteTypes.Float, 4); |
||||
|
this.WriteSingle(stream, aspectRatio); |
||||
|
} |
||||
|
|
||||
// Next 3 bytes are reserved and will set to zero.
|
private void WriteLineOrder(Stream stream, ExrLineOrder lineOrder) |
||||
stream.WriteByte(0); |
{ |
||||
stream.WriteByte(0); |
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.LineOrder, ExrConstants.AttibuteTypes.LineOrder, 1); |
||||
stream.WriteByte(0); |
stream.WriteByte((byte)lineOrder); |
||||
|
} |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.XSampling); |
private void WriteScreenWindowCenter(Stream stream, PointF screenWindowCenter) |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
{ |
||||
|
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowCenter, ExrConstants.AttibuteTypes.TwoFloat, 8); |
||||
|
this.WriteSingle(stream, screenWindowCenter.X); |
||||
|
this.WriteSingle(stream, screenWindowCenter.Y); |
||||
|
} |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.YSampling); |
private void WriteScreenWindowWidth(Stream stream, float screenWindowWidth) |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
{ |
||||
} |
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowWidth, ExrConstants.AttibuteTypes.Float, 4); |
||||
|
this.WriteSingle(stream, screenWindowWidth); |
||||
|
} |
||||
|
|
||||
private void WriteString(Stream stream, string str) |
private void WriteDataWindow(Stream stream, ExrBox2i dataWindow) |
||||
{ |
{ |
||||
foreach (char c in str) |
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DataWindow, ExrConstants.AttibuteTypes.BoxInt, 16); |
||||
{ |
this.WriteBoxInteger(stream, dataWindow); |
||||
stream.WriteByte((byte)c); |
} |
||||
} |
|
||||
|
|
||||
// Write termination byte.
|
private void WriteDisplayWindow(Stream stream, ExrBox2i displayWindow) |
||||
stream.WriteByte(0); |
{ |
||||
} |
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DisplayWindow, ExrConstants.AttibuteTypes.BoxInt, 16); |
||||
|
this.WriteBoxInteger(stream, displayWindow); |
||||
|
} |
||||
|
|
||||
private void WriteBoxInteger(Stream stream, ExrBox2i box) |
private void WriteAttributeInformation(Stream stream, string name, string type, int size) |
||||
{ |
{ |
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMin); |
// Write attribute name.
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
this.WriteString(stream, name); |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMin); |
// Write attribute type.
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
this.WriteString(stream, type); |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMax); |
// Write attribute size.
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)size); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
} |
||||
|
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMax); |
private void WriteChannelInfo(Stream stream, ExrChannelInfo channelInfo) |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
{ |
||||
} |
this.WriteString(stream, channelInfo.ChannelName); |
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, (int)channelInfo.PixelType); |
||||
private unsafe void WriteSingle(Stream stream, float value) |
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
{ |
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, *(int*)&value); |
|
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
stream.WriteByte(channelInfo.PLinear); |
||||
private void WriteHalfSingle(Stream stream, float value) |
|
||||
{ |
// Next 3 bytes are reserved and will set to zero.
|
||||
ushort valueAsShort = HalfTypeHelper.Pack(value); |
stream.WriteByte(0); |
||||
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, valueAsShort); |
stream.WriteByte(0); |
||||
stream.Write(this.buffer.AsSpan(0, 2)); |
stream.WriteByte(0); |
||||
} |
|
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.XSampling); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.YSampling); |
||||
private void WriteUnsignedInt(Stream stream, uint value) |
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
} |
||||
|
|
||||
|
private void WriteString(Stream stream, string str) |
||||
|
{ |
||||
|
foreach (char c in str) |
||||
{ |
{ |
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); |
stream.WriteByte((byte)c); |
||||
stream.Write(this.buffer.AsSpan(0, 4)); |
|
||||
} |
} |
||||
|
|
||||
|
// Write termination byte.
|
||||
|
stream.WriteByte(0); |
||||
|
} |
||||
|
|
||||
|
private void WriteBoxInteger(Stream stream, ExrBox2i box) |
||||
|
{ |
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMin); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMin); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMax); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
|
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.YMax); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
private unsafe void WriteSingle(Stream stream, float value) |
||||
|
{ |
||||
|
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, *(int*)&value); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
private void WriteHalfSingle(Stream stream, float value) |
||||
|
{ |
||||
|
ushort valueAsShort = HalfTypeHelper.Pack(value); |
||||
|
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, valueAsShort); |
||||
|
stream.Write(this.buffer.AsSpan(0, 2)); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
private void WriteUnsignedInt(Stream stream, uint value) |
||||
|
{ |
||||
|
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); |
||||
|
stream.Write(this.buffer.AsSpan(0, 4)); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,38 +1,34 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
using SixLabors.ImageSharp.Formats.Bmp; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
/// <summary>
|
||||
|
/// Registers the image encoders, decoders and mime type detectors for the OpenExr format.
|
||||
|
/// </summary>
|
||||
|
public sealed class ExrFormat : IImageFormat<ExrMetadata> |
||||
{ |
{ |
||||
/// <summary>
|
private ExrFormat() |
||||
/// Registers the image encoders, decoders and mime type detectors for the OpenExr format.
|
|
||||
/// </summary>
|
|
||||
public sealed class ExrFormat : IImageFormat<ExrMetadata> |
|
||||
{ |
{ |
||||
private ExrFormat() |
} |
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets the current instance.
|
/// Gets the current instance.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static ExrFormat Instance { get; } = new(); |
public static ExrFormat Instance { get; } = new(); |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public string Name => "EXR"; |
public string Name => "EXR"; |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public string DefaultMimeType => "image/x-exr"; |
public string DefaultMimeType => "image/x-exr"; |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public IEnumerable<string> MimeTypes => ExrConstants.MimeTypes; |
public IEnumerable<string> MimeTypes => ExrConstants.MimeTypes; |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public IEnumerable<string> FileExtensions => ExrConstants.FileExtensions; |
public IEnumerable<string> FileExtensions => ExrConstants.FileExtensions; |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public ExrMetadata CreateDefaultFormatMetadata() => new(); |
public ExrMetadata CreateDefaultFormatMetadata() => new(); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,78 +1,76 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
internal class ExrHeaderAttributes |
||||
{ |
{ |
||||
internal class ExrHeaderAttributes |
public IList<ExrChannelInfo> Channels { get; set; } |
||||
{ |
|
||||
public IList<ExrChannelInfo> Channels { get; set; } |
|
||||
|
|
||||
public ExrCompressionType? Compression { get; set; } |
public ExrCompressionType? Compression { get; set; } |
||||
|
|
||||
public ExrBox2i? DataWindow { get; set; } |
public ExrBox2i? DataWindow { get; set; } |
||||
|
|
||||
public ExrBox2i? DisplayWindow { get; set; } |
public ExrBox2i? DisplayWindow { get; set; } |
||||
|
|
||||
public ExrLineOrder? LineOrder { get; set; } |
public ExrLineOrder? LineOrder { get; set; } |
||||
|
|
||||
public float? AspectRatio { get; set; } |
public float? AspectRatio { get; set; } |
||||
|
|
||||
public float? ScreenWindowWidth { get; set; } |
public float? ScreenWindowWidth { get; set; } |
||||
|
|
||||
public PointF? ScreenWindowCenter { get; set; } |
public PointF? ScreenWindowCenter { get; set; } |
||||
|
|
||||
public uint? TileXSize { get; set; } |
public uint? TileXSize { get; set; } |
||||
|
|
||||
public uint? TileYSize { get; set; } |
public uint? TileYSize { get; set; } |
||||
|
|
||||
public int? ChunkCount { get; set; } |
public int? ChunkCount { get; set; } |
||||
|
|
||||
public bool IsValid() |
public bool IsValid() |
||||
|
{ |
||||
|
if (!this.Compression.HasValue) |
||||
{ |
{ |
||||
if (!this.Compression.HasValue) |
return false; |
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.DataWindow.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.DisplayWindow.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.LineOrder.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.AspectRatio.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.ScreenWindowWidth.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (!this.ScreenWindowCenter.HasValue) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (this.Channels is null) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return true; |
|
||||
} |
} |
||||
|
|
||||
|
if (!this.DataWindow.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!this.DisplayWindow.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!this.LineOrder.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!this.AspectRatio.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!this.ScreenWindowWidth.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!this.ScreenWindowCenter.HasValue) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (this.Channels is null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,16 +1,15 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
internal enum ExrImageDataType |
||||
{ |
{ |
||||
internal enum ExrImageDataType |
Unknown = 0, |
||||
{ |
|
||||
Unknown = 0, |
|
||||
|
|
||||
Rgb = 1, |
Rgb = 1, |
||||
|
|
||||
Rgba = 2, |
Rgba = 2, |
||||
|
|
||||
Gray = 3, |
Gray = 3, |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,31 +1,29 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers.Binary; |
using System.Buffers.Binary; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Detects OpenExr file headers.
|
||||
|
/// </summary>
|
||||
|
public sealed class ExrImageFormatDetector : IImageFormatDetector |
||||
{ |
{ |
||||
/// <summary>
|
/// <inheritdoc/>
|
||||
/// Detects OpenExr file headers.
|
public int HeaderSize => 4; |
||||
/// </summary>
|
|
||||
public sealed class ExrImageFormatDetector : IImageFormatDetector |
|
||||
{ |
|
||||
/// <inheritdoc/>
|
|
||||
public int HeaderSize => 4; |
|
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? ExrFormat.Instance : null; |
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? ExrFormat.Instance : null; |
||||
|
|
||||
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) |
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) |
||||
|
{ |
||||
|
if (header.Length >= this.HeaderSize) |
||||
{ |
{ |
||||
if (header.Length >= this.HeaderSize) |
int fileTypeMarker = BinaryPrimitives.ReadInt32LittleEndian(header); |
||||
{ |
return fileTypeMarker == ExrConstants.MagickBytes; |
||||
int fileTypeMarker = BinaryPrimitives.ReadInt32LittleEndian(header); |
|
||||
return fileTypeMarker == ExrConstants.MagickBytes; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
} |
||||
|
|
||||
|
return false; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,11 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
internal enum ExrImageType |
||||
{ |
{ |
||||
internal enum ExrImageType |
ScanLine = 0, |
||||
{ |
|
||||
ScanLine = 0, |
|
||||
|
|
||||
Tiled = 1 |
Tiled = 1 |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,14 +1,13 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
internal enum ExrLineOrder : byte |
||||
{ |
{ |
||||
internal enum ExrLineOrder : byte |
IncreasingY = 0, |
||||
{ |
|
||||
IncreasingY = 0, |
|
||||
|
|
||||
DecreasingY = 1, |
DecreasingY = 1, |
||||
|
|
||||
RandomY = 2 |
RandomY = 2 |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,32 +1,31 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides OpenExr specific metadata information for the image.
|
||||
|
/// </summary>
|
||||
|
public class ExrMetadata : IDeepCloneable |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Provides OpenExr specific metadata information for the image.
|
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
|
||||
/// </summary>
|
/// </summary>
|
||||
public class ExrMetadata : IDeepCloneable |
public ExrMetadata() |
||||
{ |
{ |
||||
/// <summary>
|
} |
||||
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
|
|
||||
/// </summary>
|
|
||||
public ExrMetadata() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
|
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
|
||||
/// </summary>
|
/// </summary>
|
||||
/// <param name="other">The metadata to create an instance from.</param>
|
/// <param name="other">The metadata to create an instance from.</param>
|
||||
private ExrMetadata(ExrMetadata other) => this.PixelType = other.PixelType; |
private ExrMetadata(ExrMetadata other) => this.PixelType = other.PixelType; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// Gets or sets the pixel format.
|
/// Gets or sets the pixel format.
|
||||
/// </summary>
|
/// </summary>
|
||||
public ExrPixelType PixelType { get; set; } = ExrPixelType.Float; |
public ExrPixelType PixelType { get; set; } = ExrPixelType.Float; |
||||
|
|
||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||
public IDeepCloneable DeepClone() => new ExrMetadata(this); |
public IDeepCloneable DeepClone() => new ExrMetadata(this); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,26 +1,25 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The different pixel formats for a OpenEXR image.
|
||||
|
/// </summary>
|
||||
|
public enum ExrPixelType |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// The different pixel formats for a OpenEXR image.
|
/// unsigned int (32 bit).
|
||||
/// </summary>
|
/// </summary>
|
||||
public enum ExrPixelType |
UnsignedInt = 0, |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// unsigned int (32 bit).
|
|
||||
/// </summary>
|
|
||||
UnsignedInt = 0, |
|
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// half (16 bit floating point).
|
/// half (16 bit floating point).
|
||||
/// </summary>
|
/// </summary>
|
||||
Half = 1, |
Half = 1, |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// float (32 bit floating point).
|
/// float (32 bit floating point).
|
||||
/// </summary>
|
/// </summary>
|
||||
Float = 2 |
Float = 2 |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,32 +1,30 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Cold path optimizations for throwing exr format based exceptions.
|
||||
|
/// </summary>
|
||||
|
internal static class ExrThrowHelper |
||||
{ |
{ |
||||
/// <summary>
|
[MethodImpl(InliningOptions.ColdPath)] |
||||
/// Cold path optimizations for throwing exr format based exceptions.
|
public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); |
||||
/// </summary>
|
|
||||
internal static class ExrThrowHelper |
|
||||
{ |
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
|
||||
public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}"); |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); |
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage); |
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
[MethodImpl(InliningOptions.ColdPath)] |
||||
public static void ThrowNotSupportedVersion() => throw new NotSupportedException("Unsupported EXR version"); |
public static void ThrowNotSupportedVersion() => throw new NotSupportedException("Unsupported EXR version"); |
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
[MethodImpl(InliningOptions.ColdPath)] |
||||
public static void ThrowNotSupported(string msg) => throw new NotSupportedException(msg); |
public static void ThrowNotSupported(string msg) => throw new NotSupportedException(msg); |
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
[MethodImpl(InliningOptions.ColdPath)] |
||||
public static void ThrowInvalidImageHeader() => throw new InvalidImageContentException("Invalid EXR image header"); |
public static void ThrowInvalidImageHeader() => throw new InvalidImageContentException("Invalid EXR image header"); |
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
[MethodImpl(InliningOptions.ColdPath)] |
||||
public static void ThrowInvalidImageHeader(string msg) => throw new InvalidImageContentException(msg); |
public static void ThrowInvalidImageHeader(string msg) => throw new InvalidImageContentException(msg); |
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,12 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Image decoder options for decoding OpenExr streams.
|
|
||||
/// </summary>
|
|
||||
internal interface IExrDecoderOptions |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
@ -1,16 +1,15 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.OpenExr |
namespace SixLabors.ImageSharp.Formats.OpenExr; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Configuration options for use during OpenExr encoding.
|
||||
|
/// </summary>
|
||||
|
internal interface IExrEncoderOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Configuration options for use during OpenExr encoding.
|
/// Gets the pixel type of the image.
|
||||
/// </summary>
|
/// </summary>
|
||||
internal interface IExrEncoderOptions |
ExrPixelType? PixelType { get; } |
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets the pixel type of the image.
|
|
||||
/// </summary>
|
|
||||
ExrPixelType? PixelType { get; } |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,21 +1,20 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
using SixLabors.ImageSharp.Formats.OpenExr; |
using SixLabors.ImageSharp.Formats.OpenExr; |
||||
using SixLabors.ImageSharp.Metadata; |
using SixLabors.ImageSharp.Metadata; |
||||
|
|
||||
namespace SixLabors.ImageSharp |
namespace SixLabors.ImageSharp; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for the <see cref="ImageMetadata"/> type.
|
||||
|
/// </summary>
|
||||
|
public static partial class MetadataExtensions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// Extension methods for the <see cref="ImageMetadata"/> type.
|
/// Gets the open exr format specific metadata for the image.
|
||||
/// </summary>
|
/// </summary>
|
||||
public static partial class MetadataExtensions |
/// <param name="metadata">The metadata this method extends.</param>
|
||||
{ |
/// <returns>The <see cref="ExrMetadata"/>.</returns>
|
||||
/// <summary>
|
public static ExrMetadata GetExrMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(ExrFormat.Instance); |
||||
/// Gets the open exr format specific metadata for the image.
|
|
||||
/// </summary>
|
|
||||
/// <param name="metadata">The metadata this method extends.</param>
|
|
||||
/// <returns>The <see cref="ExrMetadata"/>.</returns>
|
|
||||
public static ExrMetadata GetExrMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(ExrFormat.Instance); |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,156 +1,67 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System.IO; |
|
||||
using System.Threading.Tasks; |
|
||||
using SixLabors.ImageSharp.Formats; |
using SixLabors.ImageSharp.Formats; |
||||
using SixLabors.ImageSharp.Formats.OpenExr; |
using SixLabors.ImageSharp.Formats.OpenExr; |
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using Xunit; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tests.Formats.Exr |
namespace SixLabors.ImageSharp.Tests.Formats.Exr; |
||||
|
|
||||
|
[Trait("Format", "Exr")] |
||||
|
public class ImageExtensionsTest |
||||
{ |
{ |
||||
[Trait("Format", "Exr")] |
[Fact] |
||||
public class ImageExtensionsTest |
public void SaveAsExr_Stream() |
||||
{ |
{ |
||||
[Fact] |
using var memoryStream = new MemoryStream(); |
||||
public void SaveAsExr_Path() |
|
||||
{ |
|
||||
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); |
|
||||
string file = Path.Combine(dir, "SaveAsExr_Path.exr"); |
|
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
image.SaveAsOpenExr(file); |
|
||||
} |
|
||||
|
|
||||
using (Image.Load(file, out IImageFormat mime)) |
|
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
using (var image = new Image<Rgba32>(10, 10)) |
||||
public async Task SaveAsExrAsync_Path() |
|
||||
{ |
{ |
||||
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest)); |
image.SaveAsOpenExr(memoryStream, new ExrEncoder()); |
||||
string file = Path.Combine(dir, "SaveAsExrAsync_Path.exr"); |
|
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
await image.SaveAsOpenExrAsync(file); |
|
||||
} |
|
||||
|
|
||||
using (Image.Load(file, out IImageFormat mime)) |
|
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
[Fact] |
memoryStream.Position = 0; |
||||
public void SaveAsExr_Path_Encoder() |
|
||||
{ |
|
||||
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); |
|
||||
string file = Path.Combine(dir, "SaveAsExr_Path_Encoder.exr"); |
|
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
image.SaveAsOpenExr(file, new ExrEncoder()); |
|
||||
} |
|
||||
|
|
||||
using (Image.Load(file, out IImageFormat mime)) |
using (Image.Load(memoryStream, out IImageFormat mime)) |
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task SaveAsExrAsync_Path_Encoder() |
|
||||
{ |
{ |
||||
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions)); |
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
||||
string file = Path.Combine(dir, "SaveAsExrAsync_Path_Encoder.tiff"); |
|
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
await image.SaveAsOpenExrAsync(file, new ExrEncoder()); |
|
||||
} |
|
||||
|
|
||||
using (Image.Load(file, out IImageFormat mime)) |
|
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
[Fact] |
[Fact] |
||||
public void SaveAsExr_Stream() |
public void SaveAsExr_Stream_Encoder() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(); |
using var memoryStream = new MemoryStream(); |
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
image.SaveAsOpenExr(memoryStream); |
|
||||
} |
|
||||
|
|
||||
memoryStream.Position = 0; |
|
||||
|
|
||||
using (Image.Load(memoryStream, out IImageFormat mime)) |
|
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
using (var image = new Image<Rgba32>(10, 10)) |
||||
public async Task SaveAsExrAsync_StreamAsync() |
|
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(); |
image.SaveAsOpenExr(memoryStream, new ExrEncoder()); |
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
await image.SaveAsOpenExrAsync(memoryStream); |
|
||||
} |
|
||||
|
|
||||
memoryStream.Position = 0; |
|
||||
|
|
||||
using (Image.Load(memoryStream, out IImageFormat mime)) |
|
||||
{ |
|
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
[Fact] |
memoryStream.Position = 0; |
||||
public void SaveAsExr_Stream_Encoder() |
|
||||
{ |
|
||||
using var memoryStream = new MemoryStream(); |
|
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
|
||||
{ |
|
||||
image.SaveAsOpenExr(memoryStream, new ExrEncoder()); |
|
||||
} |
|
||||
|
|
||||
memoryStream.Position = 0; |
|
||||
|
|
||||
using (Image.Load(memoryStream, out IImageFormat mime)) |
using (Image.Load(memoryStream, out IImageFormat mime)) |
||||
{ |
{ |
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
[Fact] |
[Fact] |
||||
public async Task SaveAsExrAsync_Stream_Encoder() |
public async Task SaveAsExrAsync_Stream_Encoder() |
||||
{ |
{ |
||||
using var memoryStream = new MemoryStream(); |
using var memoryStream = new MemoryStream(); |
||||
|
|
||||
using (var image = new Image<Rgba32>(10, 10)) |
using (var image = new Image<Rgba32>(10, 10)) |
||||
{ |
{ |
||||
await image.SaveAsOpenExrAsync(memoryStream, new ExrEncoder()); |
await image.SaveAsOpenExrAsync(memoryStream, new ExrEncoder()); |
||||
} |
} |
||||
|
|
||||
memoryStream.Position = 0; |
memoryStream.Position = 0; |
||||
|
|
||||
using (Image.Load(memoryStream, out IImageFormat mime)) |
using (Image.Load(memoryStream, out IImageFormat mime)) |
||||
{ |
{ |
||||
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
Assert.Equal("image/x-exr", mime.DefaultMimeType); |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
|||||
Loading…
Reference in new issue