/// <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;
            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;

            for (int row = 0; row < rows; row++)
            {
                using TiffPixelSpanHandle <TiffGray8> pixelSpanHandle = writer.GetRowSpan(row);
                ReadOnlySpan <byte> rowSourceSpan      = sourceSpan.Slice(context.SourceReadOffset.X, context.ReadSize.Width);
                Span <byte>         rowDestinationSpan = MemoryMarshal.AsBytes(pixelSpanHandle.GetSpan());
                InvertCopy(rowSourceSpan, rowDestinationSpan);
                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));
            }

            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));
        }
Exemple #3
0
        public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next)
        {
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;
            TiffSize          imageSize  = context.ImageSize;
            int arraySize = imageSize.Width * imageSize.Height;

            using (IMemoryOwner <byte> pixelData = memoryPool.Rent(arraySize))
            {
                using (var writer = new TiffMemoryPixelBufferWriter <TiffGray8>(memoryPool, pixelData.Memory, imageSize.Width, imageSize.Height))
                    using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter()))
                    {
                        await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false);
                    }

                context.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
                context.BitsPerSample             = TiffValueCollection.Single <ushort>(8);
                context.UncompressedData          = pixelData.Memory.Slice(0, arraySize);

                await next.RunAsync(context).ConfigureAwait(false);

                context.UncompressedData = default;
            }

            TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter;

            if (!(ifdWriter is null))
            {
                using (await context.LockAsync().ConfigureAwait(false))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)context.PhotometricInterpretation)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, context.BitsPerSample).ConfigureAwait(false);
                }
            }
        }
        /// <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));
        }
        public void TestCrop()
        {
            ITiffPixelBuffer <TiffGray8> pixelBuffer = PixelBufferTests.InitializePixelBuffer(out TiffGray8[] pixels);
            var writer = new TiffPixelBufferWriterAdapter <TiffGray8>(pixelBuffer);

            TiffPixelBufferWriter <TiffGray8> structWriter = writer.AsPixelBufferWriter();

            Assert.Equal(3, structWriter.Width);
            Assert.Equal(2, structWriter.Height);
            Assert.False(structWriter.IsEmpty);

            Assert.Throws <ArgumentOutOfRangeException>("offset", () => structWriter.Crop(new TiffPoint(4, 0)));
            Assert.Throws <ArgumentOutOfRangeException>("offset", () => structWriter.Crop(new TiffPoint(0, 3)));
            Assert.Throws <ArgumentOutOfRangeException>("offset", () => structWriter.Crop(new TiffPoint(4, 3)));

            Assert.Throws <ArgumentOutOfRangeException>("size", () => structWriter.Crop(new TiffPoint(0, 0), new TiffSize(4, 1)));
            Assert.Throws <ArgumentOutOfRangeException>("size", () => structWriter.Crop(new TiffPoint(0, 0), new TiffSize(1, 3)));
            Assert.Throws <ArgumentOutOfRangeException>("size", () => structWriter.Crop(new TiffPoint(0, 0), new TiffSize(4, 3)));

            structWriter = writer.Crop(new TiffPoint(1, 1), new TiffSize(1, 1));
            Assert.Equal(1, structWriter.Width);
            Assert.Equal(1, structWriter.Height);

            structWriter = writer.AsPixelBufferWriter().Crop(new TiffPoint(1, 1), new TiffSize(1, 1));
            Assert.Equal(1, structWriter.Width);
            Assert.Equal(1, structWriter.Height);

            structWriter = writer.Crop(new TiffPoint(1, 1));
            Assert.Equal(2, structWriter.Width);
            Assert.Equal(1, structWriter.Height);

            structWriter = writer.AsPixelBufferWriter().Crop(new TiffPoint(1, 1));
            Assert.Equal(2, structWriter.Width);
            Assert.Equal(1, structWriter.Height);

            Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => structWriter.GetRowSpan(-1, 0, 1));
            Assert.Throws <ArgumentOutOfRangeException>("rowIndex", () => structWriter.GetRowSpan(2, 0, 1));
            Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetRowSpan(0, -1, 1));
            Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetRowSpan(0, 3, 1));
            Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetRowSpan(0, 1, -1));
            Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetRowSpan(0, 1, 2));

            Assert.Throws <ArgumentOutOfRangeException>("colIndex", () => structWriter.GetColumnSpan(-1, 0, 1));
            Assert.Throws <ArgumentOutOfRangeException>("colIndex", () => structWriter.GetColumnSpan(2, 0, 1));
            Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetColumnSpan(0, -1, 1));
            Assert.Throws <ArgumentOutOfRangeException>("start", () => structWriter.GetColumnSpan(0, 2, 1));
            Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetColumnSpan(0, 1, -1));
            Assert.Throws <ArgumentOutOfRangeException>("length", () => structWriter.GetColumnSpan(0, 1, 2));

            ITiffPixelBufferWriter <TiffGray8> writer2 = TiffPixelBufferUnsafeMarshal.GetBuffer(structWriter, out TiffPoint offset, out TiffSize size);

            Assert.True(ReferenceEquals(writer, writer2));
            Assert.Equal(1, offset.X);
            Assert.Equal(1, offset.Y);
            Assert.Equal(2, size.Width);
            Assert.Equal(1, size.Height);
        }
        /// <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));
        }
Exemple #8
0
        public override TiffPixelBufferWriter <TPixel> GetWriter <TPixel>()
        {
            TiffPixelBufferWriter <TPixel> writer = InnerContext.GetWriter <TPixel>();

            if (_isFlipOrOrientation)
            {
                return(new TiffOrientedPixelBufferWriter <TPixel>(writer, _flipLeftRigt, _flipTopBottom).AsPixelBufferWriter());
            }
            return(new TiffFlippedPixelBufferWriter <TPixel>(writer, _flipLeftRigt, _flipTopBottom).AsPixelBufferWriter());
        }
        /// <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));
        }
Exemple #10
0
        /// <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));
        }
 public ValueTask ReadAsync(TiffPoint offset, TiffPixelBufferWriter <TPixel> destination, CancellationToken cancellationToken)
 {
     if (_flipLeftRight)
     {
         offset = new TiffPoint(Width - offset.X - destination.Width, offset.Y);
     }
     if (_flipTopBottom)
     {
         offset = new TiffPoint(offset.X, Height - offset.Y - destination.Height);
     }
     destination = new TiffFlippedPixelBufferWriter <TPixel>(destination, _flipLeftRight, _flipTopBottom).AsPixelBufferWriter();
     return(_reader.ReadAsync(offset, destination, cancellationToken));
 }
        /// <summary>
        /// Converts pixel buffer writer of any pixel format <typeparamref name="TBuffer"/> into <see cref="TiffPixelBufferWriter{TPixel}"/>.
        /// </summary>
        /// <typeparam name="TBuffer">The specified pixel type.</typeparam>
        /// <param name="writer">The writer to be converted.</param>
        /// <returns>The converted writer.</returns>
        public override TiffPixelBufferWriter <TPixel> ConvertWriter <TBuffer>(TiffPixelBufferWriter <TBuffer> writer)
        {
            ITiffPixelConverterFactory?pixelConverterFactory = PixelConverterFactory;

            if (pixelConverterFactory is null)
            {
                throw new InvalidOperationException("Failed to acquire PixelConverterFactory");
            }

            ITiffPixelBufferWriter <TBuffer> innerWriter = TiffPixelBufferUnsafeMarshal.GetBuffer(writer, out TiffPoint offset, out TiffSize size);
            ITiffPixelBufferWriter <TPixel>  converted   = pixelConverterFactory.CreateConverter <TPixel, TBuffer>(innerWriter);

            return(converted.Crop(offset, size));
        }
Exemple #13
0
        /// <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));
        }
Exemple #15
0
        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);
            }
        }
Exemple #16
0
        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);
            }
        }
Exemple #17
0
        public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next)
        {
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;
            TiffSize          imageSize  = context.ImageSize;
            int arraySize = 3 * imageSize.Width * imageSize.Height;

            using (IMemoryOwner <byte> pixelData = memoryPool.Rent(arraySize))
            {
                Memory <byte> pixelDataMemory = pixelData.Memory;
                using (var writer = new TiffMemoryPixelBufferWriter <TiffRgb24>(memoryPool, pixelDataMemory, imageSize.Width, imageSize.Height))
                    using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter()))
                    {
                        await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false);
                    }

                TiffYCbCrConverter8.CreateDefault().ConvertFromRgb24(MemoryMarshal.Cast <byte, TiffRgb24>(pixelDataMemory.Span), pixelDataMemory.Span, imageSize.Width * imageSize.Height);

                context.PhotometricInterpretation = TiffPhotometricInterpretation.YCbCr;
                context.BitsPerSample             = TiffValueCollection.UnsafeWrap(s_bitsPerSample);
                context.UncompressedData          = pixelDataMemory.Slice(0, arraySize);

                await next.RunAsync(context).ConfigureAwait(false);

                context.UncompressedData = default;
            }

            TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter;

            if (!(ifdWriter is null))
            {
                using (await context.LockAsync().ConfigureAwait(false))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)context.PhotometricInterpretation)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, context.BitsPerSample).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, TiffValueCollection.Single <ushort>(3)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.YCbCrCoefficients, TiffYCbCrConverter8.DefaultLuma).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ReferenceBlackWhite, TiffYCbCrConverter8.DefaultReferenceBlackWhite).ConfigureAwait(false);
                }
            }
        }
Exemple #18
0
        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));
        }
        /// <inheritdoc />
        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));
            }

            int width  = Math.Min(_size.Width - offset.X, destination.Width);
            int height = Math.Min(_size.Height - offset.Y, destination.Height);

            ReadOnlySpan <TPixel> buffer = _buffer.GetReadOnlySpan();
            int bufferWidth = _size.Width;

            for (int row = 0; row < height; row++)
            {
                ReadOnlySpan <TPixel> sourceSpan = buffer.Slice(bufferWidth * (offset.Y + row) + offset.X, width);
                using TiffPixelSpanHandle <TPixel> destinationHandle = destination.GetRowSpan(row);
                sourceSpan.CopyTo(destinationHandle.GetSpan());
            }

            return(default);
Exemple #21
0
        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);
Exemple #22
0
        public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next)
        {
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;
            TiffSize          imageSize  = context.ImageSize;

            using (IMemoryOwner <byte> pixelData = memoryPool.Rent(imageSize.Width * imageSize.Height))
            {
                Memory <byte> pixelDataMemory = pixelData.Memory;
                using (var writer = new TiffMemoryPixelBufferWriter <TiffMask>(memoryPool, pixelDataMemory, imageSize.Width, imageSize.Height))
                    using (TiffPixelBufferWriter <TPixel> convertedWriter = context.ConvertWriter(writer.AsPixelBufferWriter()))
                    {
                        await context.GetReader().ReadAsync(convertedWriter, context.CancellationToken).ConfigureAwait(false);
                    }

                int count = PackBytesIntoBits(pixelDataMemory.Span, imageSize, _threshold);

                context.PhotometricInterpretation = TiffPhotometricInterpretation.TransparencyMask;
                context.BitsPerSample             = TiffValueCollection.Single <ushort>(1);
                context.UncompressedData          = pixelData.Memory.Slice(0, count);

                await next.RunAsync(context).ConfigureAwait(false);

                context.UncompressedData = default;
            }

            TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter;

            if (!(ifdWriter is null))
            {
                using (await context.LockAsync().ConfigureAwait(false))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, TiffValueCollection.Single((ushort)context.PhotometricInterpretation)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, context.BitsPerSample).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.FillOrder, TiffValueCollection.Single((ushort)TiffFillOrder.HigherOrderBitsFirst)).ConfigureAwait(false);
                }
            }
        }
Exemple #23
0
        private static void ProcessAssociatedPreservingColorPreMultiplying(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 <TiffRgb24> writer = context.GetWriter <TiffRgb24>();

            int rows = context.ReadSize.Height;
            int cols = context.ReadSize.Width;

            for (int row = 0; row < rows; row++)
            {
                using TiffPixelSpanHandle <TiffRgb24> pixelSpanHandle = writer.GetRowSpan(row);
                ReadOnlySpan <TiffRgba32> rowSourceSpan      = MemoryMarshal.Cast <byte, TiffRgba32>(sourceSpan.Slice(4 * context.SourceReadOffset.X, 4 * context.ReadSize.Width));
                Span <TiffRgb24>          rowDestinationSpan = pixelSpanHandle.GetSpan();

                for (int col = 0; col < cols; col++)
                {
                    TiffRgba32 pixel = rowSourceSpan[col];
                    TiffRgb24  pixel24;
                    byte       a = pixel.A;
                    if (a == 0)
                    {
                        pixel24 = default;
                    }
                    else
                    {
                        pixel24 = new TiffRgb24((byte)(pixel.R * 255 / a), (byte)(pixel.G * 255 / a), (byte)(pixel.B * 255 / a));
                    }
                    rowDestinationSpan[col] = pixel24;
                }

                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));
            }

            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 = (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));
        }
 public override TiffPixelBufferWriter <TPixel> ConvertWriter <TBuffer>(TiffPixelBufferWriter <TBuffer> writer)
 {
     throw new NotSupportedException();
 }
Exemple #28
0
        /// <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));
        }
 /// <summary>
 /// Converts pixel buffer writer of any pixel format <typeparamref name="TBuffer"/> into <see cref="TiffPixelBufferWriter{TPixel}"/>.
 /// </summary>
 /// <typeparam name="TBuffer">The specified pixel type.</typeparam>
 /// <param name="writer">The writer to be converted.</param>
 /// <returns>The converted writer.</returns>
 public abstract TiffPixelBufferWriter <TPixel> ConvertWriter <TBuffer>(TiffPixelBufferWriter <TBuffer> writer) where TBuffer : unmanaged;
Exemple #30
0
        /// <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));
        }