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