/// <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(); }
private static async Task CopyIfdAsync(TiffFileContentReader contentReader, TiffFieldReader fieldReader, TiffImageFileDirectory ifd, TiffImageFileDirectoryWriter dest, CancellationToken cancellationToken) { var tagReader = new TiffTagReader(fieldReader, ifd); foreach (TiffImageFileDirectoryEntry entry in ifd) { // Stripped image data if (entry.Tag == TiffTag.StripOffsets) { await CopyStrippedImageAsync(contentReader, tagReader, dest, cancellationToken); } else if (entry.Tag == TiffTag.StripByteCounts) { // Ignore this } // Tiled image data else if (entry.Tag == TiffTag.TileOffsets) { await CopyTiledImageAsync(contentReader, tagReader, dest, cancellationToken); } else if (entry.Tag == TiffTag.TileByteCounts) { // Ignore this } // Other fields else { await CopyFieldValueAsync(fieldReader, entry, dest, cancellationToken); } } }
private static async Task CopyStrippedImageAsync(TiffFileContentReader contentReader, TiffTagReader tagReader, TiffImageFileDirectoryWriter dest, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); TiffValueCollection <ulong> offsets = await tagReader.ReadStripOffsetsAsync(cancellationToken); TiffValueCollection <ulong> byteCounts = await tagReader.ReadStripByteCountsAsync(cancellationToken); if (offsets.Count != byteCounts.Count) { throw new InvalidDataException("Failed to copy stripped image data. StripOffsets and StripByteCounts don't have the same amount of elements."); } uint[] offsets32 = new uint[offsets.Count]; uint[] byteCounts32 = new uint[offsets.Count]; byte[]? buffer = null; try { for (int i = 0; i < offsets.Count; i++) { int offset = checked ((int)offsets[i]); int byteCount = checked ((int)byteCounts[i]); byteCounts32[i] = checked ((uint)byteCount); if (buffer is null || byteCount > buffer.Length) { if (!(buffer is null)) { ArrayPool <byte> .Shared.Return(buffer); } buffer = ArrayPool <byte> .Shared.Rent(byteCount); } if (await contentReader.ReadAsync(offset, new ArraySegment <byte>(buffer, 0, byteCount), cancellationToken) != byteCount) { throw new InvalidDataException("Invalid ByteCount field."); } TiffStreamOffset region = await dest.FileWriter.WriteAlignedBytesAsync(buffer, 0, byteCount); offsets32[i] = checked ((uint)region.Offset); } } finally { if (!(buffer is null)) { ArrayPool <byte> .Shared.Return(buffer); } } await dest.WriteTagAsync(TiffTag.StripOffsets, TiffValueCollection.UnsafeWrap(offsets32)); await dest.WriteTagAsync(TiffTag.StripByteCounts, TiffValueCollection.UnsafeWrap(byteCounts32)); }
public static async Task <int> Merge(FileInfo[] source, FileInfo output, CancellationToken cancellationToken) { if (source is null || source.Length == 0) { Console.WriteLine("Input TIFF file are not specified."); return(1); } if (output is null) { Console.WriteLine("Output TIFF file is not specified"); return(1); } await using TiffFileWriter writer = await TiffFileWriter.OpenAsync(output.FullName); TiffStreamOffset fistIfdOffset = default; TiffStreamOffset previousIfdOffset = default; foreach (FileInfo sourceFile in source) { await using TiffFileReader reader = await TiffFileReader.OpenAsync(sourceFile.FullName); await using TiffFileContentReader contentReader = await reader.CreateContentReaderAsync(); await using TiffFieldReader fieldReader = await reader.CreateFieldReaderAsync(cancellationToken); TiffStreamOffset inputIfdOffset = reader.FirstImageFileDirectoryOffset; while (!inputIfdOffset.IsZero) { TiffImageFileDirectory ifd = await reader.ReadImageFileDirectoryAsync(inputIfdOffset, cancellationToken); using (TiffImageFileDirectoryWriter ifdWriter = writer.CreateImageFileDirectory()) { await CopyIfdAsync(contentReader, fieldReader, ifd, ifdWriter, cancellationToken); previousIfdOffset = await ifdWriter.FlushAsync(previousIfdOffset); if (fistIfdOffset.IsZero) { fistIfdOffset = previousIfdOffset; } } inputIfdOffset = ifd.NextOffset; } } writer.SetFirstImageFileDirectoryOffset(fistIfdOffset); await writer.FlushAsync(); return(0); }
/// <inheritdoc /> public override async Task DecodeAsync <TPixel>(TiffPoint offset, TiffSize readSize, TiffPoint destinationOffset, ITiffPixelBufferWriter <TPixel> writer, CancellationToken cancellationToken = default) { 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."); } TiffFileContentReader reader = await _parameters.ContentSource.OpenReaderAsync(cancellationToken).ConfigureAwait(false); try { var context = new TiffDefaultImageDecoderContext <TPixel>() { MemoryPool = _parameters.MemoryPool ?? MemoryPool <byte> .Shared, CancellationToken = cancellationToken, 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) }; await _pipeline.RunAsync(context).ConfigureAwait(false); } finally { await reader.DisposeAsync().ConfigureAwait(false); } }
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(); } }
public async Task TestRead(byte[] referenceContent, TiffFileContentSource source) { var rand = new Random(42); try { TiffFileContentReader reader = await source.OpenReaderAsync(); // Random read within the range of the file. for (int i = 0; i < 100; i++) { int offset = rand.Next(0, referenceContent.Length); int count = 1; if (offset + 1 < referenceContent.Length) { count = rand.Next(1, referenceContent.Length - offset); } await AssertReadAsync(reader, offset, count, count); } // Read on the edge of the file await AssertReadAsync(reader, referenceContent.Length - 2048, 4096, 2048); // Read past the end of the file. await AssertReadAsync(reader, referenceContent.Length, 4096, 0); await AssertReadAsync(reader, referenceContent.Length + 2048, 4096, 0); } finally { await source.DisposeAsync(); } async Task AssertReadAsync(TiffFileContentReader reader, int fileOffset, int count, int expectedCount) { int readCount; byte[] buffer = ArrayPool <byte> .Shared.Rent(count); try { // Use Memory API Array.Clear(buffer, 0, count); readCount = await reader.ReadAsync(fileOffset, new Memory <byte>(buffer, 0, count), CancellationToken.None); Assert.Equal(expectedCount, readCount); Assert.True(buffer.AsSpan(0, readCount).SequenceEqual(referenceContent.AsSpan(Math.Min(referenceContent.Length, fileOffset), expectedCount))); // Use ArraySegment API Array.Clear(buffer, 0, count); readCount = await reader.ReadAsync(fileOffset, new ArraySegment <byte>(buffer, 0, count), CancellationToken.None); Assert.Equal(expectedCount, readCount); Assert.True(buffer.AsSpan(0, readCount).SequenceEqual(referenceContent.AsSpan(Math.Min(referenceContent.Length, fileOffset), expectedCount))); } finally { ArrayPool <byte> .Shared.Return(buffer); } } }