diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index e37144bd58..4b14061cf8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// A useful decoding source example can be found at /// - internal sealed class BmpDecoderCore + internal sealed class BmpDecoderCore : IImageDecoderInternals { /// /// The default mask for the red part of the color for 16 bit rgb bitmaps. @@ -89,11 +89,6 @@ internal sealed class BmpDecoderCore /// private BmpInfoHeader infoHeader; - /// - /// The global configuration. - /// - private readonly Configuration configuration; - /// /// Used for allocating memory during processing operations. /// @@ -111,67 +106,28 @@ internal sealed class BmpDecoderCore /// The options. public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) { - this.configuration = configuration; + this.Configuration = configuration; this.memoryAllocator = configuration.MemoryAllocator; this.options = options; } + /// + public Configuration Configuration { get; } + /// /// Gets the dimensions of the image. /// public Size Dimensions => new Size(this.infoHeader.Width, this.infoHeader.Height); - /// - /// Decodes the image from the specified this._stream and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be - /// decoded from. Cannot be null (Nothing in Visual Basic). - /// - /// is null. - /// - /// The decoded image. - public async Task> DecodeAsync(Stream stream) - where TPixel : unmanaged, IPixel - { - // if we can seek then we arn't in a context that errors on async operations - if (stream.CanSeek) - { - return this.Decode(stream); - } - else - { - // cheat for now do async copy of the stream into memory stream and use the sync version - // we should use an array pool backed memorystream implementation - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Decode(ms); - } - } - } - - /// - /// Decodes the image from the specified this._stream and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be - /// decoded from. Cannot be null (Nothing in Visual Basic). - /// - /// is null. - /// - /// The decoded image. + /// public Image Decode(Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { try { int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); - var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); + var image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); @@ -242,30 +198,13 @@ public Image Decode(Stream stream) } } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. + /// public IImageInfo Identify(Stream stream) { this.ReadImageHeaders(stream, out _, out _); return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public async Task IdentifyAsync(Stream stream) - { - using (var ms = new FixedCapacityPooledMemoryStream(stream.Length)) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Identify(ms); - } - } - /// /// Returns the y- value based on the given height. /// @@ -999,7 +938,7 @@ private void ReadRgb24(Buffer2D pixels, int width, int height, b int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes( - this.configuration, + this.Configuration, row.GetSpan(), pixelSpan, width); @@ -1028,7 +967,7 @@ private void ReadRgb32Fast(Buffer2D pixels, int width, int heigh int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.configuration, + this.Configuration, row.GetSpan(), pixelSpan, width); @@ -1065,7 +1004,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh this.stream.Read(row); PixelOperations.Instance.FromBgra32Bytes( - this.configuration, + this.Configuration, row.GetSpan(), bgraRowSpan, width); @@ -1101,7 +1040,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh Span pixelSpan = pixels.GetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( - this.configuration, + this.Configuration, row.GetSpan(), pixelSpan, width); @@ -1115,7 +1054,7 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh { this.stream.Read(row); PixelOperations.Instance.FromBgra32Bytes( - this.configuration, + this.Configuration, row.GetSpan(), bgraRowSpan, width); diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 8f8426780d..e4c98799ba 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -17,18 +17,13 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Performs the gif decoding operation. /// - internal sealed class GifDecoderCore + internal sealed class GifDecoderCore : IImageDecoderInternals { /// /// The temp buffer used to reduce allocations. /// private readonly byte[] buffer = new byte[16]; - /// - /// The global configuration. - /// - private readonly Configuration configuration; - /// /// The currently loaded stream. /// @@ -78,9 +73,12 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) { this.IgnoreMetadata = options.IgnoreMetadata; this.DecodingMode = options.DecodingMode; - this.configuration = configuration ?? Configuration.Default; + this.Configuration = configuration ?? Configuration.Default; } + /// + public Configuration Configuration { get; } + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// @@ -96,40 +94,11 @@ public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) /// public Size Dimensions => new Size(this.imageDescriptor.Width, this.imageDescriptor.Height); - private MemoryAllocator MemoryAllocator => this.configuration.MemoryAllocator; - - /// - /// Decodes the stream to the image. - /// - /// The pixel format. - /// The stream containing image data. - /// The decoded image - public async Task> DecodeAsync(Stream stream) - where TPixel : unmanaged, IPixel - { - if (stream.CanSeek) - { - return this.Decode(stream); - } - else - { - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Decode(ms); - } - } - } + private MemoryAllocator MemoryAllocator => this.Configuration.MemoryAllocator; - /// - /// Decodes the stream to the image. - /// - /// The pixel format. - /// The stream containing image data. - /// The decoded image + /// public Image Decode(Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { Image image = null; ImageFrame previousFrame = null; @@ -188,31 +157,7 @@ public Image Decode(Stream stream) return image; } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public async Task IdentifyAsync(Stream stream) - { - if (stream.CanSeek) - { - return this.Identify(stream); - } - else - { - using (var ms = new FixedCapacityPooledMemoryStream(stream.Length)) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Identify(ms); - } - } - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. + /// public IImageInfo Identify(Stream stream) { try @@ -410,11 +355,11 @@ private void ReadFrame(ref Image image, ref ImageFrame p if (this.imageDescriptor.LocalColorTableFlag) { int length = this.imageDescriptor.LocalColorTableSize * 3; - localColorTable = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); + localColorTable = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); this.stream.Read(localColorTable.Array, 0, length); } - indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); + indices = this.Configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); this.ReadFrameIndices(indices); ReadOnlySpan colorTable = MemoryMarshal.Cast((localColorTable ?? this.globalColorTable).GetSpan()); @@ -438,7 +383,7 @@ private void ReadFrame(ref Image image, ref ImageFrame p private void ReadFrameIndices(Buffer2D indices) { int dataSize = this.stream.ReadByte(); - using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream); + using var lzwDecoder = new LzwDecoder(this.Configuration.MemoryAllocator, this.stream); lzwDecoder.DecodePixels(dataSize, indices); } @@ -464,7 +409,7 @@ private void ReadFrameColors(ref Image image, ref ImageFrame(this.configuration, imageWidth, imageHeight, this.metadata); + image = new Image(this.Configuration, imageWidth, imageHeight, this.metadata); this.SetFrameMetadata(image.Frames.RootFrame.Metadata); diff --git a/src/ImageSharp/Formats/IImageDecoderInternals.cs b/src/ImageSharp/Formats/IImageDecoderInternals.cs new file mode 100644 index 0000000000..3ab9123530 --- /dev/null +++ b/src/ImageSharp/Formats/IImageDecoderInternals.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Abstraction for shared internals for ***DecoderCore implementations to be used with . + /// + internal interface IImageDecoderInternals + { + /// + /// Gets the associated configuration. + /// + Configuration Configuration { get; } + + /// + /// Decodes the image from the specified stream. + /// + /// The pixel format. + /// The stream, where the image should be decoded from. Cannot be null. + /// + /// is null. + /// + /// The decoded image. + Image Decode(Stream stream) + where TPixel : unmanaged, IPixel; + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + /// The . + IImageInfo Identify(Stream stream); + } +} diff --git a/src/ImageSharp/Formats/ImageDecoderUtilities.cs b/src/ImageSharp/Formats/ImageDecoderUtilities.cs new file mode 100644 index 0000000000..6bb9116cda --- /dev/null +++ b/src/ImageSharp/Formats/ImageDecoderUtilities.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats +{ + internal static class ImageDecoderUtilities + { + /// + /// Reads the raw image information from the specified stream. + /// + /// The decoder. + /// The containing image data. + public static async Task IdentifyAsync(this IImageDecoderInternals decoder, Stream stream) + { + if (stream.CanSeek) + { + return decoder.Identify(stream); + } + + using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length); + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return decoder.Identify(ms); + } + + /// + /// Decodes the image from the specified stream. + /// + /// The pixel format. + /// The decoder. + /// The stream, where the image should be decoded from. Cannot be null. + /// + /// is null. + /// + /// The decoded image. + public static async Task> DecodeAsync(this IImageDecoderInternals decoder, Stream stream) + where TPixel : unmanaged, IPixel + { + if (stream.CanSeek) + { + return decoder.Decode(stream); + } + + using MemoryStream ms = decoder.Configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length); + await stream.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + return decoder.Decode(ms); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index af1a705d44..f8151141ca 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -25,18 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class JpegDecoderCore : IRawJpegData + internal sealed class JpegDecoderCore : IRawJpegData, IImageDecoderInternals { /// /// The only supported precision /// private readonly int[] supportedPrecisions = { 8, 12 }; - /// - /// The global configuration - /// - private readonly Configuration configuration; - /// /// The buffer used to temporarily store bytes read from the stream. /// @@ -109,10 +104,13 @@ internal sealed class JpegDecoderCore : IRawJpegData /// The options. public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) { - this.configuration = configuration ?? Configuration.Default; + this.Configuration = configuration ?? Configuration.Default; this.IgnoreMetadata = options.IgnoreMetadata; } + /// + public Configuration Configuration { get; } + /// /// Gets the frame /// @@ -213,38 +211,9 @@ public static JpegFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStr return new JpegFileMarker(marker[1], stream.Position - 2, true); } - /// - /// Decodes the image from the specified and sets the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public async Task> DecodeAsync(Stream stream) - where TPixel : unmanaged, IPixel - { - if (stream.CanSeek) - { - return this.Decode(stream); - } - else - { - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Decode(ms); - } - } - } - - /// - /// Decodes the image from the specified and sets the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. + /// public Image Decode(Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { this.ParseStream(stream); this.InitExifProfile(); @@ -254,31 +223,7 @@ public Image Decode(Stream stream) return this.PostProcessIntoImage(); } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public async Task IdentifyAsync(Stream stream) - { - if (stream.CanSeek) - { - return this.Identify(stream); - } - else - { - using (var ms = new FixedCapacityPooledMemoryStream(stream.Length)) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Identify(ms); - } - } - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. + /// public IImageInfo Identify(Stream stream) { this.ParseStream(stream, true); @@ -298,7 +243,7 @@ public IImageInfo Identify(Stream stream) public void ParseStream(Stream stream, bool metadataOnly = false) { this.Metadata = new ImageMetadata(); - this.InputStream = new DoubleBufferedStreamReader(this.configuration.MemoryAllocator, stream); + this.InputStream = new DoubleBufferedStreamReader(this.Configuration.MemoryAllocator, stream); // Check for the Start Of Image marker. this.InputStream.Read(this.markerBuffer, 0, 2); @@ -937,7 +882,7 @@ private void ProcessStartOfFrameMarker(int remaining, in JpegFileMarker frameMar maxV = v; } - var component = new JpegComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; @@ -962,7 +907,7 @@ private void ProcessDefineHuffmanTablesMarker(int remaining) { int length = remaining; - using (IManagedByteBuffer huffmanData = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) + using (IManagedByteBuffer huffmanData = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) { ref byte huffmanDataRef = ref MemoryMarshal.GetReference(huffmanData.GetSpan()); for (int i = 2; i < remaining;) @@ -985,7 +930,7 @@ private void ProcessDefineHuffmanTablesMarker(int remaining) this.InputStream.Read(huffmanData.Array, 0, 16); - using (IManagedByteBuffer codeLengths = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean)) + using (IManagedByteBuffer codeLengths = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(17, AllocationOptions.Clean)) { ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths.GetSpan()); int codeLengthSum = 0; @@ -1002,7 +947,7 @@ private void ProcessDefineHuffmanTablesMarker(int remaining) JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); } - using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) + using (IManagedByteBuffer huffmanValues = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) { this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); @@ -1129,12 +1074,12 @@ private Image PostProcessIntoImage() } var image = Image.CreateUninitialized( - this.configuration, + this.Configuration, this.ImageWidth, this.ImageHeight, this.Metadata); - using (var postProcessor = new JpegImagePostProcessor(this.configuration, this)) + using (var postProcessor = new JpegImagePostProcessor(this.Configuration, this)) { postProcessor.PostProcess(image.Frames.RootFrame); } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index bb8589de49..e2b0e50fcc 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -24,18 +24,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Performs the png decoding operation. /// - internal sealed class PngDecoderCore + internal sealed class PngDecoderCore : IImageDecoderInternals { /// /// Reusable buffer. /// private readonly byte[] buffer = new byte[4]; - /// - /// The global configuration. - /// - private readonly Configuration configuration; - /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// @@ -123,60 +118,22 @@ internal sealed class PngDecoderCore /// The decoder options. public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) { - this.configuration = configuration ?? Configuration.Default; - this.memoryAllocator = this.configuration.MemoryAllocator; + this.Configuration = configuration ?? Configuration.Default; + this.memoryAllocator = this.Configuration.MemoryAllocator; this.ignoreMetadata = options.IgnoreMetadata; } + /// + public Configuration Configuration { get; } + /// /// Gets the dimensions of the image. /// public Size Dimensions => new Size(this.header.Width, this.header.Height); - /// - /// Decodes the stream to the image. - /// - /// The pixel format. - /// The stream containing image data. - /// - /// Thrown if the stream does not contain and end chunk. - /// - /// - /// Thrown if the image is larger than the maximum allowable size. - /// - /// The decoded image. - public async Task> DecodeAsync(Stream stream) - where TPixel : unmanaged, IPixel - { - if (stream.CanSeek) - { - return this.Decode(stream); - } - else - { - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Decode(ms); - } - } - } - - /// - /// Decodes the stream to the image. - /// - /// The pixel format. - /// The stream containing image data. - /// - /// Thrown if the stream does not contain and end chunk. - /// - /// - /// Thrown if the image is larger than the maximum allowable size. - /// - /// The decoded image. + /// public Image Decode(Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { var metadata = new ImageMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata(); @@ -266,31 +223,7 @@ public Image Decode(Stream stream) } } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public async Task IdentifyAsync(Stream stream) - { - if (stream.CanSeek) - { - return this.Identify(stream); - } - else - { - using (var ms = new FixedCapacityPooledMemoryStream(stream.Length)) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Identify(ms); - } - } - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. + /// public IImageInfo Identify(Stream stream) { var metadata = new ImageMetadata(); @@ -446,7 +379,7 @@ private void InitializeImage(ImageMetadata metadata, out Image i where TPixel : unmanaged, IPixel { image = Image.CreateUninitialized( - this.configuration, + this.Configuration, this.header.Width, this.header.Height, metadata); @@ -460,7 +393,7 @@ private void InitializeImage(ImageMetadata metadata, out Image i } this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); - this.scanline = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); + this.scanline = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); } /// @@ -759,7 +692,7 @@ private void ProcessDefilteredScanline(ReadOnlySpan defilteredScan case PngColorType.Rgb: PngScanlineProcessor.ProcessRgbScanline( - this.configuration, + this.Configuration, this.header, scanlineSpan, rowSpan, @@ -773,7 +706,7 @@ private void ProcessDefilteredScanline(ReadOnlySpan defilteredScan case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessRgbaScanline( - this.configuration, + this.Configuration, this.header, scanlineSpan, rowSpan, @@ -1258,7 +1191,7 @@ private void SkipChunkDataAndCrc(in PngChunk chunk) private IManagedByteBuffer ReadChunkData(int length) { // We rent the buffer here to return it afterwards in Decode() - IManagedByteBuffer buffer = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); + IManagedByteBuffer buffer = this.Configuration.MemoryAllocator.AllocateManagedByteBuffer(length, AllocationOptions.Clean); this.currentStream.Read(buffer.Array, 0, length); diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index ae8946173b..3f6d721f6a 100644 --- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs +++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.IO; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tga /// /// Performs the tga decoding operation. /// - internal sealed class TgaDecoderCore + internal sealed class TgaDecoderCore : IImageDecoderInternals { /// /// A scratch buffer to reduce allocations. @@ -38,11 +37,6 @@ internal sealed class TgaDecoderCore /// private TgaFileHeader fileHeader; - /// - /// The global configuration. - /// - private readonly Configuration configuration; - /// /// Used for allocating memory during processing operations. /// @@ -70,54 +64,22 @@ internal sealed class TgaDecoderCore /// The options. public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) { - this.configuration = configuration; + this.Configuration = configuration; this.memoryAllocator = configuration.MemoryAllocator; this.options = options; } + /// + public Configuration Configuration { get; } + /// /// Gets the dimensions of the image. /// public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height); - /// - /// Decodes the image from the specified stream. - /// - /// The pixel format. - /// The stream, where the image should be decoded from. Cannot be null. - /// - /// is null. - /// - /// The decoded image. - public async Task> DecodeAsync(Stream stream) - where TPixel : unmanaged, IPixel - { - if (stream.CanSeek) - { - return this.Decode(stream); - } - else - { - using (var ms = new MemoryStream()) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Decode(ms); - } - } - } - - /// - /// Decodes the image from the specified stream. - /// - /// The pixel format. - /// The stream, where the image should be decoded from. Cannot be null. - /// - /// is null. - /// - /// The decoded image. + /// public Image Decode(Stream stream) - where TPixel : unmanaged, IPixel + where TPixel : unmanaged, IPixel { try { @@ -135,7 +97,7 @@ public Image Decode(Stream stream) throw new UnknownImageFormatException("Width or height cannot be 0"); } - var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); + var image = Image.CreateUninitialized(this.Configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata); Buffer2D pixels = image.GetRootFramePixelBuffer(); if (this.fileHeader.ColorMapType == 1) @@ -489,11 +451,11 @@ private void ReadBgra16(int width, int height, Buffer2D pixels, if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { - PixelOperations.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width); + PixelOperations.Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width); } else { - PixelOperations.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width); + PixelOperations.Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width); } } } @@ -678,31 +640,7 @@ private void ReadRle(int width, int height, Buffer2D pixels, int } } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public async Task IdentifyAsync(Stream stream) - { - if (stream.CanSeek) - { - return this.Identify(stream); - } - else - { - using (var ms = new FixedCapacityPooledMemoryStream(stream.Length)) - { - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return this.Identify(ms); - } - } - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. + /// public IImageInfo Identify(Stream stream) { this.ReadFileHeader(stream); @@ -719,7 +657,7 @@ private void ReadL8Row(int width, Buffer2D pixels, IManagedByteB { this.currentStream.Read(row); Span pixelSpan = pixels.GetRowSpan(y); - PixelOperations.Instance.FromL8Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + PixelOperations.Instance.FromL8Bytes(this.Configuration, row.GetSpan(), pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -746,7 +684,7 @@ private void ReadBgr24Row(int width, Buffer2D pixels, IManagedBy { this.currentStream.Read(row); Span pixelSpan = pixels.GetRowSpan(y); - PixelOperations.Instance.FromBgr24Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + PixelOperations.Instance.FromBgr24Bytes(this.Configuration, row.GetSpan(), pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -765,7 +703,7 @@ private void ReadBgra32Row(int width, Buffer2D pixels, IManagedB { this.currentStream.Read(row); Span pixelSpan = pixels.GetRowSpan(y); - PixelOperations.Instance.FromBgra32Bytes(this.configuration, row.GetSpan(), pixelSpan, width); + PixelOperations.Instance.FromBgra32Bytes(this.Configuration, row.GetSpan(), pixelSpan, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs b/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs index 878fcc53a7..6953b0e102 100644 --- a/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs +++ b/src/ImageSharp/IO/FixedCapacityPooledMemoryStream.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.IO; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.IO { @@ -11,18 +12,19 @@ namespace SixLabors.ImageSharp.IO /// internal sealed class FixedCapacityPooledMemoryStream : MemoryStream { - private readonly byte[] buffer; + private readonly IManagedByteBuffer buffer; private bool isDisposed; /// /// Initializes a new instance of the class. /// /// The length of the stream buffer to rent. - public FixedCapacityPooledMemoryStream(long length) - : this(RentBuffer(length)) => this.Length = length; + /// The allocator to rent the buffer from. + public FixedCapacityPooledMemoryStream(long length, MemoryAllocator allocator) + : this(RentBuffer(length, allocator)) => this.Length = length; - private FixedCapacityPooledMemoryStream(byte[] buffer) - : base(buffer) => this.buffer = buffer; + private FixedCapacityPooledMemoryStream(IManagedByteBuffer buffer) + : base(buffer.Array) => this.buffer = buffer; /// public override long Length { get; } @@ -36,7 +38,7 @@ protected override void Dispose(bool disposing) if (disposing) { - ArrayPool.Shared.Return(this.buffer); + this.buffer.Dispose(); } base.Dispose(disposing); @@ -45,6 +47,10 @@ protected override void Dispose(bool disposing) // In the extrememly unlikely event someone ever gives us a stream // with length longer than int.MaxValue then we'll use something else. - private static byte[] RentBuffer(long length) => ArrayPool.Shared.Rent((int)length); + private static IManagedByteBuffer RentBuffer(long length, MemoryAllocator allocator) + { + Guard.MustBeBetweenOrEqualTo(length, 0, int.MaxValue, nameof(length)); + return allocator.AllocateManagedByteBuffer((int)length); + } } } diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 499f5ac195..d3fd35d5fc 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp @@ -675,7 +676,7 @@ private static T WithSeekableStream(Configuration configuration, Stream strea } // We want to be able to load images from things like HttpContext.Request.Body - using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length)) + using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length)) { stream.CopyTo(memoryStream); memoryStream.Position = 0; @@ -711,7 +712,7 @@ private static async Task WithSeekableStreamAsync( return await action(stream).ConfigureAwait(false); } - using (var memoryStream = new FixedCapacityPooledMemoryStream(stream.Length)) + using (MemoryStream memoryStream = configuration.MemoryAllocator.AllocateFixedCapacityMemoryStream(stream.Length)) { await stream.CopyToAsync(memoryStream).ConfigureAwait(false); memoryStream.Position = 0; diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index 45463c76fb..9a56390d89 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Buffers; +using System.IO; +using SixLabors.ImageSharp.IO; namespace SixLabors.ImageSharp.Memory { @@ -98,5 +100,8 @@ internal static MemoryGroup AllocateGroup( AllocationOptions options = AllocationOptions.None) where T : struct => MemoryGroup.Allocate(memoryAllocator, totalLength, bufferAlignment, options); + + internal static MemoryStream AllocateFixedCapacityMemoryStream(this MemoryAllocator allocator, long length) => + new FixedCapacityPooledMemoryStream(length, allocator); } }