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