/// <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 (_bytesPerScanlines.Count != context.PlanarRegions.Count)
            {
                throw new InvalidOperationException();
            }

            int planeCount = _bytesPerScanlines.Count;

            // calculate the uncompressed data buffer length
            int uncompressedDataLength = 0;
            int imageHeight            = context.SourceImageSize.Height;

            foreach (int bytesPerScanline in _bytesPerScanlines)
            {
                uncompressedDataLength += bytesPerScanline * imageHeight;
            }

            TiffEmptyStrileWriter?emptyWriter = null;

            // calculate the maximum buffer needed to read from stream
            int readCount = 0;

            foreach (TiffStreamRegion planarRegion in context.PlanarRegions)
            {
                int length = planarRegion.Length;
                if (length == 0)
                {
                    emptyWriter = new TiffEmptyStrileWriter(new TiffRgba32(255, 255, 255, 0));
                    break;
                }
                if (length > readCount)
                {
                    readCount = planarRegion.Length;
                }
            }

            if (!(emptyWriter is null))
            {
                emptyWriter.Write(context);
                return;
            }

            // allocate the raw data buffer and the uncompressed data buffer
            MemoryPool <byte> memoryPool = context.MemoryPool ?? MemoryPool <byte> .Shared;

            using IMemoryOwner <byte> bufferMemory = memoryPool.Rent(uncompressedDataLength);
            int planarUncompressedByteCount        = 0;
            TiffFileContentReader?reader           = context.ContentReader;

            if (reader is null)
            {
                throw new InvalidOperationException("Failed to acquire ContentReader.");
            }

            using (IMemoryOwner <byte> rawBuffer = memoryPool.Rent(readCount))
            {
                TiffDecompressionContext decompressionContext = new TiffDecompressionContext();

                // decompress each plane
                for (int i = 0; i < planeCount; i++)
                {
                    TiffStreamRegion region = context.PlanarRegions[i];

                    // Read from stream
                    readCount = await reader.ReadAsync(region.Offset, rawBuffer.Memory.Slice(0, region.Length), context.CancellationToken).ConfigureAwait(false);

                    if (readCount != region.Length)
                    {
                        throw new InvalidDataException();
                    }

                    // Decompress this plane
                    int bytesPerScanline = _bytesPerScanlines[i];
                    decompressionContext.MemoryPool = memoryPool;
                    decompressionContext.PhotometricInterpretation = _photometricInterpretation;
                    decompressionContext.BitsPerSample             = _bitsPerSample;
                    decompressionContext.ImageSize          = context.SourceImageSize;
                    decompressionContext.BytesPerScanline   = bytesPerScanline;
                    decompressionContext.SkippedScanlines   = context.SourceReadOffset.Y;
                    decompressionContext.RequestedScanlines = context.ReadSize.Height;
                    _decompressionAlgorithm.Decompress(decompressionContext, rawBuffer.Memory.Slice(0, readCount), bufferMemory.Memory.Slice(planarUncompressedByteCount, bytesPerScanline * imageHeight));
                    planarUncompressedByteCount += bytesPerScanline * imageHeight;
                }
            }

            // Pass down the data
            context.UncompressedData = bufferMemory.Memory.Slice(0, uncompressedDataLength);
            await next.RunAsync(context).ConfigureAwait(false);

            context.UncompressedData = default;
        }
        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();
            }
        }
예제 #3
0
        internal async Task <TiffStreamOffset> GenerateReducedResolutionLayerAsync(int layer, TiffStreamOffset ifdOffset, IRipperReducedResolutionGenerationReporter reporter, CancellationToken cancellationToken)
        {
            int outputTileSize         = _options.OutputTileSize;
            int outputTileSize2        = 2 * outputTileSize;
            TiffImageFileDirectory ifd = await _fileReader.ReadImageFileDirectoryAsync(ifdOffset).ConfigureAwait(false);

            TiffImageDecoder image = await _fileReader.CreateImageDecoderAsync(ifd);

            int width  = image.Width;
            int height = image.Height;

            int tiffRowCount = (width / 2 + outputTileSize - 1) / outputTileSize;
            int tiffColCount = (height / 2 + outputTileSize - 1) / outputTileSize;

            int index = 0;

            ulong[] offsets        = new ulong[tiffRowCount * tiffColCount];
            ulong[] byteCounts     = new ulong[tiffRowCount * tiffColCount];
            ulong   totalByteCount = 0;

            reporter?.ReportStartReducedResolutionLayerGeneration(layer, offsets.Length, width / 2, height / 2);

            using (Image <Rgb24> canvas2 = new Image <Rgb24>(_configuration, outputTileSize2, outputTileSize2))
            {
                for (int y = 0; y < height; y += outputTileSize2)
                {
                    for (int x = 0; x < width; x += outputTileSize2)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        MemoryMarshal.AsBytes(canvas2.GetPixelSpan()).Clear();

                        await image.DecodeAsync(new TiffPoint(x, y), canvas2).ConfigureAwait(false);

                        using (Image <Rgb24> tile24 = canvas2.Clone(ctx =>
                        {
                            ctx.Resize(outputTileSize, outputTileSize);
                        }))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            TiffStreamRegion region = await _encoder.EncodeAsync(_fileWriter, tile24).ConfigureAwait(false);

                            offsets[index]    = (ulong)region.Offset.Offset;
                            byteCounts[index] = (uint)region.Length;
                            totalByteCount   += (uint)region.Length;
                            index++;

                            reporter?.ReportReducedResolutionLayerGenerationProgress(layer, index, offsets.Length);
                        }
                    }
                }
            }

            cancellationToken.ThrowIfCancellationRequested();
            using (TiffImageFileDirectoryWriter ifdWriter = _fileWriter.CreateImageFileDirectory())
            {
                await ifdWriter.WriteTagAsync(TiffTag.NewSubfileType, new TiffValueCollection <ushort>((ushort)TiffNewSubfileType.ReducedResolution)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, new TiffValueCollection <ushort>((ushort)TiffPhotometricInterpretation.YCbCr)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.Compression, new TiffValueCollection <ushort>((ushort)TiffCompression.Jpeg)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, new TiffValueCollection <ushort>((ushort)3)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileWidth, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileLength, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                //ifdWriter.AddTag(TiffTag.ResolutionUnit, (ushort)TiffResolutionUnit.Inch);

                //await ifdWriter.WriteTagAsync(TiffTag.XResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                //await ifdWriter.WriteTagAsync(TiffTag.YResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                await ifdWriter.WriteTagAsync(TiffTag.SampleFormat, new TiffValueCollection <ushort>(new ushort[] { 1, 1, 1 })).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, new TiffValueCollection <ushort>(new ushort[] { 8, 8, 8 })).ConfigureAwait(false);

                if (UseBigTiff)
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <ulong>((ulong)(width / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <ulong>((ulong)(height / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <ulong>(offsets)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <ulong>(byteCounts)).ConfigureAwait(false);
                }
                else
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <uint>((uint)(width / 2))).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <uint>((uint)(height / 2))).ConfigureAwait(false);

                    uint[] tempArr = new uint[offsets.Length];
                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)offsets[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);

                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)byteCounts[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);
                }

                string software = Software;
                if (!string.IsNullOrEmpty(software))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.Software, new TiffValueCollection <string>(software));
                }

                ifdOffset = await ifdWriter.FlushAsync(ifdOffset).ConfigureAwait(false);
            }

            reporter?.ReportCompleteReducedResolutionLayerGeneration(layer, offsets.Length, (long)totalByteCount);


            return(ifdOffset);
        }
 public LegacyJpegStreamDecoder(TiffStreamRegion streamRegion)
 {
     _streamRegion = streamRegion;
 }
예제 #5
0
        public async Task RipBaseLayerAsync(IRipperInitialLayerAcquisitionReporter reporter, CancellationToken cancellationToken)
        {
            int outputTileSize = _options.OutputTileSize;

            int tiffRowCount = (ImageHeight + outputTileSize - 1) / outputTileSize;
            int tiffColCount = (ImageWidth + outputTileSize - 1) / outputTileSize;

            int index = 0;

            ulong[] offsets        = new ulong[tiffRowCount * tiffColCount];
            ulong[] byteCounts     = new ulong[tiffRowCount * tiffColCount];
            ulong   totalByteCount = 0;

            reporter?.ReportStartInitialLayerAcquisition(offsets.Length);

            using (var regionReader = new TileRegionReader(this))
                using (Image <Rgb24> canvas = new Image <Rgb24>(_configuration, outputTileSize, outputTileSize))
                {
                    for (int row = 0; row < tiffRowCount; row++)
                    {
                        int rowYOffset = row * outputTileSize;

                        for (int col = 0; col < tiffColCount; col++)
                        {
                            int colXOffset = col * outputTileSize;

                            MemoryMarshal.AsBytes(canvas.GetPixelSpan()).Clear();

                            await regionReader.FillRegionAsync(colXOffset, rowYOffset, canvas, cancellationToken).ConfigureAwait(false);

                            cancellationToken.ThrowIfCancellationRequested();
                            TiffStreamRegion region = await _encoder.EncodeAsync(_fileWriter, canvas).ConfigureAwait(false);

                            offsets[index]    = (ulong)region.Offset.Offset;
                            byteCounts[index] = (uint)region.Length;
                            totalByteCount   += (uint)region.Length;
                            index++;

                            reporter?.ReportInitialLayerAcquisitionProgress(index, offsets.Length);
                        }
                    }
                }

            cancellationToken.ThrowIfCancellationRequested();
            TiffStreamOffset ifdOffset;

            using (TiffImageFileDirectoryWriter ifdWriter = _fileWriter.CreateImageFileDirectory())
            {
                await ifdWriter.WriteTagAsync(TiffTag.PhotometricInterpretation, new TiffValueCollection <ushort>((ushort)TiffPhotometricInterpretation.YCbCr)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.Compression, new TiffValueCollection <ushort>((ushort)TiffCompression.Jpeg)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.SamplesPerPixel, new TiffValueCollection <ushort>((ushort)3)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileWidth, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.TileLength, new TiffValueCollection <ushort>((ushort)outputTileSize)).ConfigureAwait(false);

                //ifdWriter.AddTag(TiffTag.ResolutionUnit, (ushort)TiffResolutionUnit.Inch);

                //await ifdWriter.WriteTagAsync(TiffTag.XResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                //await ifdWriter.WriteTagAsync(TiffTag.YResolution, new TiffValueCollection<TiffRational>(new TiffRational(72, 1)));
                await ifdWriter.WriteTagAsync(TiffTag.SampleFormat, new TiffValueCollection <ushort>(new ushort[] { 1, 1, 1 })).ConfigureAwait(false);

                await ifdWriter.WriteTagAsync(TiffTag.BitsPerSample, new TiffValueCollection <ushort>(new ushort[] { 8, 8, 8 })).ConfigureAwait(false);

                if (UseBigTiff)
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <ulong>((ulong)_manifest.Width)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <ulong>((ulong)_manifest.Height)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <ulong>(offsets)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <ulong>(byteCounts)).ConfigureAwait(false);
                }
                else
                {
                    await ifdWriter.WriteTagAsync(TiffTag.ImageWidth, new TiffValueCollection <uint>((uint)_manifest.Width)).ConfigureAwait(false);

                    await ifdWriter.WriteTagAsync(TiffTag.ImageLength, new TiffValueCollection <uint>((uint)_manifest.Height)).ConfigureAwait(false);

                    uint[] tempArr = new uint[offsets.Length];
                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)offsets[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileOffsets, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);

                    for (int i = 0; i < tempArr.Length; i++)
                    {
                        tempArr[i] = (uint)byteCounts[i];
                    }
                    await ifdWriter.WriteTagAsync(TiffTag.TileByteCounts, new TiffValueCollection <uint>(tempArr)).ConfigureAwait(false);
                }

                string software = Software;
                if (!string.IsNullOrEmpty(software))
                {
                    await ifdWriter.WriteTagAsync(TiffTag.Software, new TiffValueCollection <string>(software));
                }

                ifdOffset = await ifdWriter.FlushAsync().ConfigureAwait(false);
            }

            _fileWriter.SetFirstImageFileDirectoryOffset(ifdOffset);
            await _fileWriter.FlushAsync().ConfigureAwait(false);

            reporter?.ReportCompleteInitialLayerAcquisition(offsets.Length, (long)totalByteCount);
        }