public override Span <TSource> GetSpan() { if (_innerHandle is null) { throw new ObjectDisposedException(GetType().FullName); } if (_useInplaceConvert) { return(MemoryMarshal.Cast <TDestination, TSource>(_innerHandle.GetSpan())); } return(MemoryMarshal.Cast <byte, TSource>(_buffer.AsSpan(0, _length * Unsafe.SizeOf <TSource>()))); }
/// <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 bytesPerScanline = 4 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffCmyk32> writer = context.GetWriter <TiffCmyk32>(); int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffCmyk32> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); sourceSpan.Slice(4 * context.SourceReadOffset.X, 4 * context.ReadSize.Width).CopyTo(rowDestinationSpan); sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
private static void ProcessAssociatedWithUndoColorPreMultiplying(TiffImageDecoderContext context) { int bytesPerScanline = 4 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <TiffRgba32> rowSourceSpan = MemoryMarshal.Cast <byte, TiffRgba32>(sourceSpan.Slice(4 * context.SourceReadOffset.X, 4 * context.ReadSize.Width)); Span <TiffRgba32> rowDestinationSpan = pixelSpanHandle.GetSpan(); for (int col = 0; col < cols; col++) { TiffRgba32 pixel = rowSourceSpan[col]; byte a = pixel.A; if (a != 0) { pixel.R = (byte)(pixel.R * 255 / a); pixel.G = (byte)(pixel.G * 255 / a); pixel.B = (byte)(pixel.B * 255 / a); } rowDestinationSpan[col] = pixel; } sourceSpan = sourceSpan.Slice(bytesPerScanline); } }
/// <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)); } TiffYCbCrConverter8 converter = _converter; int bytesPerScanline = 3 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <byte> rowSourceSpan = sourceSpan.Slice(3 * context.SourceReadOffset.X, 3 * context.ReadSize.Width); Span <TiffRgba32> rowDestinationSpan = pixelSpanHandle.GetSpan(); converter.ConvertToRgba32(rowSourceSpan, rowDestinationSpan, cols); sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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 override Span <TSource> GetSpan() { if (_innerHandle is null) { throw new ObjectDisposedException(GetType().FullName); } return(MemoryMarshal.Cast <TDestination, TSource>(_innerHandle.GetSpan())); }
/// <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)); } TiffYCbCrConverter16 converter = _converter; int skippedRowOffset = context.SourceImageSize.Width * context.SourceReadOffset.Y; int planarByteCount = sizeof(ushort) * context.SourceImageSize.Width * context.SourceImageSize.Height; ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span; ReadOnlySpan <ushort> sourceY = MemoryMarshal.Cast <byte, ushort>(sourceSpan.Slice(0, planarByteCount)); ReadOnlySpan <ushort> sourceCb = MemoryMarshal.Cast <byte, ushort>(sourceSpan.Slice(planarByteCount, planarByteCount)); ReadOnlySpan <ushort> sourceCr = MemoryMarshal.Cast <byte, ushort>(sourceSpan.Slice(2 * planarByteCount, planarByteCount)); using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; bool reverseEndiannessNeeded = context.IsLittleEndian != BitConverter.IsLittleEndian; if (reverseEndiannessNeeded) { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); int rowOffset = skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X; for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + col; rowDestinationSpan[col] = converter.ConvertToRgba64(BinaryPrimitives.ReverseEndianness(sourceY[componentOffset]), BinaryPrimitives.ReverseEndianness(sourceCb[componentOffset]), BinaryPrimitives.ReverseEndianness(sourceCr[componentOffset])); } } } else { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); int rowOffset = skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X; for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + col; rowDestinationSpan[col] = converter.ConvertToRgba64(sourceY[componentOffset], sourceCb[componentOffset], sourceCr[componentOffset]); } } } return(next.RunAsync(context)); }
/// <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 <TiffGray8> writer = context.GetWriter <TiffGray8>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffGray8> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * bitCount); if (bitCount * 2 >= 8) { // Fast path for bits >= 4 for (int col = 0; col < cols; col++) { uint value = bitReader.Read(bitCount); value = FastExpandBits(value, bitCount, 8); pixelSpan[col] = new TiffGray8((byte)value); } } else { // Slow path for (int col = 0; col < cols; col++) { uint value = bitReader.Read(bitCount); value = ExpandBits(value, bitCount, 8); pixelSpan[col] = new TiffGray8((byte)value); } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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)); } TiffYCbCrConverter16 converter = _converter; int elementsPerScanline = 3 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * elementsPerScanline * sizeof(ushort)); Span <ushort> sourceSpan = MemoryMarshal.Cast <byte, ushort>(source.Span); using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; bool reverseEndiannessNeeded = context.IsLittleEndian != BitConverter.IsLittleEndian; if (reverseEndiannessNeeded) { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <ushort> rowSourceSpan = sourceSpan.Slice(3 * context.SourceReadOffset.X, 3 * context.ReadSize.Width); Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); for (int i = 0; i < rowSourceSpan.Length; i++) { rowSourceSpan[i] = BinaryPrimitives.ReverseEndianness(rowSourceSpan[i]); } converter.ConvertToRgba64(rowSourceSpan, rowDestinationSpan, cols); sourceSpan = sourceSpan.Slice(elementsPerScanline); } } else { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <ushort> rowSourceSpan = sourceSpan.Slice(3 * context.SourceReadOffset.X, 3 * context.ReadSize.Width); Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); converter.ConvertToRgba64(rowSourceSpan, rowDestinationSpan, cols); sourceSpan = sourceSpan.Slice(elementsPerScanline); } } return(next.RunAsync(context)); }
/// <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 bytesPerScanline = 8 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <TiffCmyk64> cmykSourceSpan = MemoryMarshal.Cast <byte, TiffCmyk64>(source.Span); using TiffPixelBufferWriter <TiffCmyk64> writer = context.GetWriter <TiffCmyk64>(); int rows = context.ReadSize.Height; if (context.IsLittleEndian == BitConverter.IsLittleEndian) { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffCmyk64> pixelSpanHandle = writer.GetRowSpan(row); cmykSourceSpan.Slice(context.SourceReadOffset.X, context.ReadSize.Width).CopyTo(pixelSpanHandle.GetSpan()); cmykSourceSpan = cmykSourceSpan.Slice(context.SourceImageSize.Width); } } else { int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffCmyk64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffCmyk64> rowDestinationSpan = pixelSpanHandle.GetSpan(); for (int col = 0; col < cols; col++) { TiffCmyk64 cmyk = cmykSourceSpan[col]; cmyk.C = BinaryPrimitives.ReverseEndianness(cmyk.C); cmyk.M = BinaryPrimitives.ReverseEndianness(cmyk.M); cmyk.Y = BinaryPrimitives.ReverseEndianness(cmyk.Y); cmyk.K = BinaryPrimitives.ReverseEndianness(cmyk.K); rowDestinationSpan[col] = cmyk; } cmykSourceSpan = cmykSourceSpan.Slice(context.SourceImageSize.Width); } } return(next.RunAsync(context)); }
/// <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)); } // Colormap array is read using TiffTagReader, its elements should be in machine-endian. ushort[] colorMap = _colorMap; 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 <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; TiffRgba64 pixel = default; pixel.A = ushort.MaxValue; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgba64> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * bitCount); for (int col = 0; col < cols; col++) { uint offset = bitReader.Read(bitCount); pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; pixelSpan[col] = pixel; } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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)); } // Colormap array is read using TiffTagReader, its elements should be in machine-endian. ushort[] colorMap = _colorMap; int bytesPerScanline = context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); int rows = context.ReadSize.Height; TiffRgba64 pixel = default; pixel.A = ushort.MaxValue; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <byte> rowSourceSpan = sourceSpan.Slice(context.SourceReadOffset.X, context.ReadSize.Width); Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); for (int i = 0; i < rowSourceSpan.Length; i++) { int offset = rowSourceSpan[i]; pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; rowDestinationSpan[i] = pixel; } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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 bytesPerScanline = context.SourceImageSize.Width * 2; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffGray16> writer = context.GetWriter <TiffGray16>(); bool reverseEndiannessNeeded = context.IsLittleEndian != BitConverter.IsLittleEndian; int rows = context.ReadSize.Height; if (reverseEndiannessNeeded) { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray16> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <ushort> scanline = MemoryMarshal.Cast <byte, ushort>(sourceSpan).Slice(context.SourceReadOffset.X, context.ReadSize.Width); Span <ushort> destination16 = MemoryMarshal.Cast <TiffGray16, ushort>(pixelSpanHandle.GetSpan()); for (int i = 0; i < scanline.Length; i++) { destination16[i] = BinaryPrimitives.ReverseEndianness(scanline[i]); } sourceSpan = sourceSpan.Slice(bytesPerScanline); } } else { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray16> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); sourceSpan.Slice(sizeof(ushort) * context.SourceReadOffset.X, sizeof(ushort) * context.ReadSize.Width).CopyTo(rowDestinationSpan); sourceSpan = sourceSpan.Slice(bytesPerScanline); } } return(next.RunAsync(context)); }
public void TestWriteOnStructWriter() { ITiffPixelBuffer <TiffGray8> pixelBuffer = PixelBufferTests.InitializePixelBuffer(out TiffGray8[] pixels); var writer = new TiffPixelBufferWriterAdapter <TiffGray8>(pixelBuffer); TiffPixelBufferWriter <TiffGray8> structWriter; structWriter = writer.Crop(new TiffPoint(1, 0), new TiffSize(1, 1)); Assert.Equal(1, structWriter.Width); Assert.Equal(1, structWriter.Height); using (TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = structWriter.GetRowSpan(0, 0, 1)) { Assert.Equal(1, pixelSpanHandle.Length); Span <TiffGray8> span = pixelSpanHandle.GetSpan(); Assert.Equal(1, span.Length); span[0] = new TiffGray8(0xa1); } Assert.Equal(0x11, pixels[0].Intensity); Assert.Equal(0xa1, pixels[1].Intensity); Assert.Equal(0x13, pixels[2].Intensity); Assert.Equal(0x21, pixels[3].Intensity); Assert.Equal(0x22, pixels[4].Intensity); Assert.Equal(0x23, pixels[5].Intensity); using (TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = structWriter.GetColumnSpan(0, 0, 1)) { Assert.Equal(1, pixelSpanHandle.Length); Span <TiffGray8> span = pixelSpanHandle.GetSpan(); Assert.Equal(1, span.Length); span[0] = new TiffGray8(0xa2); } Assert.Equal(0x11, pixels[0].Intensity); Assert.Equal(0xa2, pixels[1].Intensity); Assert.Equal(0x13, pixels[2].Intensity); Assert.Equal(0x21, pixels[3].Intensity); Assert.Equal(0x22, pixels[4].Intensity); Assert.Equal(0x23, pixels[5].Intensity); // Failure cases Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => structWriter.GetRowSpan(-1, 0, 1)); Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => structWriter.GetRowSpan(1, 0, 1)); Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetRowSpan(0, -1, 1)); Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetRowSpan(0, 2, 1)); Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetRowSpan(0, 0, -1)); Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetRowSpan(0, 0, 2)); }
public void TestWriteRows() { ITiffPixelBuffer <TiffGray8> pixelBuffer = PixelBufferTests.InitializePixelBuffer(out TiffGray8[] pixels); var writer = new TiffPixelBufferWriterAdapter <TiffGray8>(pixelBuffer); // Write to first row using (TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(0, 0, 1)) { Assert.Equal(1, pixelSpanHandle.Length); Span <TiffGray8> span = pixelSpanHandle.GetSpan(); Assert.Equal(1, span.Length); span[0] = new TiffGray8(0xa1); } // Make sure only the affected pixels are changed while others stay the same. Assert.Equal(0xa1, pixels[0].Intensity); Assert.Equal(0x12, pixels[1].Intensity); Assert.Equal(0x13, pixels[2].Intensity); Assert.Equal(0x21, pixels[3].Intensity); Assert.Equal(0x22, pixels[4].Intensity); Assert.Equal(0x23, pixels[5].Intensity); // Write to second row using (TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(1, 1, 2)) { Assert.Equal(2, pixelSpanHandle.Length); Span <TiffGray8> span = pixelSpanHandle.GetSpan(); Assert.Equal(2, span.Length); span[0] = new TiffGray8(0xa2); span[1] = new TiffGray8(0xa3); } // Make sure only the affected pixels are changed while others stay the same. Assert.Equal(0xa1, pixels[0].Intensity); Assert.Equal(0x12, pixels[1].Intensity); Assert.Equal(0x13, pixels[2].Intensity); Assert.Equal(0x21, pixels[3].Intensity); Assert.Equal(0xa2, pixels[4].Intensity); Assert.Equal(0xa3, pixels[5].Intensity); // Failure cases Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => writer.GetRowSpan(-1, 1, 1)); Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => writer.GetRowSpan(2, 1, 1)); Assert.Throws <ArgumentOutOfRangeException>("start", () => writer.GetRowSpan(0, -1, 1)); Assert.Throws <ArgumentOutOfRangeException>("start", () => writer.GetRowSpan(0, 4, 1)); Assert.Throws <ArgumentOutOfRangeException>("length", () => writer.GetRowSpan(0, 1, -1)); Assert.Throws <ArgumentOutOfRangeException>("length", () => writer.GetRowSpan(0, 1, 3)); }
public void Write(TiffImageDecoderContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } TiffRgba32 fillColor = _fillColor; using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>(); int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row); pixelSpanHandle.GetSpan().Fill(fillColor); } }
/// <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 skippedRowOffset = context.SourceImageSize.Width * context.SourceReadOffset.Y; int planarByteCount = context.SourceImageSize.Width * context.SourceImageSize.Height; ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span; ReadOnlySpan <byte> sourceC = sourceSpan.Slice(0, planarByteCount); ReadOnlySpan <byte> sourceM = sourceSpan.Slice(planarByteCount, planarByteCount); ReadOnlySpan <byte> sourceY = sourceSpan.Slice(2 * planarByteCount, planarByteCount); ReadOnlySpan <byte> sourceK = sourceSpan.Slice(3 * planarByteCount, planarByteCount); using TiffPixelBufferWriter <TiffCmyk32> writer = context.GetWriter <TiffCmyk32>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffCmyk32> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffCmyk32> rowDestinationSpan = pixelSpanHandle.GetSpan(); int rowOffset = skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X; for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + col; rowDestinationSpan[col] = new TiffCmyk32(sourceC[componentOffset], sourceM[componentOffset], sourceY[componentOffset], sourceK[componentOffset]); } } return(next.RunAsync(context)); }
public ValueTask ReadAsync(TiffPoint offset, TiffPixelBufferWriter <TPixel> destination, CancellationToken cancellationToken) { if (offset.X >= (uint)_size.Width || offset.Y >= (uint)_size.Height) { throw new ArgumentOutOfRangeException(nameof(offset)); } ImageFrame <TPixel> image = _image; int offsetX = _offset.X + offset.X; int offsetY = _offset.Y + offset.Y; int width = Math.Min(_size.Width - offset.X, destination.Width); int height = Math.Min(_size.Height - offset.Y, destination.Height); for (int row = 0; row < height; row++) { Span <TPixel> sourceSpan = image.GetPixelRowSpan(offsetY + row).Slice(offsetX, width); using TiffPixelSpanHandle <TPixel> destinationHandle = destination.GetRowSpan(row); sourceSpan.CopyTo(destinationHandle.GetSpan()); } return(default);
/// <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)); } // Colormap array is read using TiffTagReader, its elements should be in machine-endian. ushort[] colorMap = _colorMap; int bytesPerScanline = (context.SourceImageSize.Width + 1) / 2; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); int xOffset = context.SourceReadOffset.X; int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <byte> bitsSpan = sourceSpan.Slice(xOffset >> 1); // xOffset / 2 Span <TiffRgba64> rowDestinationSpan = pixelSpanHandle.GetSpan(); int bitsOffset = xOffset & 1; // xOffset % 2 int sourceIndex = 0; int destinationIndex = 0; int remainingWidth = context.ReadSize.Width; int offset; TiffRgba64 pixel; pixel.A = ushort.MaxValue; if (bitsOffset == 1) { remainingWidth--; offset = bitsSpan[sourceIndex++] & 0xf; pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; rowDestinationSpan[destinationIndex++] = pixel; } for (; (remainingWidth >> 1) > 0; remainingWidth -= 2) // for (; remainingWidth >= 2; remainingWidth -= 2) { int bitsValue = bitsSpan[sourceIndex++]; offset = bitsValue >> 4; pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; rowDestinationSpan[destinationIndex++] = pixel; offset = bitsValue & 0xf; pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; rowDestinationSpan[destinationIndex++] = pixel; } if (remainingWidth != 0) { offset = bitsSpan[sourceIndex++] >> 4; pixel.R = colorMap[offset]; pixel.G = colorMap[SingleColorCount + offset]; pixel.B = colorMap[2 * SingleColorCount + offset]; rowDestinationSpan[destinationIndex++] = pixel; } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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)); } Span <ushort> bitsPerSample = stackalloc ushort[4]; _bitsPerSample.CopyTo(bitsPerSample); bool isHigherOrderBitsFirst = _fillOrder != TiffFillOrder.LowerOrderBitsFirst; int bytesPerScanlineR = (context.SourceImageSize.Width * bitsPerSample[0] + 7) / 8; int bytesPerScanlineG = (context.SourceImageSize.Width * bitsPerSample[1] + 7) / 8; int bytesPerScanlineB = (context.SourceImageSize.Width * bitsPerSample[2] + 7) / 8; int bytesPerScanlineA = (context.SourceImageSize.Width * bitsPerSample[3] + 7) / 8; ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span; ReadOnlySpan <byte> sourceR = sourceSpan.Slice(0, context.SourceImageSize.Height * bytesPerScanlineR); ReadOnlySpan <byte> sourceG = sourceSpan.Slice(sourceR.Length, context.SourceImageSize.Height * bytesPerScanlineG); ReadOnlySpan <byte> sourceB = sourceSpan.Slice(sourceR.Length + sourceG.Length, context.SourceImageSize.Height * bytesPerScanlineB); ReadOnlySpan <byte> sourceA = sourceSpan.Slice(sourceR.Length + sourceG.Length + sourceB.Length, context.SourceImageSize.Height * bytesPerScanlineA); sourceR = sourceR.Slice(context.SourceReadOffset.Y * bytesPerScanlineR); sourceG = sourceG.Slice(context.SourceReadOffset.Y * bytesPerScanlineG); sourceB = sourceB.Slice(context.SourceReadOffset.Y * bytesPerScanlineB); sourceA = sourceB.Slice(context.SourceReadOffset.Y * bytesPerScanlineA); using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); 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 isLittleEndian = context.IsLittleEndian; bool reverseEndiannessR = isLittleEndian && bitsPerSample[0] % 8 == 0; bool reverseEndiannessG = isLittleEndian && bitsPerSample[1] % 8 == 0; bool reverseEndiannessB = isLittleEndian && bitsPerSample[2] % 8 == 0; bool reverseEndiannessA = isLittleEndian && bitsPerSample[3] % 8 == 0; bool canDoFastPath = bitsPerSample[0] >= 16 && bitsPerSample[1] >= 16 && bitsPerSample[2] >= 16 && bitsPerSample[3] >= 16 && !reverseEndiannessR & !reverseEndiannessG & !reverseEndiannessB & !reverseEndiannessA; TiffRgba64 pixel = default; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgba64> pixelSpan = pixelSpanHandle.GetSpan(); var bitReaderR = new BitReader(sourceR.Slice(0, bytesPerScanlineR), isHigherOrderBitsFirst); var bitReaderG = new BitReader(sourceG.Slice(0, bytesPerScanlineG), isHigherOrderBitsFirst); var bitReaderB = new BitReader(sourceB.Slice(0, bytesPerScanlineB), isHigherOrderBitsFirst); var bitReaderA = new BitReader(sourceA.Slice(0, bytesPerScanlineA), isHigherOrderBitsFirst); bitReaderR.Advance(context.SourceReadOffset.X * bitsPerSample[0]); bitReaderG.Advance(context.SourceReadOffset.X * bitsPerSample[1]); bitReaderB.Advance(context.SourceReadOffset.X * bitsPerSample[2]); bitReaderA.Advance(context.SourceReadOffset.X * bitsPerSample[3]); if (canDoFastPath) { // Fast path for bits >= 8 for (int col = 0; col < cols; col++) { pixel.R = (ushort)(FastExpandBits(bitReaderR.Read(bitsPerSample[0]), bitsPerSample[0], 32) >> 16); pixel.G = (ushort)(FastExpandBits(bitReaderG.Read(bitsPerSample[1]), bitsPerSample[1], 32) >> 16); pixel.B = (ushort)(FastExpandBits(bitReaderB.Read(bitsPerSample[2]), bitsPerSample[2], 32) >> 16); pixel.A = (ushort)(FastExpandBits(bitReaderA.Read(bitsPerSample[3]), bitsPerSample[3], 32) >> 16); pixelSpan[col] = pixel; } } else { // Slow path for (int col = 0; col < cols; col++) { pixel.R = (ushort)(ExpandBits(bitReaderR.Read(bitsPerSample[0]), bitsPerSample[0], 32, reverseEndiannessR) >> 16); pixel.G = (ushort)(ExpandBits(bitReaderG.Read(bitsPerSample[1]), bitsPerSample[1], 32, reverseEndiannessG) >> 16); pixel.B = (ushort)(ExpandBits(bitReaderB.Read(bitsPerSample[2]), bitsPerSample[2], 32, reverseEndiannessB) >> 16); pixel.A = (ushort)(ExpandBits(bitReaderA.Read(bitsPerSample[3]), bitsPerSample[3], 32, reverseEndiannessA) >> 16); pixelSpan[col] = pixel; } } if (_isAlphaAssociated) { if (_undoColorPreMultiplying) { UndoColorPreMultiplying(pixelSpan); } else { WipeAlphaChanel(pixelSpan); } } sourceR = sourceR.Slice(bytesPerScanlineR); sourceG = sourceG.Slice(bytesPerScanlineG); sourceB = sourceB.Slice(bytesPerScanlineB); sourceA = sourceA.Slice(bytesPerScanlineA); } return(next.RunAsync(context)); }
/// <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)); } Span <ushort> bitsPerSample = stackalloc ushort[3]; _bitsPerSample.CopyTo(bitsPerSample); bool isHigherOrderBitsFirst = _fillOrder != TiffFillOrder.LowerOrderBitsFirst; bool canDoFastPath = bitsPerSample[0] >= 4 && bitsPerSample[1] >= 4 && bitsPerSample[2] >= 4; int totalBitsPerSample = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; int bytesPerScanline = (context.SourceImageSize.Width * totalBitsPerSample + 7) / 8; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgb24> writer = context.GetWriter <TiffRgb24>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; TiffRgb24 pixel = default; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgb24> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgb24> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * totalBitsPerSample); if (canDoFastPath) { // Fast path for bits >= 8 for (int col = 0; col < cols; col++) { pixel.R = (byte)FastExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 8); pixel.G = (byte)FastExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 8); pixel.B = (byte)FastExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 8); pixelSpan[col] = pixel; } } else { // Slow path for (int col = 0; col < cols; col++) { pixel.R = (byte)ExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 8); pixel.G = (byte)ExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 8); pixel.B = (byte)ExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 8); pixelSpan[col] = pixel; } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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)); } Span <ushort> bitsPerSample = stackalloc ushort[3]; _bitsPerSample.CopyTo(bitsPerSample); bool isHigherOrderBitsFirst = _fillOrder != TiffFillOrder.LowerOrderBitsFirst; int totalBitsPerSample = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; int bytesPerScanline = (context.SourceImageSize.Width * totalBitsPerSample + 7) / 8; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba64> writer = context.GetWriter <TiffRgba64>(); 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 isLittleEndian = context.IsLittleEndian; bool reverseEndiannessR = isLittleEndian && bitsPerSample[0] % 8 == 0; bool reverseEndiannessG = isLittleEndian && bitsPerSample[1] % 8 == 0; bool reverseEndiannessB = isLittleEndian && bitsPerSample[2] % 8 == 0; bool canDoFastPath = bitsPerSample[0] >= 16 && bitsPerSample[1] >= 16 && bitsPerSample[2] >= 16 && !reverseEndiannessR & !reverseEndiannessG & !reverseEndiannessB; TiffRgba64 pixel = default; pixel.A = ushort.MaxValue; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba64> pixelSpanHandle = writer.GetRowSpan(row); Span <TiffRgba64> pixelSpan = pixelSpanHandle.GetSpan(); var bitReader = new BitReader(sourceSpan.Slice(0, bytesPerScanline), isHigherOrderBitsFirst); bitReader.Advance(context.SourceReadOffset.X * totalBitsPerSample); if (canDoFastPath) { // Fast path for bits >= 8 for (int col = 0; col < cols; col++) { pixel.R = (ushort)(FastExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 32) >> 16); pixel.G = (ushort)(FastExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 32) >> 16); pixel.B = (ushort)(FastExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 32) >> 16); pixelSpan[col] = pixel; } } else { // Slow path for (int col = 0; col < cols; col++) { pixel.R = (ushort)(ExpandBits(bitReader.Read(bitsPerSample[0]), bitsPerSample[0], 32, reverseEndiannessR) >> 16); pixel.G = (ushort)(ExpandBits(bitReader.Read(bitsPerSample[1]), bitsPerSample[1], 32, reverseEndiannessG) >> 16); pixel.B = (ushort)(ExpandBits(bitReader.Read(bitsPerSample[2]), bitsPerSample[2], 32, reverseEndiannessB) >> 16); pixelSpan[col] = pixel; } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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 bytesPerScanline = 6 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffBgra64> writer = context.GetWriter <TiffBgra64>(); int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffBgra64> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.Cast <TiffBgra64, byte>(pixelSpanHandle.GetSpan()); CopyScanlineRgbToBgra(sourceSpan.Slice(6 * context.SourceReadOffset.X, 6 * context.ReadSize.Width), rowDestinationSpan.Slice(0, 8 * context.ReadSize.Width), context.ReadSize.Width, context.IsLittleEndian == BitConverter.IsLittleEndian); sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
/// <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 bytesPerScanline = (context.SourceImageSize.Width + 7) / 8; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffGray8> writer = context.GetWriter <TiffGray8>(); int xOffset = context.SourceReadOffset.X; int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <byte> bitsSpan = sourceSpan.Slice(xOffset >> 3); // xOffset / 8 Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); int bitsOffset = xOffset & 7; // xOffset % 8 int sourceIndex = 0; int destinationIndex = 0; int remainingWidth = context.ReadSize.Width; byte bits; if (bitsOffset > 0) { remainingWidth -= 8 - bitsOffset; bits = MaybeReverseBits(bitsSpan[sourceIndex++]); for (; bitsOffset < 8; bitsOffset++) { bool isSet = (bits >> (7 - bitsOffset) & 1) != 0; rowDestinationSpan[destinationIndex++] = isSet ? (byte)0 : (byte)255; } } while (remainingWidth >= 8) { bits = (bitsSpan[sourceIndex++]); bool bit0 = (bits >> 7 & 1) != 0; bool bit1 = (bits >> 6 & 1) != 0; bool bit2 = (bits >> 5 & 1) != 0; bool bit3 = (bits >> 4 & 1) != 0; bool bit4 = (bits >> 3 & 1) != 0; bool bit5 = (bits >> 2 & 1) != 0; bool bit6 = (bits >> 1 & 1) != 0; bool bit7 = (bits & 1) != 0; rowDestinationSpan[destinationIndex++] = bit0 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit1 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit2 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit3 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit4 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit5 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit6 ? (byte)0 : (byte)255; rowDestinationSpan[destinationIndex++] = bit7 ? (byte)0 : (byte)255; remainingWidth -= 8; } if (remainingWidth > 0) { bits = MaybeReverseBits(bitsSpan[sourceIndex++]); for (; remainingWidth > 0; remainingWidth--) { bool isSet = (bits & 0b10000000) != 0; rowDestinationSpan[destinationIndex++] = isSet ? (byte)0 : (byte)255; bits = (byte)(bits << 1); } } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }
private static void ProcessUnassociated(TiffImageDecoderContext context) { int bytesPerScanline = 4 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>(); int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.Cast <TiffRgba32, byte>(pixelSpanHandle.GetSpan()); sourceSpan.Slice(4 * context.SourceReadOffset.X, 4 * context.ReadSize.Width).CopyTo(rowDestinationSpan); sourceSpan = sourceSpan.Slice(bytesPerScanline); } }
/// <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 skippedRowOffset = context.SourceImageSize.Width * context.SourceReadOffset.Y; int planarByteCount = sizeof(ushort) * context.SourceImageSize.Width * context.SourceImageSize.Height; ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span; ReadOnlySpan <byte> sourceR = sourceSpan.Slice(0, planarByteCount); ReadOnlySpan <byte> sourceG = sourceSpan.Slice(planarByteCount, planarByteCount); ReadOnlySpan <byte> sourceB = sourceSpan.Slice(2 * planarByteCount, planarByteCount); using TiffPixelBufferWriter <TiffBgra64> writer = context.GetWriter <TiffBgra64>(); int rows = context.ReadSize.Height; int cols = context.ReadSize.Width; if (context.IsLittleEndian == BitConverter.IsLittleEndian) { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffBgra64> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); int rowOffset = sizeof(ushort) * (skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X); for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + sizeof(ushort) * col; ulong value = 0xffff; value = (value << 16) | (uint)(sourceR[componentOffset + 1] << 8) | sourceR[componentOffset]; // r value = (value << 16) | (uint)(sourceG[componentOffset + 1] << 8) | sourceG[componentOffset]; // g value = (value << 16) | (uint)(sourceB[componentOffset + 1] << 8) | sourceB[componentOffset]; // b BinaryPrimitives.WriteUInt64LittleEndian(rowDestinationSpan, value); rowDestinationSpan = rowDestinationSpan.Slice(8); } } } else { for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffBgra64> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); int rowOffset = sizeof(ushort) * (skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X); for (int col = 0; col < cols; col++) { int componentOffset = rowOffset + sizeof(ushort) * col; ulong value = 0xffff; value = (value << 16) | (uint)(sourceR[componentOffset] << 8) | sourceR[componentOffset + 1]; // r value = (value << 16) | (uint)(sourceG[componentOffset] << 8) | sourceG[componentOffset + 1]; // g value = (value << 16) | (uint)(sourceB[componentOffset] << 8) | sourceB[componentOffset + 1]; // b BinaryPrimitives.WriteUInt64LittleEndian(rowDestinationSpan, value); rowDestinationSpan = rowDestinationSpan.Slice(8); } } } return(next.RunAsync(context)); }
private static void ProcessUnassociated(TiffImageDecoderContext context) { int bytesPerScanline = 8 * context.SourceImageSize.Width; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffBgra64> writer = context.GetWriter <TiffBgra64>(); int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffBgra64> pixelSpanHandle = writer.GetRowSpan(row); Span <byte> rowDestinationSpan = MemoryMarshal.Cast <TiffBgra64, byte>(pixelSpanHandle.GetSpan()); CopyScanlineRgbaToBgra(sourceSpan.Slice(8 * context.SourceReadOffset.X, 8 * context.ReadSize.Width), rowDestinationSpan, context.ReadSize.Width, context.IsLittleEndian == BitConverter.IsLittleEndian); sourceSpan = sourceSpan.Slice(bytesPerScanline); } }
/// <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 bytesPerScanline = (context.SourceImageSize.Width + 1) / 2; Memory <byte> source = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline); ReadOnlySpan <byte> sourceSpan = source.Span; using TiffPixelBufferWriter <TiffGray8> writer = context.GetWriter <TiffGray8>(); int xOffset = context.SourceReadOffset.X; int rows = context.ReadSize.Height; for (int row = 0; row < rows; row++) { using TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(row); ReadOnlySpan <byte> bitsSpan = sourceSpan.Slice(xOffset >> 1); // xOffset / 2 Span <byte> rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan()); int bitsOffset = xOffset & 1; // xOffset % 2 int sourceIndex = 0; int destinationIndex = 0; int remainingWidth = context.ReadSize.Width; byte bits; if (bitsOffset > 0) { remainingWidth--; bits = bitsSpan[sourceIndex++]; bits = (byte)(bits & 0xf); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits); } // manual loop unrolling for (; (remainingWidth >> 4) > 0; remainingWidth -= 16) // for (; remainingWidth >= 16; remainingWidth -= 16) { bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); } for (; (remainingWidth >> 1) > 0; remainingWidth -= 2) // for (; remainingWidth >= 2; remainingWidth -= 2) { bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); rowDestinationSpan[destinationIndex++] = (byte)(bits << 4 | bits & 0xf); } if (remainingWidth != 0) { bits = bitsSpan[sourceIndex++]; rowDestinationSpan[destinationIndex++] = (byte)(bits & 0xf0 | bits >> 4); } sourceSpan = sourceSpan.Slice(bytesPerScanline); } return(next.RunAsync(context)); }