/// <summary> /// Crops the input image into tiles and runs the next middleware for each tile. /// </summary> /// <param name="context">The encoder context.</param> /// <param name="next">The next middleware.</param> /// <returns>A <see cref="Task"/> that completes when the image has been encoded.</returns> public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } var state = context.GetService(typeof(TiffParallelEncodingState)) as TiffParallelEncodingState; TiffCroppedImageEncoderContext <TPixel>?wrappedContext = null; int width = context.ImageSize.Width, height = context.ImageSize.Height; int tileWidth = _tileSize.Width, tileHeight = _tileSize.Height; int tileAcross = (width + tileWidth - 1) / tileWidth; int tileDown = (height + tileHeight - 1) / tileHeight; int tileCount = tileAcross * tileDown; ulong[] tileOffsets = new ulong[tileCount]; ulong[] tileByteCounts = new ulong[tileCount]; int index = 0; state?.LockTaskCompletion(); for (int row = 0; row < tileDown; row++) { int offsetY = row * tileHeight; int imageHeight = Math.Min(height - offsetY, tileHeight); for (int col = 0; col < tileAcross; col++) { int offsetX = col * tileWidth; int imageWidth = Math.Min(width - offsetX, tileWidth); wrappedContext ??= new TiffCroppedImageEncoderContext <TPixel>(context); wrappedContext.ExposeIfdWriter = row == 0 && col == 0; wrappedContext.OutputRegion = default; wrappedContext.Crop(new TiffPoint(offsetX, offsetY), new TiffSize(imageWidth, imageHeight)); if (state is null) { await next.RunAsync(wrappedContext).ConfigureAwait(false); tileOffsets[index] = (ulong)(long)wrappedContext.OutputRegion.Offset; tileByteCounts[index] = (ulong)wrappedContext.OutputRegion.Length; } else { TiffCroppedImageEncoderContext <TPixel>?wContext = wrappedContext; wrappedContext = null; int currentIndex = index; await state.DispatchAsync(async() => { await next.RunAsync(wContext).ConfigureAwait(false); tileOffsets[currentIndex] = (ulong)(long)wContext.OutputRegion.Offset; tileByteCounts[currentIndex] = (ulong)wContext.OutputRegion.Length; }, context.CancellationToken).ConfigureAwait(false); } index++; } } // Wait until all tiles are written. if (!(state is null)) { state.ReleaseTaskCompletion(); await state.Complete.Task.ConfigureAwait(false); } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { using (await context.LockAsync().ConfigureAwait(false)) { CancellationToken cancellationToken = context.CancellationToken; await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, TiffValueCollection.Single((uint)width), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ImageLength, TiffValueCollection.Single((uint)height), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileWidth, TiffValueCollection.Single((ushort)tileWidth), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileLength, TiffValueCollection.Single((ushort)tileHeight), cancellationToken).ConfigureAwait(false); if (context.FileWriter?.UseBigTiff ?? false) { await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, TiffValueCollection.UnsafeWrap(tileOffsets), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, TiffValueCollection.UnsafeWrap(tileByteCounts), cancellationToken).ConfigureAwait(false); } else { uint[] tileOffsets32 = new uint[tileCount]; uint[] tileByteCounts32 = new uint[tileCount]; for (int i = 0; i < tileCount; i++) { tileOffsets32[i] = (uint)tileOffsets[i]; tileByteCounts32[i] = (uint)tileByteCounts[i]; } await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, TiffValueCollection.UnsafeWrap(tileOffsets32), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, TiffValueCollection.UnsafeWrap(tileByteCounts32), cancellationToken).ConfigureAwait(false); } } } }
/// <summary> /// Crops the input image into multiple strips and runs the next middleware for each strip. /// </summary> /// <param name="context">The encoder context.</param> /// <param name="next">The next middleware.</param> /// <returns>A <see cref="Task"/> that completes when the image has been encoded.</returns> public async ValueTask InvokeAsync(TiffImageEncoderContext <TPixel> context, ITiffImageEncoderPipelineNode <TPixel> next) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (next is null) { throw new ArgumentNullException(nameof(next)); } var state = context.GetService(typeof(TiffParallelEncodingState)) as TiffParallelEncodingState; TiffCroppedImageEncoderContext <TPixel>?wrappedContext = null; int width = context.ImageSize.Width, height = context.ImageSize.Height; int rowsPerStrip = _rowsPerStrip <= 0 ? height : _rowsPerStrip; int stripCount = (height + rowsPerStrip - 1) / rowsPerStrip; ulong[] stripOffsets = new ulong[stripCount]; ulong[] stripByteCounts = new ulong[stripCount]; state?.LockTaskCompletion(); for (int i = 0; i < stripCount; i++) { int offsetY = i * rowsPerStrip; int stripHeight = Math.Min(height - offsetY, rowsPerStrip); wrappedContext ??= new TiffCroppedImageEncoderContext <TPixel>(context); wrappedContext.ExposeIfdWriter = i == 0; wrappedContext.OutputRegion = default; wrappedContext.Crop(new TiffPoint(0, offsetY), new TiffSize(width, stripHeight)); if (state is null) { await next.RunAsync(wrappedContext).ConfigureAwait(false); stripOffsets[i] = (ulong)(long)wrappedContext.OutputRegion.Offset; stripByteCounts[i] = (ulong)wrappedContext.OutputRegion.Length; } else { TiffCroppedImageEncoderContext <TPixel>?wContext = wrappedContext; wrappedContext = null; int currentIndex = i; await state.DispatchAsync(async() => { await next.RunAsync(wContext).ConfigureAwait(false); stripOffsets[currentIndex] = (ulong)(long)wContext.OutputRegion.Offset; stripByteCounts[currentIndex] = (ulong)wContext.OutputRegion.Length; }, context.CancellationToken).ConfigureAwait(false); } } // Wait until all strips are written. if (!(state is null)) { state.ReleaseTaskCompletion(); await state.Complete.Task.ConfigureAwait(false); } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { CancellationToken cancellationToken = context.CancellationToken; await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, TiffValueCollection.Single((uint)width), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.ImageLength, TiffValueCollection.Single((uint)height), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.RowsPerStrip, TiffValueCollection.Single((ushort)rowsPerStrip), cancellationToken).ConfigureAwait(false); if (context.FileWriter?.UseBigTiff ?? false) { await ifdWriter.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(stripOffsets), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(stripByteCounts), cancellationToken).ConfigureAwait(false); } else { uint[] stripOffsets32 = new uint[stripCount]; uint[] stripByteCounts32 = new uint[stripCount]; for (int i = 0; i < stripCount; i++) { stripOffsets32[i] = (uint)stripOffsets[i]; stripByteCounts32[i] = (uint)stripByteCounts[i]; } await ifdWriter.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(stripOffsets32), cancellationToken).ConfigureAwait(false); await ifdWriter.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(stripByteCounts32), cancellationToken).ConfigureAwait(false); } } }