Skip to content

Commit aad5cfa

Browse files
Merge pull request #2762 from SixLabors/js/mono-aot-decoder-workaround
Mono AOT decoder workaround for slow jpeg decoding.
2 parents 65bd9b5 + 53cf942 commit aad5cfa

28 files changed

+251
-305
lines changed

src/ImageSharp/Advanced/AotCompilerTools.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
using SixLabors.ImageSharp.Formats.Gif;
1111
using SixLabors.ImageSharp.Formats.Jpeg;
1212
using SixLabors.ImageSharp.Formats.Jpeg.Components;
13+
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
1314
using SixLabors.ImageSharp.Formats.Pbm;
1415
using SixLabors.ImageSharp.Formats.Png;
16+
using SixLabors.ImageSharp.Formats.Qoi;
1517
using SixLabors.ImageSharp.Formats.Tga;
1618
using SixLabors.ImageSharp.Formats.Tiff;
19+
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
1720
using SixLabors.ImageSharp.Formats.Webp;
1821
using SixLabors.ImageSharp.Memory;
1922
using SixLabors.ImageSharp.PixelFormats;
@@ -129,6 +132,7 @@ private static void Seed<TPixel>()
129132
AotCompileImageDecoderInternals<TPixel>();
130133
AotCompileImageEncoders<TPixel>();
131134
AotCompileImageDecoders<TPixel>();
135+
AotCompileSpectralConverter<TPixel>();
132136
AotCompileImageProcessors<TPixel>();
133137
AotCompileGenericImageProcessors<TPixel>();
134138
AotCompileResamplers<TPixel>();
@@ -195,39 +199,41 @@ private static void AotCompileImageProcessingContextFactory<TPixel>()
195199
=> default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext<TPixel>(default, default, default);
196200

197201
/// <summary>
198-
/// This method pre-seeds the all <see cref="IImageEncoderInternals"/> in the AoT compiler.
202+
/// This method pre-seeds the all core encoders in the AoT compiler.
199203
/// </summary>
200204
/// <typeparam name="TPixel">The pixel format.</typeparam>
201205
[Preserve]
202206
private static void AotCompileImageEncoderInternals<TPixel>()
203207
where TPixel : unmanaged, IPixel<TPixel>
204208
{
205-
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
206209
default(BmpEncoderCore).Encode<TPixel>(default, default, default);
207210
default(GifEncoderCore).Encode<TPixel>(default, default, default);
208211
default(JpegEncoderCore).Encode<TPixel>(default, default, default);
209212
default(PbmEncoderCore).Encode<TPixel>(default, default, default);
210213
default(PngEncoderCore).Encode<TPixel>(default, default, default);
214+
default(QoiEncoderCore).Encode<TPixel>(default, default, default);
211215
default(TgaEncoderCore).Encode<TPixel>(default, default, default);
212216
default(TiffEncoderCore).Encode<TPixel>(default, default, default);
217+
default(WebpEncoderCore).Encode<TPixel>(default, default, default);
213218
}
214219

215220
/// <summary>
216-
/// This method pre-seeds the all <see cref="IImageDecoderInternals"/> in the AoT compiler.
221+
/// This method pre-seeds the all <see cref="ImageDecoderCore"/> in the AoT compiler.
217222
/// </summary>
218223
/// <typeparam name="TPixel">The pixel format.</typeparam>
219224
[Preserve]
220225
private static void AotCompileImageDecoderInternals<TPixel>()
221226
where TPixel : unmanaged, IPixel<TPixel>
222227
{
223-
default(WebpDecoderCore).Decode<TPixel>(default, default);
224-
default(BmpDecoderCore).Decode<TPixel>(default, default);
225-
default(GifDecoderCore).Decode<TPixel>(default, default);
226-
default(JpegDecoderCore).Decode<TPixel>(default, default);
227-
default(PbmDecoderCore).Decode<TPixel>(default, default);
228-
default(PngDecoderCore).Decode<TPixel>(default, default);
229-
default(TgaDecoderCore).Decode<TPixel>(default, default);
230-
default(TiffDecoderCore).Decode<TPixel>(default, default);
228+
default(BmpDecoderCore).Decode<TPixel>(default, default, default);
229+
default(GifDecoderCore).Decode<TPixel>(default, default, default);
230+
default(JpegDecoderCore).Decode<TPixel>(default, default, default);
231+
default(PbmDecoderCore).Decode<TPixel>(default, default, default);
232+
default(PngDecoderCore).Decode<TPixel>(default, default, default);
233+
default(QoiDecoderCore).Decode<TPixel>(default, default, default);
234+
default(TgaDecoderCore).Decode<TPixel>(default, default, default);
235+
default(TiffDecoderCore).Decode<TPixel>(default, default, default);
236+
default(WebpDecoderCore).Decode<TPixel>(default, default, default);
231237
}
232238

233239
/// <summary>
@@ -266,6 +272,17 @@ private static void AotCompileImageDecoders<TPixel>()
266272
AotCompileImageDecoder<TPixel, TiffDecoder>();
267273
}
268274

275+
[Preserve]
276+
private static void AotCompileSpectralConverter<TPixel>()
277+
where TPixel : unmanaged, IPixel<TPixel>
278+
{
279+
default(SpectralConverter<TPixel>).GetPixelBuffer(default);
280+
default(GrayJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
281+
default(RgbJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
282+
default(TiffJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
283+
default(TiffOldJpegSpectralConverter<TPixel>).GetPixelBuffer(default);
284+
}
285+
269286
/// <summary>
270287
/// This method pre-seeds the <see cref="IImageEncoder"/> in the AoT compiler.
271288
/// </summary>

src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
2121
/// <remarks>
2222
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
2323
/// </remarks>
24-
internal sealed class BmpDecoderCore : IImageDecoderInternals
24+
internal sealed class BmpDecoderCore : ImageDecoderCore
2525
{
2626
/// <summary>
2727
/// The default mask for the red part of the color for 16 bit rgb bitmaps.
@@ -104,22 +104,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals
104104
/// </summary>
105105
/// <param name="options">The options.</param>
106106
public BmpDecoderCore(BmpDecoderOptions options)
107+
: base(options.GeneralOptions)
107108
{
108-
this.Options = options.GeneralOptions;
109109
this.rleSkippedPixelHandling = options.RleSkippedPixelHandling;
110110
this.configuration = options.GeneralOptions.Configuration;
111111
this.memoryAllocator = this.configuration.MemoryAllocator;
112112
}
113113

114114
/// <inheritdoc />
115-
public DecoderOptions Options { get; }
116-
117-
/// <inheritdoc />
118-
public Size Dimensions => new(this.infoHeader.Width, this.infoHeader.Height);
119-
120-
/// <inheritdoc />
121-
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
122-
where TPixel : unmanaged, IPixel<TPixel>
115+
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
123116
{
124117
Image<TPixel>? image = null;
125118
try
@@ -205,7 +198,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
205198
}
206199

207200
/// <inheritdoc />
208-
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
201+
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
209202
{
210203
this.ReadImageHeaders(stream, out _, out _);
211204
return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), new(this.infoHeader.Width, this.infoHeader.Height), this.metadata);
@@ -1369,6 +1362,8 @@ private void ReadInfoHeader(BufferedReadStream stream)
13691362
this.bmpMetadata = this.metadata.GetBmpMetadata();
13701363
this.bmpMetadata.InfoHeaderType = infoHeaderType;
13711364
this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
1365+
1366+
this.Dimensions = new(this.infoHeader.Width, this.infoHeader.Height);
13721367
}
13731368

13741369
/// <summary>

src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Buffers;
55
using System.Buffers.Binary;
66
using System.Runtime.InteropServices;
7-
using SixLabors.ImageSharp.Advanced;
87
using SixLabors.ImageSharp.Common.Helpers;
98
using SixLabors.ImageSharp.Memory;
109
using SixLabors.ImageSharp.Metadata;
@@ -17,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
1716
/// <summary>
1817
/// Image encoder for writing an image to a stream as a Windows bitmap.
1918
/// </summary>
20-
internal sealed class BmpEncoderCore : IImageEncoderInternals
19+
internal sealed class BmpEncoderCore
2120
{
2221
/// <summary>
2322
/// The amount to pad each row by.

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
1717
/// <summary>
1818
/// Performs the gif decoding operation.
1919
/// </summary>
20-
internal sealed class GifDecoderCore : IImageDecoderInternals
20+
internal sealed class GifDecoderCore : ImageDecoderCore
2121
{
2222
/// <summary>
2323
/// The temp buffer used to reduce allocations.
@@ -94,23 +94,16 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
9494
/// </summary>
9595
/// <param name="options">The decoder options.</param>
9696
public GifDecoderCore(DecoderOptions options)
97+
: base(options)
9798
{
98-
this.Options = options;
9999
this.configuration = options.Configuration;
100100
this.skipMetadata = options.SkipMetadata;
101101
this.maxFrames = options.MaxFrames;
102102
this.memoryAllocator = this.configuration.MemoryAllocator;
103103
}
104104

105105
/// <inheritdoc />
106-
public DecoderOptions Options { get; }
107-
108-
/// <inheritdoc />
109-
public Size Dimensions => new(this.imageDescriptor.Width, this.imageDescriptor.Height);
110-
111-
/// <inheritdoc />
112-
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
113-
where TPixel : unmanaged, IPixel<TPixel>
106+
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
114107
{
115108
uint frameCount = 0;
116109
Image<TPixel>? image = null;
@@ -181,7 +174,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
181174
}
182175

183176
/// <inheritdoc />
184-
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
177+
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
185178
{
186179
uint frameCount = 0;
187180
ImageFrameMetadata? previousFrame = null;
@@ -287,6 +280,8 @@ private void ReadImageDescriptor(BufferedReadStream stream)
287280
{
288281
GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0");
289282
}
283+
284+
this.Dimensions = new(this.imageDescriptor.Width, this.imageDescriptor.Height);
290285
}
291286

292287
/// <summary>

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
1919
/// <summary>
2020
/// Implements the GIF encoding protocol.
2121
/// </summary>
22-
internal sealed class GifEncoderCore : IImageEncoderInternals
22+
internal sealed class GifEncoderCore
2323
{
2424
/// <summary>
2525
/// Used for allocating memory during processing operations.

src/ImageSharp/Formats/IImageDecoderInternals.cs

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/ImageSharp/Formats/IImageEncoderInternals.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/ImageSharp/Formats/ImageDecoder.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ internal static T WithSeekableStream<T>(
189189
throw new NotSupportedException("Cannot read from the stream.");
190190
}
191191

192-
T PeformActionAndResetPosition(Stream s, long position)
192+
T PerformActionAndResetPosition(Stream s, long position)
193193
{
194194
T result = action(s);
195195

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

207207
if (stream.CanSeek)
208208
{
209-
return PeformActionAndResetPosition(stream, stream.Position);
209+
return PerformActionAndResetPosition(stream, stream.Position);
210210
}
211211

212212
Configuration configuration = options.Configuration;
@@ -231,7 +231,7 @@ internal static Task<T> WithSeekableMemoryStreamAsync<T>(
231231
throw new NotSupportedException("Cannot read from the stream.");
232232
}
233233

234-
Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken ct)
234+
Task<T> PerformActionAndResetPosition(Stream s, long position, CancellationToken ct)
235235
{
236236
try
237237
{
@@ -263,15 +263,15 @@ Task<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken
263263
// code below to copy the stream to an in-memory buffer before invoking the action.
264264
if (stream is MemoryStream ms)
265265
{
266-
return PeformActionAndResetPosition(ms, ms.Position, cancellationToken);
266+
return PerformActionAndResetPosition(ms, ms.Position, cancellationToken);
267267
}
268268

269269
if (stream is ChunkedMemoryStream cms)
270270
{
271-
return PeformActionAndResetPosition(cms, cms.Position, cancellationToken);
271+
return PerformActionAndResetPosition(cms, cms.Position, cancellationToken);
272272
}
273273

274-
return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken);
274+
return CopyToMemoryStreamAndActionAsync(options, stream, PerformActionAndResetPosition, cancellationToken);
275275
}
276276

277277
private static async Task<T> CopyToMemoryStreamAndActionAsync<T>(
@@ -282,7 +282,7 @@ private static async Task<T> CopyToMemoryStreamAndActionAsync<T>(
282282
{
283283
long position = stream.CanSeek ? stream.Position : 0;
284284
Configuration configuration = options.Configuration;
285-
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
285+
await using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
286286
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
287287
memoryStream.Position = 0;
288288
return await action(memoryStream, position, cancellationToken).ConfigureAwait(false);

0 commit comments

Comments
 (0)