public static Task <int> Optimize(FileInfo source, FileInfo output) { using var bytes = new MemoryPoolBufferWriter(); using (FileStream stream = source.OpenRead()) { ReadAllBytes(stream, bytes); } var optimizer = new JpegOptimizer(); optimizer.SetInput(bytes.GetReadOnlySequence()); optimizer.Scan(); using var writer = new MemoryPoolBufferWriter(); optimizer.SetOutput(writer); optimizer.Optimize(); using (FileStream stream = output.OpenWrite()) { WriteAllBytes(writer.GetReadOnlySequence(), stream); } return(Task.FromResult(0)); }
/// <summary> /// Apply compression to <see cref="TiffImageEncoderContext{TPixel}.UncompressedData"/> and writes the compressed image to <see cref="TiffImageEncoderContext{TPixel}.FileWriter"/>. Writes <see cref="TiffTag.Compression"/> to thhe IFD writer and runs the next middleware. /// </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)); } if (context.FileWriter is null) { throw new InvalidOperationException("Failed to acquire FileWriter"); } TiffValueCollection <ushort> bitsPerSample = context.BitsPerSample; int totalBits = 0; for (int i = 0; i < bitsPerSample.Count; i++) { totalBits += bitsPerSample[i]; } int width = context.ImageSize.Width; int bytesPerScanlines = (totalBits * width + 7) / 8; var compressionContext = new TiffCompressionContext { MemoryPool = context.MemoryPool, PhotometricInterpretation = context.PhotometricInterpretation, ImageSize = context.ImageSize, BitsPerSample = context.BitsPerSample, BytesPerScanline = bytesPerScanlines }; using (var bufferWriter = new MemoryPoolBufferWriter(context.MemoryPool)) { _compressionAlgorithm.Compress(compressionContext, context.UncompressedData, bufferWriter); int length = bufferWriter.Length; using (await context.LockAsync().ConfigureAwait(false)) { TiffStreamOffset offset = await context.FileWriter !.WriteAlignedBytesAsync(bufferWriter.GetReadOnlySequence(), context.CancellationToken).ConfigureAwait(false); context.BitsPerSample = compressionContext.BitsPerSample; context.OutputRegion = new TiffStreamRegion(offset, length); } } TiffImageFileDirectoryWriter?ifdWriter = context.IfdWriter; if (!(ifdWriter is null)) { using (await context.LockAsync().ConfigureAwait(false)) { CancellationToken cancellationToken = context.CancellationToken; await ifdWriter.WriteTagAsync(TiffTag.Compression, TiffValueCollection.Single((ushort)_compression), cancellationToken).ConfigureAwait(false); } } await next.RunAsync(context).ConfigureAwait(false); }
private void InitializeTables() { using var buffer = new MemoryPoolBufferWriter(); _encoder.WriteTables(buffer, _useSharedHuffmanTables, _useSharedQuantizationTables); _jpegTables = buffer.ToArray(); }
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 static Task <int> Decode(FileInfo source, string output) { using var writer = new MemoryPoolBufferWriter(); using (FileStream stream = source.OpenRead()) { ReadAllBytes(stream, writer); } var decoder = new JpegDecoder(); decoder.SetInput(writer.GetReadOnlySequence()); decoder.Identify(); if (decoder.NumberOfComponents != 1 && decoder.NumberOfComponents != 3) { // We only support Grayscale and YCbCr. throw new NotSupportedException("This color space is not supported"); } int width = decoder.Width; int height = decoder.Height; byte[] ycbcr = new byte[width * height * 3]; if (decoder.Precision == 8) { // This is the most common case for JPEG. // We use the fatest implement. decoder.SetOutputWriter(new JpegBufferOutputWriter8Bit(width, height, 3, ycbcr)); } else if (decoder.Precision < 8) { decoder.SetOutputWriter(new JpegBufferOutputWriterLessThan8Bit(width, height, decoder.Precision, 3, ycbcr)); } else { decoder.SetOutputWriter(new JpegBufferOutputWriterGreaterThan8Bit(width, height, decoder.Precision, 3, ycbcr)); } decoder.Decode(); if (decoder.NumberOfComponents == 1) { // For grayscale image, we need to fill Cb and Cr in the YCbCr buffer. for (int i = 0; i < ycbcr.Length; i += 3) { ycbcr[i + 1] = 128; ycbcr[i + 2] = 128; } } using var image = new Image <Rgb24>(width, height); // Convert YCbCr to RGB for (int i = 0; i < height; i++) { JpegYCbCrToRgbConverter.Shared.ConvertYCbCr8ToRgb24(ycbcr.AsSpan(i * width * 3, width * 3), MemoryMarshal.AsBytes(image.GetPixelRowSpan(i)), width); } image.Save(output); return(Task.FromResult(0)); }