diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 2ea456286f..ea4cd1c8c4 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -6,11 +6,26 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Tga; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Processing.Processors.Binarization; +using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Processing.Processors.Effects; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors.Normalization; +using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Quantization; +using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Advanced { @@ -25,180 +40,510 @@ namespace SixLabors.ImageSharp.Advanced [ExcludeFromCodeCoverage] internal static class AotCompilerTools { - static AotCompilerTools() - { - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); - } - /// /// This is the method that seeds the AoT compiler. /// None of these seed methods needs to actually be called to seed the compiler. /// The calls just need to be present when the code is compiled, and each implementation will be built. /// + /// + /// This method doesn't actually do anything but serves an important purpose... + /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: + /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." + /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT + /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on + /// iOS so it bombs out. + /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the + /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! + /// + [Preserve] private static void SeedEverything() { - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); - Seed(); + try + { + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + Unsafe.SizeOf(); + + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + Seed(); + } + catch + { + // nop + } + + throw new InvalidOperationException("This method is used for AOT code generation only. Do not call it at runtime."); } /// /// Seeds the compiler using the given pixel format. /// /// The pixel format. + [Preserve] private static void Seed() where TPixel : unmanaged, IPixel { // This is we actually call all the individual methods you need to seed. - AotCompileOctreeQuantizer(); - AotCompileWuQuantizer(); - AotCompilePaletteQuantizer(); - AotCompileDithering(); - AotCompilePixelOperations(); + AotCompileImage(); + AotCompileImageProcessingContextFactory(); + AotCompileImageEncoderInternals(); + AotCompileImageDecoderInternals(); + AotCompileImageEncoders(); + AotCompileImageDecoders(); + AotCompileImageProcessors(); + AotCompileGenericImageProcessors(); + AotCompileResamplers(); + AotCompileQuantizers(); + AotCompilePixelSamplingStrategys(); + AotCompileDithers(); + AotCompileMemoryManagers(); Unsafe.SizeOf(); - AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder()); - AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder()); - AotCodec(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder()); - AotCodec(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder()); - // TODO: Do the discovery work to figure out what works and what doesn't. } /// - /// This method doesn't actually do anything but serves an important purpose... - /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception: - /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." - /// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT - /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on - /// iOS so it bombs out. - /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the - /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! + /// This method pre-seeds the for a given pixel format in the AoT compiler. /// /// The pixel format. - private static void AotCompileOctreeQuantizer() + [Preserve] + private static unsafe void AotCompileImage() where TPixel : unmanaged, IPixel { - using (var test = new OctreeQuantizer(Configuration.Default, new OctreeQuantizer().Options)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + Image img = default; + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + img.CloneAs(default); + + ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); + ImageFrame.LoadPixelData(default, default(ReadOnlySpan), default, default); } /// - /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. - private static void AotCompileWuQuantizer() + [Preserve] + private static void AotCompileImageProcessingContextFactory() + where TPixel : unmanaged, IPixel + => default(DefaultImageOperationsProviderFactory).CreateImageProcessingContext(default, default, default); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileImageEncoderInternals() where TPixel : unmanaged, IPixel { - using (var test = new WuQuantizer(Configuration.Default, new WuQuantizer().Options)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + default(BmpEncoderCore).Encode(default, default, default); + default(GifEncoderCore).Encode(default, default, default); + default(JpegEncoderCore).Encode(default, default, default); + default(PngEncoderCore).Encode(default, default, default); + default(TgaEncoderCore).Encode(default, default, default); } /// - /// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. - private static void AotCompilePaletteQuantizer() + [Preserve] + private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { - using (var test = (PaletteQuantizer)new PaletteQuantizer(Array.Empty()).CreatePixelSpecificQuantizer(Configuration.Default)) - { - var frame = new ImageFrame(Configuration.Default, 1, 1); - test.QuantizeFrame(frame, frame.Bounds()); - } + default(BmpDecoderCore).Decode(default, default, default); + default(GifDecoderCore).Decode(default, default, default); + default(JpegDecoderCore).Decode(default, default, default); + default(PngDecoderCore).Decode(default, default, default); + default(TgaDecoderCore).Decode(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileImageEncoders() + where TPixel : unmanaged, IPixel + { + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); + AotCompileImageEncoder(); } /// - /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS. + /// This method pre-seeds the all in the AoT compiler. /// /// The pixel format. - private static void AotCompileDithering() + [Preserve] + private static void AotCompileImageDecoders() where TPixel : unmanaged, IPixel { - ErrorDither errorDither = ErrorDither.FloydSteinberg; - OrderedDither orderedDither = OrderedDither.Bayer2x2; - TPixel pixel = default; - using (var image = new ImageFrame(Configuration.Default, 1, 1)) - { - errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0); - orderedDither.Dither(pixel, 0, 0, 0, 0); - } + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); + AotCompileImageDecoder(); } /// - /// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS. + /// This method pre-seeds the in the AoT compiler. /// - /// The image decoder to seed. - /// The image encoder to seed. /// The pixel format. - private static void AotCodec(IImageDecoder decoder, IImageEncoder encoder) + /// The encoder. + [Preserve] + private static void AotCompileImageEncoder() where TPixel : unmanaged, IPixel + where TEncoder : class, IImageEncoder { - try - { - decoder.Decode(Configuration.Default, null); - } - catch - { - } + default(TEncoder).Encode(default, default); + default(TEncoder).EncodeAsync(default, default, default); + } - try - { - encoder.Encode(null, null); - } - catch - { - } + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The decoder. + [Preserve] + private static void AotCompileImageDecoder() + where TPixel : unmanaged, IPixel + where TDecoder : class, IImageDecoder + { + default(TDecoder).Decode(default, default); + default(TDecoder).DecodeAsync(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// + /// There is no structure that implements ISwizzler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileImageProcessors() + where TPixel : unmanaged, IPixel + { + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + AotCompileImageProcessor(); + + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + AotCompilerCloningImageProcessor(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + [Preserve] + private static void AotCompileImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, IImageProcessor + => default(TProc).CreatePixelSpecificProcessor(default, default, default); + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + [Preserve] + private static void AotCompilerCloningImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, ICloningImageProcessor + => default(TProc).CreatePixelSpecificCloningProcessor(default, default, default); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// + /// There is no structure that implements ISwizzler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileGenericImageProcessors() + where TPixel : unmanaged, IPixel + { + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + AotCompileGenericCloningImageProcessor>(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + [Preserve] + private static void AotCompileGenericCloningImageProcessor() + where TPixel : unmanaged, IPixel + where TProc : class, ICloningImageProcessor + => default(TProc).CloneAndExecute(); + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileResamplers() + where TPixel : unmanaged, IPixel + { + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + AotCompileResampler(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The processor type + [Preserve] + private static void AotCompileResampler() + where TPixel : unmanaged, IPixel + where TResampler : struct, IResampler + { + default(TResampler).ApplyTransform(default); + + default(AffineTransformProcessor).ApplyTransform(default); + default(ProjectiveTransformProcessor).ApplyTransform(default); + default(ResizeProcessor).ApplyTransform(default); + default(RotateProcessor).ApplyTransform(default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileQuantizers() + where TPixel : unmanaged, IPixel + { + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + AotCompileQuantizer(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The quantizer type + [Preserve] + private static void AotCompileQuantizer() + where TPixel : unmanaged, IPixel + + where TQuantizer : class, IQuantizer + { + default(TQuantizer).CreatePixelSpecificQuantizer(default); + default(TQuantizer).CreatePixelSpecificQuantizer(default, default); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompilePixelSamplingStrategys() + where TPixel : unmanaged, IPixel + { + default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default); + default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileDithers() + where TPixel : unmanaged, IPixel + { + AotCompileDither(); + AotCompileDither(); + } + + /// + /// This method pre-seeds the in the AoT compiler. + /// + /// The pixel format. + /// The dither. + [Preserve] + private static void AotCompileDither() + where TPixel : unmanaged, IPixel + where TDither : struct, IDither + { + var octree = default(OctreeQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref octree, default, default, default); + + var palette = default(PaletteQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref palette, default, default, default); + + var wu = default(WuQuantizer); + default(TDither).ApplyQuantizationDither, TPixel>(ref wu, default, default, default); + default(TDither).ApplyPaletteDither.DitherProcessor, TPixel>(default, default, default); + } + + /// + /// This method pre-seeds the all in the AoT compiler. + /// + /// The pixel format. + [Preserve] + private static void AotCompileMemoryManagers() + where TPixel : unmanaged, IPixel + { + AotCompileMemoryManager(); + AotCompileMemoryManager(); } /// - /// This method pre-seeds the PixelOperations engine for the AoT compiler on iOS. + /// This method pre-seeds the in the AoT compiler. /// /// The pixel format. - private static void AotCompilePixelOperations() + /// The buffer. + [Preserve] + private static void AotCompileMemoryManager() where TPixel : unmanaged, IPixel + where TBuffer : MemoryAllocator { - var pixelOp = new PixelOperations(); - pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); + default(TBuffer).Allocate(default, default); } } } diff --git a/src/ImageSharp/Advanced/PreserveAttribute.cs b/src/ImageSharp/Advanced/PreserveAttribute.cs new file mode 100644 index 0000000000..a16b30e235 --- /dev/null +++ b/src/ImageSharp/Advanced/PreserveAttribute.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// This is necessary to avoid being excluded from compilation in environments that do AOT builds, such as Unity's IL2CPP and Xamarin. + /// The only thing that matters is the class name. + /// There is no need to use or inherit from the PreserveAttribute class in each environment. + /// + internal sealed class PreserveAttribute : System.Attribute + { + } +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 789d02046e..4631cd4229 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -72,7 +72,8 @@ protected override void Dispose(bool disposing) /// Used to allow inlining of calls to /// . /// - private readonly struct DitherProcessor : IPaletteDitherImageProcessor + /// Internal for AOT + internal readonly struct DitherProcessor : IPaletteDitherImageProcessor { private readonly EuclideanPixelMap pixelMap;