Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mono AOT decoder workaround for slow jpeg decoding. #2762

Open
wants to merge 5 commits into
base: release/3.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions src/ImageSharp/Advanced/AotCompilerTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
Expand Down Expand Up @@ -129,6 +132,7 @@ private static void Seed<TPixel>()
AotCompileImageDecoderInternals<TPixel>();
AotCompileImageEncoders<TPixel>();
AotCompileImageDecoders<TPixel>();
AotCompileSpectralConverter<TPixel>();
AotCompileImageProcessors<TPixel>();
AotCompileGenericImageProcessors<TPixel>();
AotCompileResamplers<TPixel>();
Expand Down Expand Up @@ -195,39 +199,41 @@ private static void AotCompileImageProcessingContextFactory<TPixel>()
=> default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext<TPixel>(default, default, default);

/// <summary>
/// This method pre-seeds the all <see cref="IImageEncoderInternals"/> in the AoT compiler.
/// This method pre-seeds the all core encoders in the AoT compiler.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve]
private static void AotCompileImageEncoderInternals<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
default(BmpEncoderCore).Encode<TPixel>(default, default, default);
default(GifEncoderCore).Encode<TPixel>(default, default, default);
default(JpegEncoderCore).Encode<TPixel>(default, default, default);
default(PbmEncoderCore).Encode<TPixel>(default, default, default);
default(PngEncoderCore).Encode<TPixel>(default, default, default);
default(QoiEncoderCore).Encode<TPixel>(default, default, default);
default(TgaEncoderCore).Encode<TPixel>(default, default, default);
default(TiffEncoderCore).Encode<TPixel>(default, default, default);
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
}

/// <summary>
/// This method pre-seeds the all <see cref="IImageDecoderInternals"/> in the AoT compiler.
/// This method pre-seeds the all <see cref="ImageDecoderCore"/> in the AoT compiler.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
[Preserve]
private static void AotCompileImageDecoderInternals<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
default(WebpDecoderCore).Decode<TPixel>(default, default);
default(BmpDecoderCore).Decode<TPixel>(default, default);
default(GifDecoderCore).Decode<TPixel>(default, default);
default(JpegDecoderCore).Decode<TPixel>(default, default);
default(PbmDecoderCore).Decode<TPixel>(default, default);
default(PngDecoderCore).Decode<TPixel>(default, default);
default(TgaDecoderCore).Decode<TPixel>(default, default);
default(TiffDecoderCore).Decode<TPixel>(default, default);
default(BmpDecoderCore).Decode<TPixel>(default, default, default);
default(GifDecoderCore).Decode<TPixel>(default, default, default);
default(JpegDecoderCore).Decode<TPixel>(default, default, default);
default(PbmDecoderCore).Decode<TPixel>(default, default, default);
default(PngDecoderCore).Decode<TPixel>(default, default, default);
default(QoiDecoderCore).Decode<TPixel>(default, default, default);
default(TgaDecoderCore).Decode<TPixel>(default, default, default);
default(TiffDecoderCore).Decode<TPixel>(default, default, default);
default(WebpDecoderCore).Decode<TPixel>(default, default, default);
}

/// <summary>
Expand Down Expand Up @@ -266,6 +272,17 @@ private static void AotCompileImageDecoders<TPixel>()
AotCompileImageDecoder<TPixel, TiffDecoder>();
}

[Preserve]
private static void AotCompileSpectralConverter<TPixel>()
where TPixel : unmanaged, IPixel<TPixel>
{
default(SpectralConverter<TPixel>).GetPixelBuffer(default);
default(GrayJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(RgbJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(TiffJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
default(TiffOldJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
}

/// <summary>
/// This method pre-seeds the <see cref="IImageEncoder"/> in the AoT compiler.
/// </summary>
Expand Down
17 changes: 6 additions & 11 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
/// </remarks>
internal sealed class BmpDecoderCore : IImageDecoderInternals
internal sealed class BmpDecoderCore : ImageDecoderCore
{
/// <summary>
/// The default mask for the red part of the color for 16 bit rgb bitmaps.
Expand Down Expand Up @@ -104,22 +104,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
/// </summary>
/// <param name="options">The options.</param>
public BmpDecoderCore(BmpDecoderOptions options)
: base(options.GeneralOptions)
{
this.Options = options.GeneralOptions;
this.rleSkippedPixelHandling = options.RleSkippedPixelHandling;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}

/// <inheritdoc />
public DecoderOptions Options { get; }

/// <inheritdoc />
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);

/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
{
Image<TPixel>? image = null;
try
Expand Down Expand Up @@ -205,7 +198,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
}

/// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ReadImageHeaders(stream, out _, out _);
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), new(this.infoHeader.Width, this.infoHeader.Height), this.metadata);
Expand Down Expand Up @@ -1369,6 +1362,8 @@ private void ReadInfoHeader(BufferedReadStream stream)
this.bmpMetadata = this.metadata.GetBmpMetadata();
this.bmpMetadata.InfoHeaderType = infoHeaderType;
this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;

this.Dimensions = new(this.infoHeader.Width, this.infoHeader.Height);
}

/// <summary>
Expand Down
3 changes: 1 addition & 2 deletions src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
Expand All @@ -17,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary>
internal sealed class BmpEncoderCore : IImageEncoderInternals
internal sealed class BmpEncoderCore
{
/// <summary>
/// The amount to pad each row by.
Expand Down
17 changes: 6 additions & 11 deletions src/ImageSharp/Formats/Gif/GifDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary>
/// Performs the gif decoding operation.
/// </summary>
internal sealed class GifDecoderCore : IImageDecoderInternals
internal sealed class GifDecoderCore : ImageDecoderCore
{
/// <summary>
/// The temp buffer used to reduce allocations.
Expand Down Expand Up @@ -94,23 +94,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// </summary>
/// <param name="options">The decoder options.</param>
public GifDecoderCore(DecoderOptions options)
: base(options)
{
this.Options = options;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator;
}

/// <inheritdoc />
public DecoderOptions Options { get; }

/// <inheritdoc />
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);

/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
{
uint frameCount = 0;
Image<TPixel>? image = null;
Expand Down Expand Up @@ -181,7 +174,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
}

/// <inheritdoc />
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
uint frameCount = 0;
ImageFrameMetadata? previousFrame = null;
Expand Down Expand Up @@ -287,6 +280,8 @@ private void ReadImageDescriptor(BufferedReadStream stream)
{
GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0");
}

this.Dimensions = new(this.imageDescriptor.Width, this.imageDescriptor.Height);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Gif/GifEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
/// <summary>
/// Implements the GIF encoding protocol.
/// </summary>
internal sealed class GifEncoderCore : IImageEncoderInternals
internal sealed class GifEncoderCore
{
/// <summary>
/// Used for allocating memory during processing operations.
Expand Down
50 changes: 0 additions & 50 deletions src/ImageSharp/Formats/IImageDecoderInternals.cs

This file was deleted.

22 changes: 0 additions & 22 deletions src/ImageSharp/Formats/IImageEncoderInternals.cs

This file was deleted.

14 changes: 7 additions & 7 deletions src/ImageSharp/Formats/ImageDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ private static bool ShouldResize(DecoderOptions options, Image image)
throw new NotSupportedException("Cannot read from the stream.");
}

T PeformActionAndResetPosition(Stream s, long position)
T PerformActionAndResetPosition(Stream s, long position)
{
T result = action(s);

Expand All @@ -206,7 +206,7 @@ T PeformActionAndResetPosition(Stream s, long position)

if (stream.CanSeek)
{
return PeformActionAndResetPosition(stream, stream.Position);
return PerformActionAndResetPosition(stream, stream.Position);
}

Configuration configuration = options.Configuration;
Expand All @@ -231,7 +231,7 @@ T PeformActionAndResetPosition(Stream s, long position)
throw new NotSupportedException("Cannot read from the stream.");
}

Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken ct)
Task<T> PerformActionAndResetPosition(Stream s, long position, CancellationToken ct)
{
try
{
Expand Down Expand Up @@ -263,15 +263,15 @@ Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken
// code below to copy the stream to an in-memory buffer before invoking the action.
if (stream is MemoryStream ms)
{
return PeformActionAndResetPosition(ms, ms.Position, cancellationToken);
return PerformActionAndResetPosition(ms, ms.Position, cancellationToken);
}

if (stream is ChunkedMemoryStream cms)
{
return PeformActionAndResetPosition(cms, cms.Position, cancellationToken);
return PerformActionAndResetPosition(cms, cms.Position, cancellationToken);
}

return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken);
return CopyToMemoryStreamAndActionAsync(options, stream, PerformActionAndResetPosition, cancellationToken);
}

private static async Task<T> CopyToMemoryStreamAndActionAsync<T>(
Expand All @@ -282,7 +282,7 @@ Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken
{
long position = stream.CanSeek ? stream.Position : 0;
Configuration configuration = options.Configuration;
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
await using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
return await action(memoryStream, position, cancellationToken).ConfigureAwait(false);
Expand Down
Loading
Loading