/// <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 horizontalSubsampling = _horizontalSubsampling;
            int verticalSubsampling   = _verticalSubsampling;

            if (horizontalSubsampling == 1 && verticalSubsampling == 1)
            {
                return(next.RunAsync(context));
            }

            if (_isPlanar)
            {
                ProcessPlanarData(context);
            }
            else
            {
                ProcessChunkyData(context);
            }

            return(next.RunAsync(context));
        }
Exemplo n.º 2
0
        /// <inheritdoc />
        public override void Decode <TPixel>(TiffPoint offset, TiffSize readSize, TiffPoint destinationOffset, ITiffPixelBufferWriter <TPixel> writer)
        {
            if (writer is null)
            {
                throw new ArgumentNullException(nameof(writer));
            }
            readSize = new TiffSize(Math.Max(0, Math.Min(writer.Width - destinationOffset.X, readSize.Width)), Math.Max(0, Math.Min(writer.Height - destinationOffset.Y, readSize.Height)));
            readSize = new TiffSize(Math.Max(0, Math.Min(Width - offset.X, readSize.Width)), Math.Max(0, Math.Min(Height - offset.Y, readSize.Height)));
            if (readSize.IsAreaEmpty)
            {
                return;
            }

            if (_parameters.ContentSource is null)
            {
                throw new InvalidOperationException("Failed to acquire ContentSource.");
            }

            using TiffFileContentReader reader = TiffSyncFileContentSource.WrapReader(_parameters.ContentSource.OpenReader());
            var context = new TiffDefaultImageDecoderContext <TPixel>()
            {
                MemoryPool            = _parameters.MemoryPool ?? MemoryPool <byte> .Shared,
                CancellationToken     = CancellationToken.None,
                OperationContext      = _parameters.OperationContext,
                ContentReader         = reader,
                SourceImageSize       = _parameters.ImageSize,
                SourceReadOffset      = offset,
                ReadSize              = readSize,
                PixelConverterFactory = _parameters.PixelConverterFactory ?? TiffDefaultPixelConverterFactory.Instance,
                DestinationWriter     = new TiffPixelBufferWriter <TPixel>(TiffNoopDisposablePixelBufferWriter.Wrap(writer)).Crop(destinationOffset, readSize)
            };

            _pipeline.RunAsync(context).AsTask().GetAwaiter().GetResult();
        }
Exemplo n.º 3
0
        public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            TiffParallelDecodingState?state = context.GetService(typeof(TiffParallelDecodingState)) as TiffParallelDecodingState;

            if (state is null)
            {
                return(next.RunAsync(context));
            }

            return(new ValueTask(state.DispatchAsync(() => next.RunAsync(context), context.CancellationToken)));
        }
        /// <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));
            }

            if (_predictor == TiffPredictor.None)
            {
                return(next.RunAsync(context));
            }
            if (_predictor != TiffPredictor.HorizontalDifferencing)
            {
                throw new NotSupportedException("Predictor not supportted.");
            }

            int  skipped          = 0;
            bool isMultiplePlanar = _bytesPerScanlines.Count > 1;

            for (int planarIndex = 0; planarIndex < _bytesPerScanlines.Count; planarIndex++)
            {
                int bytesPerScanline = _bytesPerScanlines[planarIndex];

                // Current plane buffer
                Span <byte> plane = context.UncompressedData.Span.Slice(skipped, bytesPerScanline * context.SourceImageSize.Height);

                // Skip scanlines that are not to be decoded
                plane = plane.Slice(bytesPerScanline * context.SourceReadOffset.Y);

                TiffValueCollection <ushort> bitsPerSample = isMultiplePlanar ? TiffValueCollection.Single(_bitsPerSample[planarIndex]) : _bitsPerSample;

                for (int row = 0; row < context.ReadSize.Height; row++)
                {
                    // Process every scanline
                    Span <byte> scanline = plane.Slice(row * bytesPerScanline, bytesPerScanline);
                    UndoHorizontalDifferencingForScanline(scanline, bitsPerSample, context.SourceImageSize.Width);
                }

                skipped += bytesPerScanline * context.SourceImageSize.Height;
            }

            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));
        }
        /// <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 = 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));
        }
Exemplo n.º 8
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));
            }

            if (!_isAlphaAssociated)
            {
                ProcessUnassociated(context);
            }
            else if (_undoColorPreMultiplying)
            {
                ProcessAssociatedWithUndoColorPreMultiplying(context);
            }
            else
            {
                ProcessAssociatedPreservingColorPreMultiplying(context);
            }

            return(next.RunAsync(context));
        }
Exemplo n.º 9
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  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));
        }
        /// <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));
            }

            if (_orientation == 0 || _orientation == TiffOrientation.TopLeft)
            {
                return(next.RunAsync(context));
            }

            return(next.RunAsync(new TiffOrientatedImageDecoderContext(context, _orientation)));
        }
        /// <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));
        }
Exemplo n.º 15
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));
        }
Exemplo n.º 16
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));
        }
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            using var state = new TiffParallelDecodingState(_maxDegreeOfParallelism);

            using var mutexService = new ParallelMutexService();

            context.RegisterService(typeof(TiffParallelDecodingState), state);
            context.RegisterService(typeof(ITiffParallelMutexService), mutexService);

            state.LockTaskCompletion();

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

            state.ReleaseTaskCompletion();

            await state.Complete !.Task.ConfigureAwait(false);

            context.RegisterService(typeof(TiffParallelDecodingState), null);
            context.RegisterService(typeof(ITiffParallelMutexService), null);
        }
        /// <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));
        }
Exemplo n.º 20
0
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            IDisposable?lockObj      = null;
            var         mutexService = context.GetService(typeof(ITiffParallelMutexService)) as ITiffParallelMutexService;

            if (!(mutexService is null))
            {
                lockObj = await mutexService.LockAsync(context.CancellationToken).ConfigureAwait(false);
            }

            try
            {
                await next.RunAsync(context).ConfigureAwait(false);
            }
            finally
            {
                if (!(lockObj is null))
                {
                    lockObj.Dispose();
                }
            }
        }
        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;
            Span <ushort> uncompressedData = MemoryMarshal.Cast <byte, ushort>(context.UncompressedData.Span);

            for (int i = 0; i < uncompressedData.Length; i++)
            {
                uncompressedData[i] = (ushort)FastExpandBits(uncompressedData[i], bitCount);
            }

            return(next.RunAsync(new JpegDataEndianContextWrapper(context)));
        }
Exemplo n.º 22
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));
        }
        /// <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));
        }
Exemplo n.º 25
0
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            MemoryPool <byte>     memoryPool    = context.MemoryPool ?? MemoryPool <byte> .Shared;
            TiffFileContentReader contentReader = context.ContentReader ?? throw new InvalidOperationException();

            IMemoryOwner <byte>?dataHandle = null;
            Memory <byte>       data;

            try
            {
                const int BufferSize = 65536;
                using (var bufferWriter = new MemoryPoolBufferWriter(memoryPool))
                {
                    // Read JPEG stream
                    TiffStreamRegion streamRegion = _streamRegion;
                    do
                    {
                        int           readSize = Math.Min(streamRegion.Length, BufferSize);
                        Memory <byte> memory   = bufferWriter.GetMemory(readSize);
                        memory   = memory.Slice(0, Math.Min(streamRegion.Length, memory.Length));
                        readSize = await contentReader.ReadAsync(streamRegion.Offset, memory, context.CancellationToken).ConfigureAwait(false);

                        bufferWriter.Advance(readSize);
                        streamRegion = new TiffStreamRegion(streamRegion.Offset + readSize, streamRegion.Length - readSize);
                    } while (streamRegion.Length > 0);

                    // Identify the image
                    var decoder = new JpegDecoder();
                    decoder.MemoryPool = memoryPool;
                    decoder.SetInput(bufferWriter.GetReadOnlySequence());
                    decoder.Identify();
                    if (decoder.Width != context.SourceImageSize.Width || decoder.Height != context.SourceImageSize.Height)
                    {
                        throw new InvalidOperationException("The image size does not match.");
                    }
                    if (decoder.Precision != 8)
                    {
                        throw new InvalidOperationException("Only 8-bit JPEG is supported.");
                    }

                    // Adjust buffer size and reading region to reduce buffer size
                    int skippedWidth  = context.SourceReadOffset.X / 8 * 8;
                    int skippedHeight = context.SourceReadOffset.Y / 8 * 8;
                    context.SourceReadOffset = new TiffPoint(context.SourceReadOffset.X % 8, context.SourceReadOffset.Y % 8);
                    int bufferWidth  = context.SourceReadOffset.X + context.ReadSize.Width;
                    int bufferHeight = context.SourceReadOffset.Y + context.ReadSize.Height;
                    context.SourceImageSize = new TiffSize(bufferWidth, bufferHeight);

                    // Allocate buffer and decode image
                    int dataSize = bufferWidth * bufferHeight * decoder.NumberOfComponents;
                    dataHandle = memoryPool.Rent(dataSize);
                    data       = dataHandle.Memory.Slice(0, dataSize);
                    decoder.SetOutputWriter(new LegacyJpegBufferOutputWriter(skippedWidth, bufferWidth, skippedHeight, bufferHeight, decoder.NumberOfComponents, data));
                    decoder.Decode();
                }

                // Pass the buffer to the next middleware
                context.UncompressedData = data;
                await next.RunAsync(context).ConfigureAwait(false);

                context.UncompressedData = null;
            }
            finally
            {
                dataHandle?.Dispose();
            }
        }
Exemplo n.º 26
0
        /// <inheritdoc />
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next is null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            if (context.ContentReader is null)
            {
                throw new ArgumentException("ContentReader is not provided in the TiffImageDecoderContext instance.");
            }
            if (context.OperationContext is null)
            {
                throw new ArgumentException("OperationContext is not provided in the TiffImageDecoderContext instance.");
            }

            bool isParallel = !(context.GetService(typeof(TiffParallelDecodingState)) is null);

            // Initialize the cache
            TiffFieldReader?      reader = null;
            TiffStrileOffsetCache cache;

            if (_lazyLoad)
            {
                reader = new TiffFieldReader(context.ContentReader, context.OperationContext, leaveOpen: true);
                cache  = new TiffStrileOffsetCache(reader, _tileOffsetsEntry, _tileByteCountsEntry, CacheSize);
            }
            else
            {
                cache = new TiffStrileOffsetCache(_tileOffsets, _tileByteCounts);
            }

            int tileWidth  = _tileWidth;
            int tileHeight = _tileHeight;

            try
            {
                int tileAcross = (context.SourceImageSize.Width + tileWidth - 1) / tileWidth;
                int tileDown   = (context.SourceImageSize.Height + tileHeight - 1) / tileHeight;
                int tileCount  = tileAcross * tileDown;
                CancellationToken cancellationToken = context.CancellationToken;

                // Make sure the region to read is in the image boundary.
                if (context.SourceReadOffset.X >= context.SourceImageSize.Width || context.SourceReadOffset.Y >= context.SourceImageSize.Height)
                {
                    return;
                }
                context.ReadSize = new TiffSize(Math.Min(context.ReadSize.Width, context.SourceImageSize.Width - context.SourceReadOffset.X), Math.Min(context.ReadSize.Height, context.SourceImageSize.Height - context.SourceReadOffset.Y));
                if (context.ReadSize.IsAreaEmpty)
                {
                    return;
                }

                int planeCount = _planaCount;
                TiffImageEnumeratorDecoderContext?            wrapperContext = null;
                TiffMutableValueCollection <TiffStreamRegion> planarRegions  = default;
                if (!isParallel)
                {
                    wrapperContext = new TiffImageEnumeratorDecoderContext(context);
                    planarRegions  = new TiffMutableValueCollection <TiffStreamRegion>(planeCount);
                }

                // loop through all the tiles overlapping with the region to read.
                int colStart = context.SourceReadOffset.X / tileWidth;
                int colEnd   = (context.SourceReadOffset.X + context.ReadSize.Width + tileWidth - 1) / tileWidth;
                int rowStart = context.SourceReadOffset.Y / tileHeight;
                int rowEnd   = (context.SourceReadOffset.Y + context.ReadSize.Height + tileHeight - 1) / tileHeight;

                for (int row = rowStart; row < rowEnd; row++)
                {
                    // Calculate coordinates on the y direction for the tiles on this row.
                    int currentYOffset     = row * tileHeight;
                    int skippedScanlines   = Math.Max(0, context.SourceReadOffset.Y - currentYOffset);
                    int requestedScanlines = Math.Min(tileHeight - skippedScanlines, context.SourceReadOffset.Y + context.ReadSize.Height - currentYOffset - skippedScanlines);

                    for (int col = colStart; col < colEnd; col++)
                    {
                        if (isParallel)
                        {
                            wrapperContext = new TiffImageEnumeratorDecoderContext(context);
                            planarRegions  = new TiffMutableValueCollection <TiffStreamRegion>(planeCount);
                        }

                        wrapperContext !.SourceImageSize = new TiffSize(tileWidth, tileHeight);

                        // Calculate coordinates on the y direction for this tile.
                        int currentXOffset = col * tileWidth;
                        int skippedXOffset = Math.Max(0, context.SourceReadOffset.X - currentXOffset);
                        int requestedWidth = Math.Min(tileWidth - skippedXOffset, context.SourceReadOffset.X + context.ReadSize.Width - currentXOffset - skippedXOffset);

                        // Update size info of this tile
                        wrapperContext.SourceReadOffset = new TiffPoint(skippedXOffset, skippedScanlines);
                        wrapperContext.ReadSize         = new TiffSize(requestedWidth, requestedScanlines);

                        // Update size info of the destination buffer
                        wrapperContext.SetCropSize(new TiffPoint(Math.Max(0, currentXOffset - context.SourceReadOffset.X), Math.Max(0, currentYOffset - context.SourceReadOffset.Y)), context.ReadSize);

                        // Check to see if there is any region area to be read
                        if (wrapperContext.ReadSize.IsAreaEmpty)
                        {
                            continue;
                        }

                        // Prepare stream regions of this tile
                        for (int planarIndex = 0; planarIndex < planeCount; planarIndex++)
                        {
                            int tileIndex = planarIndex * tileCount + row * tileAcross + col;
                            planarRegions[planarIndex] = await cache.GetOffsetAndCountAsync(tileIndex, cancellationToken).ConfigureAwait(false);
                        }
                        wrapperContext.PlanarRegions = planarRegions.GetReadOnlyView();

                        cancellationToken.ThrowIfCancellationRequested();

                        // Pass down the data
                        await next.RunAsync(wrapperContext).ConfigureAwait(false);
                    }
                }
            }
            finally
            {
                ((IDisposable)cache).Dispose();
                reader?.Dispose();
            }
        }
        /// <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));
        }
        /// <inheritdoc />
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next is null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            if (_bytesPerScanlines.Count != context.PlanarRegions.Count)
            {
                throw new InvalidOperationException();
            }

            int planeCount = _bytesPerScanlines.Count;

            // calculate the uncompressed data buffer length
            int uncompressedDataLength = 0;
            int imageHeight            = context.SourceImageSize.Height;

            foreach (int bytesPerScanline in _bytesPerScanlines)
            {
                uncompressedDataLength += bytesPerScanline * imageHeight;
            }

            TiffEmptyStrileWriter?emptyWriter = null;

            // calculate the maximum buffer needed to read from stream
            int readCount = 0;

            foreach (TiffStreamRegion planarRegion in context.PlanarRegions)
            {
                int length = planarRegion.Length;
                if (length == 0)
                {
                    emptyWriter = new TiffEmptyStrileWriter(new TiffRgba32(255, 255, 255, 0));
                    break;
                }
                if (length > readCount)
                {
                    readCount = planarRegion.Length;
                }
            }

            if (!(emptyWriter is null))
            {
                emptyWriter.Write(context);
                return;
            }

            // allocate the raw data buffer and the uncompressed data buffer
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;

            using IMemoryOwner <byte> bufferMemory = memoryPool.Rent(uncompressedDataLength);
            int planarUncompressedByteCount        = 0;
            TiffFileContentReader?reader           = context.ContentReader;

            if (reader is null)
            {
                throw new InvalidOperationException("Failed to acquire ContentReader.");
            }

            using (IMemoryOwner <byte> rawBuffer = memoryPool.Rent(readCount))
            {
                TiffDecompressionContext decompressionContext = new TiffDecompressionContext();

                // decompress each plane
                for (int i = 0; i < planeCount; i++)
                {
                    TiffStreamRegion region = context.PlanarRegions[i];

                    // Read from stream
                    readCount = await reader.ReadAsync(region.Offset, rawBuffer.Memory.Slice(0, region.Length), context.CancellationToken).ConfigureAwait(false);

                    if (readCount != region.Length)
                    {
                        throw new InvalidDataException();
                    }

                    // Decompress this plane
                    int bytesPerScanline = _bytesPerScanlines[i];
                    decompressionContext.MemoryPool = memoryPool;
                    decompressionContext.PhotometricInterpretation = _photometricInterpretation;
                    decompressionContext.BitsPerSample             = _bitsPerSample;
                    decompressionContext.ImageSize          = context.SourceImageSize;
                    decompressionContext.BytesPerScanline   = bytesPerScanline;
                    decompressionContext.SkippedScanlines   = context.SourceReadOffset.Y;
                    decompressionContext.RequestedScanlines = context.ReadSize.Height;
                    _decompressionAlgorithm.Decompress(decompressionContext, rawBuffer.Memory.Slice(0, readCount), bufferMemory.Memory.Slice(planarUncompressedByteCount, bytesPerScanline * imageHeight));
                    planarUncompressedByteCount += bytesPerScanline * imageHeight;
                }
            }

            // Pass down the data
            context.UncompressedData = bufferMemory.Memory.Slice(0, uncompressedDataLength);
            await next.RunAsync(context).ConfigureAwait(false);

            context.UncompressedData = default;
        }
Exemplo n.º 29
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));
        }
        /// <inheritdoc />
        public async ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next is null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            if (context.ContentReader is null)
            {
                throw new ArgumentException("ContentReader is not provided in the TiffImageDecoderContext instance.");
            }
            if (context.OperationContext is null)
            {
                throw new ArgumentException("OperationContext is not provided in the TiffImageDecoderContext instance.");
            }

            bool isParallel = !(context.GetService(typeof(TiffParallelDecodingState)) is null);

            // Initialize the cache
            TiffFieldReader?      reader = null;
            TiffStrileOffsetCache cache;

            if (_lazyLoad)
            {
                reader = new TiffFieldReader(context.ContentReader, context.OperationContext, leaveOpen: true);
                cache  = new TiffStrileOffsetCache(reader, _stripOffsetsEntry, _stripsByteCountEntry, CacheSize);
            }
            else
            {
                cache = new TiffStrileOffsetCache(_stripOffsets, _stripsByteCount);
            }

            try
            {
                int rowsPerStrip = _rowsPerStrip;
                CancellationToken cancellationToken = context.CancellationToken;

                // Special case for mailformed file.
                if (rowsPerStrip <= 0 && _stripCount == 1)
                {
                    rowsPerStrip = context.SourceImageSize.Height;
                }

                // Make sure the region to read is in the image boundary.
                if (context.SourceReadOffset.X >= context.SourceImageSize.Width || context.SourceReadOffset.Y >= context.SourceImageSize.Height)
                {
                    return;
                }
                context.ReadSize = new TiffSize(Math.Min(context.ReadSize.Width, context.SourceImageSize.Width - context.SourceReadOffset.X), Math.Min(context.ReadSize.Height, context.SourceImageSize.Height - context.SourceReadOffset.Y));
                if (context.ReadSize.IsAreaEmpty)
                {
                    return;
                }

                // Create a wrapped context
                int planeCount = _planeCount;
                TiffImageEnumeratorDecoderContext?            wrapperContext = null;
                TiffMutableValueCollection <TiffStreamRegion> planarRegions  = default;
                if (!isParallel)
                {
                    wrapperContext = new TiffImageEnumeratorDecoderContext(context);
                    planarRegions  = new TiffMutableValueCollection <TiffStreamRegion>(planeCount);
                }

                // loop through all the strips overlapping with the region to read
                int stripStart       = context.SourceReadOffset.Y / rowsPerStrip;
                int stripEnd         = (context.SourceReadOffset.Y + context.ReadSize.Height + rowsPerStrip - 1) / rowsPerStrip;
                int actualStripCount = _stripCount / _planeCount;
                for (int stripIndex = stripStart; stripIndex < stripEnd; stripIndex++)
                {
                    if (isParallel)
                    {
                        wrapperContext = new TiffImageEnumeratorDecoderContext(context);
                        planarRegions  = new TiffMutableValueCollection <TiffStreamRegion>(planeCount);
                    }

                    // Calculate size info of this strip
                    int currentYOffset     = stripIndex * rowsPerStrip;
                    int stripImageHeight   = Math.Min(rowsPerStrip, context.SourceImageSize.Height - currentYOffset);
                    int skippedScanlines   = Math.Max(0, context.SourceReadOffset.Y - currentYOffset);
                    int requestedScanlines = Math.Min(stripImageHeight - skippedScanlines, context.SourceReadOffset.Y + context.ReadSize.Height - currentYOffset - skippedScanlines);
                    wrapperContext !.SourceImageSize = new TiffSize(context.SourceImageSize.Width, stripImageHeight);
                    wrapperContext.SourceReadOffset  = new TiffPoint(context.SourceReadOffset.X, skippedScanlines);
                    wrapperContext.ReadSize          = new TiffSize(context.ReadSize.Width, requestedScanlines);

                    // Update size info of the destination buffer
                    wrapperContext.SetCropSize(new TiffPoint(0, Math.Max(0, currentYOffset - context.SourceReadOffset.Y)), context.ReadSize);

                    // Check to see if there is any region area to be read
                    if (wrapperContext.ReadSize.IsAreaEmpty)
                    {
                        continue;
                    }

                    // Prepare stream regions of this strip
                    for (int planarIndex = 0; planarIndex < planeCount; planarIndex++)
                    {
                        int accessIndex = planarIndex * actualStripCount + stripIndex;
                        planarRegions[planarIndex] = await cache.GetOffsetAndCountAsync(accessIndex, cancellationToken).ConfigureAwait(false);
                    }
                    wrapperContext.PlanarRegions = planarRegions.GetReadOnlyView();

                    cancellationToken.ThrowIfCancellationRequested();

                    // Pass down the data
                    await next.RunAsync(wrapperContext).ConfigureAwait(false);
                }
            }
            finally
            {
                ((IDisposable)cache).Dispose();
                reader?.Dispose();
            }
        }