From 95ee73e241186b779a4ecfe35e3cd3c06759b282 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 25 Apr 2026 16:10:57 +0200 Subject: [PATCH] Implement pxr decompression for pixel type uint and float --- .../Decompressors/Pxr24Compression.cs | 106 +++++++++++++++--- .../Exr/Compression/ExrDecompressorFactory.cs | 5 +- src/ImageSharp/Formats/Exr/ExrDecoderCore.cs | 20 +++- 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs index d720c190f9..d2fdcfcd16 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.IO.Compression; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Exr.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -21,6 +22,8 @@ internal class Pxr24Compression : ExrBaseDecompressor private readonly int channelCount; + private readonly ExrPixelType pixelType; + /// /// Initializes a new instance of the class. /// @@ -30,12 +33,14 @@ internal class Pxr24Compression : ExrBaseDecompressor /// The pixel rows per block. /// The witdh of one row in pixels. /// The number of channels for a pixel. - public Pxr24Compression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount) + /// The pixel type. + public Pxr24Compression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount, ExrPixelType pixelType) : base(allocator, bytesPerBlock, bytesPerRow, width) { this.tmpBuffer = allocator.Allocate((int)bytesPerBlock); this.rowsPerBlock = rowsPerBlock; this.channelCount = channelCount; + this.pixelType = pixelType; } /// @@ -78,23 +83,94 @@ internal class Pxr24Compression : ExrBaseDecompressor { for (int c = 0; c < this.channelCount; c++) { - int offsetT1 = lastIn; - lastIn += this.Width; - int offsetT2 = lastIn; - lastIn += this.Width; - uint pixel = 0; - for (int x = 0; x < this.Width; x++) + switch (this.pixelType) { - uint t1 = uncompressed[offsetT1]; - uint t2 = uncompressed[offsetT2]; - uint diff = (t1 << 8) | t2; + case ExrPixelType.UnsignedInt: + { + int offsetT0 = lastIn; + lastIn += this.Width; + int offsetT1 = lastIn; + lastIn += this.Width; + int offsetT2 = lastIn; + lastIn += this.Width; + int offsetT3 = lastIn; + lastIn += this.Width; + + uint pixel = 0; + for (int x = 0; x < this.Width; x++) + { + uint t0 = uncompressed[offsetT0]; + uint t1 = uncompressed[offsetT1]; + uint t2 = uncompressed[offsetT2]; + uint t3 = uncompressed[offsetT3]; + uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8) | t3; + + pixel += diff; + outputBuffer[outputOffset] = (ushort)pixel; + + offsetT0++; + offsetT1++; + offsetT2++; + offsetT3++; + outputOffset++; + } + + break; + } + + case ExrPixelType.Half: + { + int offsetT0 = lastIn; + lastIn += this.Width; + int offsetT1 = lastIn; + lastIn += this.Width; + + uint pixel = 0; + for (int x = 0; x < this.Width; x++) + { + uint t0 = uncompressed[offsetT0]; + uint t1 = uncompressed[offsetT1]; + uint diff = (t0 << 8) | t1; + + pixel += diff; + outputBuffer[outputOffset] = (ushort)pixel; + + offsetT0++; + offsetT1++; + outputOffset++; + } + + break; + } + + case ExrPixelType.Float: + { + int offsetT0 = lastIn; + lastIn += this.Width; + int offsetT1 = lastIn; + lastIn += this.Width; + int offsetT2 = lastIn; + lastIn += this.Width; + + uint pixel = 0; + for (int x = 0; x < this.Width; x++) + { + uint t0 = uncompressed[offsetT0]; + uint t1 = uncompressed[offsetT1]; + uint t2 = uncompressed[offsetT2]; + uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8); + + pixel += diff; + outputBuffer[outputOffset] = (ushort)pixel; - pixel += diff; - outputBuffer[outputOffset] = (ushort)pixel; + offsetT0++; + offsetT1++; + offsetT2++; + outputOffset++; + } - offsetT1++; - offsetT2++; - outputOffset++; + break; + } } } } diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs index 8123284585..6f63a932b7 100644 --- a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs +++ b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs @@ -30,14 +30,15 @@ internal static class ExrDecompressorFactory uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, - int channelCount) => method switch + int channelCount, + ExrPixelType pixelType) => method switch { ExrCompression.None => new NoneExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, width), ExrCompression.Zips => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, width), ExrCompression.Zip => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, width), ExrCompression.RunLengthEncoded => new RunLengthExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, width), ExrCompression.B44 => new B44ExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount), - ExrCompression.Pxr24 => new Pxr24Compression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount), + ExrCompression.Pxr24 => new Pxr24Compression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount, pixelType), _ => throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)), }; } diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs index ddfbcdc186..2e60f3a5b9 100644 --- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs +++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs @@ -154,7 +154,15 @@ internal sealed class ExrDecoderCore : ImageDecoderCore Span bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width); Span alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width); - using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, width, bytesPerBlock, bytesPerRow, rowsPerBlock, channelCount); + using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create( + this.Compression, + this.memoryAllocator, + width, + bytesPerBlock, + bytesPerRow, + rowsPerBlock, + channelCount, + this.PixelType); int decodedRows = 0; while (decodedRows < height) @@ -219,7 +227,15 @@ internal sealed class ExrDecoderCore : ImageDecoderCore Span bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width); Span alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width); - using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, width, bytesPerBlock, bytesPerRow, rowsPerBlock, channelCount); + using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create( + this.Compression, + this.memoryAllocator, + width, + bytesPerBlock, + bytesPerRow, + rowsPerBlock, + channelCount, + this.PixelType); int decodedRows = 0; while (decodedRows < height)