Exemple #1
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();
        }
Exemple #2
0
        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);
                }
            }
        }
Exemple #3
0
        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));
        }
Exemple #4
0
        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);
        }
Exemple #5
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);
                }
            }
        }