/// <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();
            }
        }
Example #2
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();
            }
        }