/// <inheritdoc /> public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } int bitCount = _bitCount; bool isHigherOrderBitsFirst = _fillOrder != TiffFillOrder.LowerOrderBitsFirst; int bytesPerScanline = (context.SourceImageSize.Width * bitCount + 7) / 8; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffGray16> writer = context.GetWriter <TiffGray16>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; // BitReader.Read reads bytes in big-endian way, we only need to reverse the endianness if the source is little-endian. bool reverseEndianness = context.IsLittleEndian && bitCount % 8 == 0; bool canDoFastPath = bitCount >= 16 && !reverseEndianness; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray16> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffGray16> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * bitCount); if (canDoFastPath) { // Fast path for bits >= 16 for (int col = 0; col < cols; col++) { uint value = bitReader.Read(bitCount); value = FastExpandBits(value, bitCount, 32); pixelSpan[col] = new TiffGray16((ushort)(~value >> 16)); } } else { // Slow path for (int col = 0; col < cols; col++) { uint value = bitReader.Read(bitCount); value = (uint)ExpandBits(value, bitCount, 32, reverseEndianness); pixelSpan[col] = new TiffGray16((ushort)(~value >> 16)); } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
public void TestGray16() { Assert.Equal(2, Unsafe.SizeOf <TiffGray16>()); var defaultPixel = default(TiffGray16); Assert.Equal(0, defaultPixel.Intensity); var pixel1 = new TiffGray16(0x1234); Assert.Equal(0x1234, pixel1.Intensity); Assert.False(pixel1.Equals(defaultPixel)); Assert.False(defaultPixel.Equals(pixel1)); Assert.False(pixel1 == defaultPixel); Assert.False(defaultPixel == pixel1); Assert.True(pixel1 != defaultPixel); Assert.True(defaultPixel != pixel1); Assert.False(pixel1.GetHashCode() == defaultPixel.GetHashCode()); var pixel2 = new TiffGray16(0x1234); Assert.True(pixel1.Equals(pixel2)); Assert.True(pixel2.Equals(pixel1)); Assert.True(pixel1 == pixel2); Assert.True(pixel2 == pixel1); Assert.False(pixel1 != pixel2); Assert.False(pixel2 != pixel1); Assert.True(pixel1.GetHashCode() == pixel2.GetHashCode()); }
public async Task TestImageDecodeAsync(string refImage, int refIfd, int refBitDepth, string testImage, int testIfd, int testBitDepth) { const int BufferBitDepth = 16; // We do not support decoding into 32 bit buffer yet. refBitDepth = Math.Min(refBitDepth, BufferBitDepth); testBitDepth = Math.Min(testBitDepth, BufferBitDepth); TiffStreamOffset ifdOffset; TiffImageFileDirectory ifd; // Load reference image await using TiffFileReader refTiff = await TiffFileReader.OpenAsync(refImage); ifdOffset = refTiff.FirstImageFileDirectoryOffset; Assert.False(ifdOffset.IsZero); for (int i = 0; i < refIfd; i++) { ifd = await refTiff.ReadImageFileDirectoryAsync(ifdOffset); ifdOffset = ifd.NextOffset; Assert.False(ifdOffset.IsZero); } ifd = await refTiff.ReadImageFileDirectoryAsync(ifdOffset); TiffImageDecoder refDecoder = await refTiff.CreateImageDecoderAsync(ifd); // Load test image await using TiffFileReader testTiff = await TiffFileReader.OpenAsync(testImage); ifdOffset = testTiff.FirstImageFileDirectoryOffset; Assert.False(ifdOffset.IsZero); for (int i = 0; i < testIfd; i++) { ifd = await testTiff.ReadImageFileDirectoryAsync(ifdOffset); ifdOffset = ifd.NextOffset; Assert.False(ifdOffset.IsZero); } ifd = await testTiff.ReadImageFileDirectoryAsync(ifdOffset); TiffImageDecoder testDecoder = await testTiff.CreateImageDecoderAsync(ifd); Assert.Equal(refDecoder.Width, testDecoder.Width); Assert.Equal(refDecoder.Height, testDecoder.Height); TiffGray16[] refBuffer = new TiffGray16[refDecoder.Width * refDecoder.Height]; TiffGray16[] testBuffer = new TiffGray16[testDecoder.Width * testDecoder.Height]; await refDecoder.DecodeAsync(TiffPixelBuffer.Wrap(refBuffer, refDecoder.Width, refDecoder.Height)); await testDecoder.DecodeAsync(TiffPixelBuffer.Wrap(testBuffer, testDecoder.Width, testDecoder.Height)); ShiftPixels(MemoryMarshal.Cast <TiffGray16, ushort>(refBuffer), BufferBitDepth - refBitDepth); ShiftPixels(MemoryMarshal.Cast <TiffGray16, ushort>(testBuffer), BufferBitDepth - testBitDepth); Assert.True(refBuffer.AsSpan().SequenceEqual(testBuffer)); }