diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
index 487c880644..266d842bfa 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
@@ -139,10 +139,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
fistRow = -startY;
}
- int end = operation.Map.Height;
-
int maxHeight = source.Height - startY;
- end = Math.Min(end, maxHeight);
+ int end = Math.Min(operation.Map.Height, maxHeight);
for (int row = fistRow; row < end; row++)
{
diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
new file mode 100644
index 0000000000..9e7624480d
--- /dev/null
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -0,0 +1,103 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Dithering;
+using SixLabors.ImageSharp.Processing.Processors.Quantization;
+
+namespace SixLabors.ImageSharp.Advanced
+{
+ ///
+ /// Unlike traditional Mono/.NET, code on the iPhone is statically compiled ahead of time instead of being
+ /// compiled on demand by a JIT compiler. This means there are a few limitations with respect to generics,
+ /// these are caused because not every possible generic instantiation can be determined up front at compile time.
+ /// The Aot Compiler is designed to overcome the limitations of this compiler.
+ ///
+ public static class AotCompilerTools
+ {
+ ///
+ /// Seeds the compiler using the given pixel format.
+ ///
+ /// The pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ {
+ // This is we actually call all the individual methods you need to seed.
+ AotCompileOctreeQuantizer();
+ AotCompileWuQuantizer();
+ AotCompileDithering();
+
+ // TODO: Do the discovery work to figure out what works and what doesn't.
+ }
+
+ ///
+ /// Seeds the compiler using the given pixel formats.
+ ///
+ /// The first pixel format.
+ /// The second pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ where TPixel2 : struct, IPixel
+ {
+ Seed();
+ Seed();
+ }
+
+ ///
+ /// Seeds the compiler using the given pixel formats.
+ ///
+ /// The first pixel format.
+ /// The second pixel format.
+ /// The third pixel format.
+ public static void Seed()
+ where TPixel : struct, IPixel
+ where TPixel2 : struct, IPixel
+ where TPixel3 : struct, IPixel
+ {
+ Seed();
+ Seed();
+ }
+
+ ///
+ /// 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 excepion:
+ /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode."
+ /// The reason this happens is the SaveAsGif method makes haevy 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!!!
+ ///
+ /// The pixel format.
+ private static void AotCompileOctreeQuantizer()
+ where TPixel : struct, IPixel
+ {
+ var test = new OctreeFrameQuantizer(new OctreeQuantizer(false));
+ test.AotGetPalette();
+ }
+
+ ///
+ /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS.
+ ///
+ /// The pixel format.
+ private static void AotCompileWuQuantizer()
+ where TPixel : struct, IPixel
+ {
+ var test = new WuFrameQuantizer(new WuQuantizer(false));
+ test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1));
+ test.AotGetPalette();
+ }
+
+ ///
+ /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS.
+ ///
+ /// The pixel format.
+ private static void AotCompileDithering()
+ where TPixel : struct, IPixel
+ {
+ var test = new FloydSteinbergDiffuser();
+ TPixel pixel = default;
+ test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
index c27c61608d..f21235d06c 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs
@@ -45,9 +45,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float ka = ComputeKa(this.HunterLabWhitePoint);
float kb = ComputeKb(this.HunterLabWhitePoint);
- float l = 100 * MathF.Sqrt(y / yn);
- float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn));
- float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn));
+ float yByYn = y / yn;
+ float sqrtYbyYn = MathF.Sqrt(yByYn);
+ float l = 100 * sqrtYbyYn;
+ float a = ka * (((x / xn) - yByYn) / sqrtYbyYn);
+ float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn);
if (float.IsNaN(a))
{
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
index 783d29a557..4d6808e6c0 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
@@ -26,9 +26,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
- float y = ImageMaths.Pow2(l / 100F) * yn;
- float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn;
- float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn);
+ float pow = ImageMaths.Pow2(l / 100F);
+ float sqrtPow = MathF.Sqrt(pow);
+ float y = pow * yn;
+
+ float x = (((a / ka) * sqrtPow) + pow) * xn;
+ float z = (((b / kb) * sqrtPow) - pow) * (-zn);
return new CieXyz(x, y, z);
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index eb519f4214..ef3ca24ee8 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -310,9 +310,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
else
{
- for (int i = 0; i < cmd[0]; i++)
+ int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0]
+ byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop
+
+ for (; count < max; count++)
{
- buffer[count++] = cmd[1];
+ buffer[count] = cmd1;
}
}
}
diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
index 34c353ec97..2d32fd23aa 100644
--- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs
@@ -309,10 +309,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Non-empty slot
if (Unsafe.Add(ref hashTableRef, i) >= 0)
{
- int disp = hsizeReg - i;
- if (i == 0)
+ int disp = 1;
+ if (i != 0)
{
- disp = 1;
+ disp = hsizeReg - i;
}
do
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 2be5addc2f..81393342d6 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -543,15 +543,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
var sb = new StringBuilder();
sb.Append('[');
- for (int i = 0; i < Size; i++)
+ for (int i = 0; i < Size - 1; i++)
{
sb.Append(this[i]);
- if (i < Size - 1)
- {
- sb.Append(',');
- }
+ sb.Append(',');
}
+ sb.Append(this[Size - 1]);
+
sb.Append(']');
return sb.ToString();
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index 67f665576b..f6da9cb2ec 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -856,10 +856,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void ProcessStartOfScanMarker()
{
int selectorsCount = this.InputStream.ReadByte();
- int componentIndex = -1;
for (int i = 0; i < selectorsCount; i++)
{
- componentIndex = -1;
+ int componentIndex = -1;
int selector = this.InputStream.ReadByte();
for (int j = 0; j < this.Frame.ComponentIds.Length; j++)
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index a4677ba2b7..0dcbd8fef7 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -822,11 +822,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
for (int i = 0; i < componentCount; i++)
{
- this.buffer[(3 * i) + 6] = (byte)(i + 1);
+ int i3 = 3 * i;
+ this.buffer[i3 + 6] = (byte)(i + 1);
// We use 4:2:0 chroma subsampling by default.
- this.buffer[(3 * i) + 7] = subsamples[i];
- this.buffer[(3 * i) + 8] = chroma[i];
+ this.buffer[i3 + 7] = subsamples[i];
+ this.buffer[i3 + 8] = chroma[i];
}
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index e953bc1f07..532e6f7471 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -953,9 +953,9 @@ namespace SixLabors.ImageSharp.Formats.Png
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
ref byte resultRef = ref MemoryMarshal.GetReference(result);
- byte mask = (byte)(0xFF >> (8 - bits));
- byte shift0 = (byte)(8 - bits);
int shift = 8 - bits;
+ byte mask = (byte)(0xFF >> shift);
+ byte shift0 = (byte)shift;
int v = 0;
int resultOffset = 0;
diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
index 528c012c58..4242b2d554 100644
--- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
+++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
@@ -190,12 +190,11 @@ namespace SixLabors.ImageSharp.Formats.Png
else
{
Rgba32 rgba32 = default;
- int bps = bytesPerSample;
for (int x = 0; x < header.Width; x++)
{
int offset = x * bytesPerPixel;
byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
- byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bps);
+ byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
rgba32.R = luminance;
rgba32.G = luminance;
diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs
index 07adc03ff6..34927e6e2b 100644
--- a/src/ImageSharp/Image.FromBytes.cs
+++ b/src/ImageSharp/Image.FromBytes.cs
@@ -198,18 +198,17 @@ namespace SixLabors.ImageSharp
return null;
}
- IImageFormat format = default;
foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors)
{
IImageFormat f = detector.DetectFormat(data);
if (f != null)
{
- format = f;
+ return f;
}
}
- return format;
+ return default;
}
///
diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
index a8c6c5d7e0..3b9b046a00 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs
@@ -139,8 +139,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
protected byte GetTransparentIndex()
{
// Transparent pixels are much more likely to be found at the end of a palette.
- int index = this.paletteVector.Length - 1;
- for (int i = this.paletteVector.Length - 1; i >= 0; i--)
+ int paletteVectorLengthMinus1 = this.paletteVector.Length - 1;
+
+ int index = paletteVectorLengthMinus1;
+ for (int i = paletteVectorLengthMinus1; i >= 0; i--)
{
ref Vector4 candidate = ref this.paletteVector[i];
if (candidate.Equals(default))
diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
index 1eeb0be410..dd56375f63 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
@@ -136,6 +136,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
+ internal TPixel[] AotGetPalette() => this.GetPalette();
+
///
protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors);
diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
index 43d22597df..44df226cfd 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
@@ -173,6 +173,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
+ internal TPixel[] AotGetPalette() => this.GetPalette();
+
///
protected override TPixel[] GetPalette()
{
diff --git a/src/ImageSharp/Processing/ResizeHelper.cs b/src/ImageSharp/Processing/ResizeHelper.cs
index b9233937b1..3ae632162f 100644
--- a/src/ImageSharp/Processing/ResizeHelper.cs
+++ b/src/ImageSharp/Processing/ResizeHelper.cs
@@ -333,25 +333,21 @@ namespace SixLabors.ImageSharp.Processing
return (new Size(sourceWidth, sourceWidth), new Rectangle(0, 0, sourceWidth, sourceHeight));
}
- // Fractional variants for preserving aspect ratio.
- float percentHeight = MathF.Abs(height / (float)sourceHeight);
- float percentWidth = MathF.Abs(width / (float)sourceWidth);
-
- float sourceRatio = (float)sourceHeight / sourceWidth;
-
// Find the shortest distance to go.
int widthDiff = sourceWidth - width;
int heightDiff = sourceHeight - height;
if (widthDiff < heightDiff)
{
+ float sourceRatio = (float)sourceHeight / sourceWidth;
destinationHeight = (int)MathF.Round(width * sourceRatio);
height = destinationHeight;
destinationWidth = width;
}
else if (widthDiff > heightDiff)
{
- destinationWidth = (int)MathF.Round(height / sourceRatio);
+ float sourceRatioInverse = (float)sourceWidth / sourceHeight;
+ destinationWidth = (int)MathF.Round(height * sourceRatioInverse);
destinationHeight = height;
width = destinationWidth;
}
@@ -360,12 +356,14 @@ namespace SixLabors.ImageSharp.Processing
if (height > width)
{
destinationWidth = width;
+ float percentWidth = MathF.Abs(width / (float)sourceWidth);
destinationHeight = (int)MathF.Round(sourceHeight * percentWidth);
height = destinationHeight;
}
else
{
destinationHeight = height;
+ float percentHeight = MathF.Abs(height / (float)sourceHeight);
destinationWidth = (int)MathF.Round(sourceWidth * percentHeight);
width = destinationWidth;
}
diff --git a/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs b/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs
new file mode 100644
index 0000000000..5c1dd4bb08
--- /dev/null
+++ b/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.PixelFormats;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Advanced
+{
+ public class AotCompilerTests
+ {
+ [Fact]
+ public void AotCompiler_NoExceptions()
+ {
+ AotCompilerTools.Seed();
+ }
+ }
+}