|
|
@ -9,7 +9,6 @@ using System.Runtime.CompilerServices; |
|
|
using System.Text; |
|
|
using System.Text; |
|
|
using System.Threading; |
|
|
using System.Threading; |
|
|
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
|
|
using SixLabors.ImageSharp.Formats.OpenExr.Compression; |
|
|
using SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors; |
|
|
|
|
|
using SixLabors.ImageSharp.Formats.Pbm; |
|
|
using SixLabors.ImageSharp.Formats.Pbm; |
|
|
using SixLabors.ImageSharp.IO; |
|
|
using SixLabors.ImageSharp.IO; |
|
|
using SixLabors.ImageSharp.Memory; |
|
|
using SixLabors.ImageSharp.Memory; |
|
|
@ -77,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
{ |
|
|
{ |
|
|
this.ReadExrHeader(stream); |
|
|
this.ReadExrHeader(stream); |
|
|
|
|
|
|
|
|
if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips and not ExrCompressionType.RunLengthEncoded) |
|
|
if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips and not ExrCompressionType.Zip and not ExrCompressionType.RunLengthEncoded) |
|
|
{ |
|
|
{ |
|
|
ExrThrowHelper.ThrowNotSupported($"Compression {this.Compression} is not yet supported"); |
|
|
ExrThrowHelper.ThrowNotSupported($"Compression {this.Compression} is not yet supported"); |
|
|
} |
|
|
} |
|
|
@ -109,19 +108,21 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
{ |
|
|
{ |
|
|
bool hasAlpha = this.HasAlpha(); |
|
|
bool hasAlpha = this.HasAlpha(); |
|
|
uint bytesPerRow = this.CalculateBytesPerRow(); |
|
|
uint bytesPerRow = this.CalculateBytesPerRow(); |
|
|
|
|
|
uint rowsPerBlock = this.RowsPerBlock(); |
|
|
|
|
|
uint bytesPerBlock = bytesPerRow * rowsPerBlock; |
|
|
|
|
|
|
|
|
using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 4); |
|
|
using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 4); |
|
|
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerRow); |
|
|
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerBlock); |
|
|
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan(); |
|
|
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan(); |
|
|
Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); |
|
|
Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); |
|
|
Span<float> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); |
|
|
Span<float> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); |
|
|
Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); |
|
|
Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); |
|
|
Span<float> alphaPixelData = rowBuffer.GetSpan().Slice(this.Width * 3, this.Width); |
|
|
Span<float> alphaPixelData = rowBuffer.GetSpan().Slice(this.Width * 3, this.Width); |
|
|
|
|
|
|
|
|
using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, bytesPerRow); |
|
|
using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, bytesPerBlock); |
|
|
|
|
|
|
|
|
TPixel color = default; |
|
|
TPixel color = default; |
|
|
for (int y = 0; y < this.Height; y++) |
|
|
for (uint y = 0; y < this.Height; y += rowsPerBlock) |
|
|
{ |
|
|
{ |
|
|
stream.Read(this.buffer, 0, 8); |
|
|
stream.Read(this.buffer, 0, 8); |
|
|
ulong rowOffset = BinaryPrimitives.ReadUInt64LittleEndian(this.buffer); |
|
|
ulong rowOffset = BinaryPrimitives.ReadUInt64LittleEndian(this.buffer); |
|
|
@ -129,15 +130,16 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
|
|
|
|
|
|
stream.Position = (long)rowOffset; |
|
|
stream.Position = (long)rowOffset; |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
uint rowIndex = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
uint rowStartIndex = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
|
|
|
|
|
|
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan((int)rowIndex); |
|
|
|
|
|
|
|
|
|
|
|
stream.Read(this.buffer, 0, 4); |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
uint compressedBytesCount = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
uint compressedBytesCount = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData); |
|
|
decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData); |
|
|
|
|
|
|
|
|
int offset = 0; |
|
|
int offset = 0; |
|
|
|
|
|
for (uint rowIndex = rowStartIndex; rowIndex < rowStartIndex + rowsPerBlock && rowIndex < this.Height; rowIndex++) |
|
|
|
|
|
{ |
|
|
|
|
|
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan((int)rowIndex); |
|
|
for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++) |
|
|
for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++) |
|
|
{ |
|
|
{ |
|
|
ExrChannelInfo channel = this.Channels[channelIdx]; |
|
|
ExrChannelInfo channel = this.Channels[channelIdx]; |
|
|
@ -203,8 +205,6 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
stream.Position = nextRowOffsetPosition; |
|
|
|
|
|
|
|
|
|
|
|
if (hasAlpha) |
|
|
if (hasAlpha) |
|
|
{ |
|
|
{ |
|
|
for (int x = 0; x < this.Width; x++) |
|
|
for (int x = 0; x < this.Width; x++) |
|
|
@ -224,6 +224,9 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stream.Position = nextRowOffsetPosition; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void DecodeUnsignedIntPixelData<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels) |
|
|
private void DecodeUnsignedIntPixelData<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels) |
|
|
@ -231,19 +234,21 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
{ |
|
|
{ |
|
|
bool hasAlpha = this.HasAlpha(); |
|
|
bool hasAlpha = this.HasAlpha(); |
|
|
uint bytesPerRow = this.CalculateBytesPerRow(); |
|
|
uint bytesPerRow = this.CalculateBytesPerRow(); |
|
|
|
|
|
uint rowsPerBlock = this.RowsPerBlock(); |
|
|
|
|
|
uint bytesPerBlock = bytesPerRow * rowsPerBlock; |
|
|
|
|
|
|
|
|
using IMemoryOwner<uint> rowBuffer = this.memoryAllocator.Allocate<uint>(this.Width * 4); |
|
|
using IMemoryOwner<uint> rowBuffer = this.memoryAllocator.Allocate<uint>(this.Width * 4); |
|
|
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerRow); |
|
|
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerBlock); |
|
|
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan(); |
|
|
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan(); |
|
|
Span<uint> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); |
|
|
Span<uint> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); |
|
|
Span<uint> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); |
|
|
Span<uint> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); |
|
|
Span<uint> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); |
|
|
Span<uint> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); |
|
|
Span<uint> alphaPixelData = rowBuffer.GetSpan().Slice(this.Width * 3, this.Width); |
|
|
Span<uint> alphaPixelData = rowBuffer.GetSpan().Slice(this.Width * 3, this.Width); |
|
|
|
|
|
|
|
|
using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, bytesPerRow); |
|
|
using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, bytesPerBlock); |
|
|
|
|
|
|
|
|
TPixel color = default; |
|
|
TPixel color = default; |
|
|
for (int y = 0; y < this.Height; y++) |
|
|
for (uint y = 0; y < this.Height; y += rowsPerBlock) |
|
|
{ |
|
|
{ |
|
|
stream.Read(this.buffer, 0, 8); |
|
|
stream.Read(this.buffer, 0, 8); |
|
|
ulong rowOffset = BinaryPrimitives.ReadUInt64LittleEndian(this.buffer); |
|
|
ulong rowOffset = BinaryPrimitives.ReadUInt64LittleEndian(this.buffer); |
|
|
@ -251,15 +256,16 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
|
|
|
|
|
|
stream.Position = (long)rowOffset; |
|
|
stream.Position = (long)rowOffset; |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
uint rowIndex = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
uint rowStartIndex = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
|
|
|
|
|
|
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan((int)rowIndex); |
|
|
|
|
|
|
|
|
|
|
|
stream.Read(this.buffer, 0, 4); |
|
|
stream.Read(this.buffer, 0, 4); |
|
|
uint compressedBytesCount = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
uint compressedBytesCount = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); |
|
|
decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData); |
|
|
decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData); |
|
|
|
|
|
|
|
|
int offset = 0; |
|
|
int offset = 0; |
|
|
|
|
|
for (uint rowIndex = rowStartIndex; rowIndex < rowStartIndex + rowsPerBlock && rowIndex < this.Height; rowIndex++) |
|
|
|
|
|
{ |
|
|
|
|
|
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan((int)rowIndex); |
|
|
for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++) |
|
|
for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++) |
|
|
{ |
|
|
{ |
|
|
ExrChannelInfo channel = this.Channels[channelIdx]; |
|
|
ExrChannelInfo channel = this.Channels[channelIdx]; |
|
|
@ -335,14 +341,14 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private int ReadPixelRowChannelHalfSingle(Span<byte> decompressedPixelData, Span<float> channelData) |
|
|
private int ReadPixelRowChannelHalfSingle(Span<byte> decompressedPixelData, Span<float> channelData) |
|
|
{ |
|
|
{ |
|
|
int offset = 0; |
|
|
int offset = 0; |
|
|
ushort shortValue = 0; |
|
|
|
|
|
for (int x = 0; x < this.Width; x++) |
|
|
for (int x = 0; x < this.Width; x++) |
|
|
{ |
|
|
{ |
|
|
shortValue = BinaryPrimitives.ReadUInt16LittleEndian(decompressedPixelData.Slice(offset, 2)); |
|
|
ushort shortValue = BinaryPrimitives.ReadUInt16LittleEndian(decompressedPixelData.Slice(offset, 2)); |
|
|
channelData[x] = HalfTypeHelper.Unpack(shortValue); |
|
|
channelData[x] = HalfTypeHelper.Unpack(shortValue); |
|
|
offset += 2; |
|
|
offset += 2; |
|
|
} |
|
|
} |
|
|
@ -353,10 +359,9 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
private int ReadPixelRowChannelSingle(Span<byte> decompressedPixelData, Span<float> channelData) |
|
|
private int ReadPixelRowChannelSingle(Span<byte> decompressedPixelData, Span<float> channelData) |
|
|
{ |
|
|
{ |
|
|
int offset = 0; |
|
|
int offset = 0; |
|
|
int intValue = 0; |
|
|
|
|
|
for (int x = 0; x < this.Width; x++) |
|
|
for (int x = 0; x < this.Width; x++) |
|
|
{ |
|
|
{ |
|
|
intValue = BinaryPrimitives.ReadInt32LittleEndian(decompressedPixelData.Slice(offset, 4)); |
|
|
int intValue = BinaryPrimitives.ReadInt32LittleEndian(decompressedPixelData.Slice(offset, 4)); |
|
|
channelData[x] = Unsafe.As<int, float>(ref intValue); |
|
|
channelData[x] = Unsafe.As<int, float>(ref intValue); |
|
|
offset += 4; |
|
|
offset += 4; |
|
|
} |
|
|
} |
|
|
@ -668,5 +673,22 @@ namespace SixLabors.ImageSharp.Formats.OpenExr |
|
|
|
|
|
|
|
|
return bytesPerRow; |
|
|
return bytesPerRow; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private uint RowsPerBlock() |
|
|
|
|
|
{ |
|
|
|
|
|
switch (this.Compression) |
|
|
|
|
|
{ |
|
|
|
|
|
case ExrCompressionType.Zip: |
|
|
|
|
|
case ExrCompressionType.Pxr24: |
|
|
|
|
|
return 16; |
|
|
|
|
|
case ExrCompressionType.B44: |
|
|
|
|
|
case ExrCompressionType.B44A: |
|
|
|
|
|
case ExrCompressionType.Piz: |
|
|
|
|
|
return 32; |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
return 1; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|