diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
index 2b98746546..5c6535bd37 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
@@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void AllocateComponents()
{
- bool fullScan = this.Progressive || this.Interleaved;
+ bool fullScan = this.Progressive || !this.Interleaved;
for (int i = 0; i < this.ComponentCount; i++)
{
IJpegComponent component = this.Components[i];
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
index 5bad2a5b04..7fe6a4990d 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
@@ -5,7 +5,6 @@ using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg
diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
index 4a0a728ad9..5b5dedaab3 100644
--- a/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
+++ b/tests/ImageSharp.Tests.ProfilingSandbox/Program.cs
@@ -2,18 +2,10 @@
// Licensed under the Six Labors Split License.
using System;
-using System.Diagnostics;
-using System.IO;
using System.Reflection;
using System.Threading;
-using PhotoSauce.MagicScaler;
-using PhotoSauce.MagicScaler.Interpolators;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.Formats.Jpeg.Components;
-using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
-using SixLabors.ImageSharp.Processing.Processors.Transforms;
+using SixLabors.ImageSharp.Memory.Internals;
+using SixLabors.ImageSharp.Tests.Formats.Jpg;
using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations;
using SixLabors.ImageSharp.Tests.ProfilingBenchmarks;
using Xunit.Abstractions;
@@ -33,173 +25,31 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args);
}
- const string pathTemplate = "C:\\Users\\pl4nu\\Downloads\\{0}.jpg";
-
+ ///
+ /// The main entry point. Useful for executing benchmarks and performance unit tests manually,
+ /// when the IDE test runners lack some of the functionality. Eg.: it's not possible to run JetBrains memory profiler for unit tests.
+ ///
+ ///
+ /// The arguments to pass to the program.
+ ///
public static void Main(string[] args)
{
- //string imageName = "Calliphora_aligned_size";
- //string imageName = "Calliphora";
- string imageName = "1x1";
- //string imageName = "bw_check";
- //string imageName = "bw_check_color";
- ReEncodeImage(imageName, JpegEncodingColor.YCbCrRatio444, 100);
- ReEncodeImage(imageName, JpegEncodingColor.YCbCrRatio422, 100);
- ReEncodeImage(imageName, JpegEncodingColor.YCbCrRatio420, 100);
- ReEncodeImage(imageName, JpegEncodingColor.YCbCrRatio411, 100);
- ReEncodeImage(imageName, JpegEncodingColor.YCbCrRatio410, 100);
- //ReEncodeImage(imageName, JpegEncodingColor.Luminance, 100);
- //ReEncodeImage(imageName, JpegEncodingColor.Rgb, 100);
- //ReEncodeImage(imageName, JpegEncodingColor.Cmyk, 100);
-
- // Encoding q=75 | color=YCbCrRatio444
- // Elapsed: 4901ms across 500 iterations
- // Average: 9,802ms
- //BenchmarkEncoder(imageName, 500, 75, JpegEncodingColor.YCbCrRatio444);
- }
-
- private static void BenchmarkEncoder(string fileName, int iterations, int quality, JpegEncodingColor color)
- {
- string loadPath = String.Format(pathTemplate, fileName);
-
- using var inputStream = new FileStream(loadPath, FileMode.Open);
- using var saveStream = new MemoryStream();
-
- var decoder = new JpegDecoder { IgnoreMetadata = true };
- using Image img = decoder.Decode(Configuration.Default, inputStream, CancellationToken.None);
-
- var encoder = new JpegEncoder()
- {
- Quality = quality,
- ColorType = color,
- };
-
- Stopwatch sw = new Stopwatch();
- sw.Start();
- for (int i = 0; i < iterations; i++)
- {
- img.SaveAsJpeg(saveStream, encoder);
- saveStream.Position = 0;
- }
- sw.Stop();
-
- Console.WriteLine($"// Encoding q={quality} | color={color}\n" +
- $"// Elapsed: {sw.ElapsedMilliseconds}ms across {iterations} iterations\n" +
- $"// Average: {(double)sw.ElapsedMilliseconds / iterations}ms");
- }
-
- private static void BenchmarkDecoder(string fileName, int iterations)
- {
- string loadPath = String.Format(pathTemplate, fileName);
-
- using var fileStream = new FileStream(loadPath, FileMode.Open);
- using var inputStream = new MemoryStream();
- fileStream.CopyTo(inputStream);
-
- var decoder = new JpegDecoder { IgnoreMetadata = true };
-
- var sw = new Stopwatch();
- sw.Start();
- for (int i = 0; i < iterations; i++)
+ try
{
- inputStream.Position = 0;
- using Image img = decoder.Decode(Configuration.Default, inputStream, CancellationToken.None);
+ LoadResizeSaveParallelMemoryStress.Run(args);
}
- sw.Stop();
-
- Console.WriteLine($"// Decoding\n" +
- $"// Elapsed: {sw.ElapsedMilliseconds}ms across {iterations} iterations\n" +
- $"// Average: {(double)sw.ElapsedMilliseconds / iterations}ms");
- }
-
- private static void BenchmarkResizingLoop__explicit(string fileName, Size targetSize, int iterations)
- {
- string loadPath = String.Format(pathTemplate, fileName);
-
- using var fileStream = new FileStream(loadPath, FileMode.Open);
- using var saveStream = new MemoryStream();
- using var inputStream = new MemoryStream();
- fileStream.CopyTo(inputStream);
-
- var decoder = new JpegDecoder { IgnoreMetadata = true };
- var encoder = new JpegEncoder { ColorType = JpegEncodingColor.YCbCrRatio444 };
-
- var sw = new Stopwatch();
- sw.Start();
- for (int i = 0; i < iterations; i++)
+ catch (Exception ex)
{
- inputStream.Position = 0;
- using Image img = decoder.Decode(Configuration.Default, inputStream, CancellationToken.None);
- img.Mutate(ctx => ctx.Resize(targetSize, KnownResamplers.Box, false));
- img.SaveAsJpeg(saveStream, encoder);
+ Console.WriteLine(ex);
}
- sw.Stop();
-
- Console.WriteLine($"// Decode-Resize-Encode w/ Mutate()\n" +
- $"// Elapsed: {sw.ElapsedMilliseconds}ms across {iterations} iterations\n" +
- $"// Average: {(double)sw.ElapsedMilliseconds / iterations}ms");
- }
- private static void ReEncodeImage(string fileName, JpegEncodingColor mode, int? quality = null)
- {
- string loadPath = String.Format(pathTemplate, fileName);
- using Image img = Image.Load(loadPath);
-
- string savePath = String.Format(pathTemplate, $"q{quality}_{mode}_test_{fileName}");
- var encoder = new JpegEncoder()
- {
- Quality = quality,
- ColorType = mode,
- };
- img.SaveAsJpeg(savePath, encoder);
- }
-
- private static void ReencodeImageResize__explicit(string fileName, Size targetSize, IResampler sampler, int? quality = null)
- {
- string loadPath = String.Format(pathTemplate, fileName);
- string savePath = String.Format(pathTemplate, $"is_res_{sampler.GetType().Name}[{targetSize.Width}x{targetSize.Height}]_{fileName}");
-
- var decoder = new JpegDecoder();
- var encoder = new JpegEncoder()
- {
- Quality = quality,
- ColorType = JpegEncodingColor.YCbCrRatio444
- };
-
- using Image img = decoder.Decode(Configuration.Default, File.OpenRead(loadPath), CancellationToken.None);
- img.Mutate(ctx => ctx.Resize(targetSize, sampler, compand: false));
- img.SaveAsJpeg(savePath, encoder);
- }
-
- private static void ReencodeImageResize__Netvips(string fileName, Size targetSize, int? quality)
- {
- string loadPath = String.Format(pathTemplate, fileName);
- string savePath = String.Format(pathTemplate, $"netvips_resize_{fileName}");
-
- using var thumb = NetVips.Image.Thumbnail(loadPath, targetSize.Width, targetSize.Height);
-
- // Save the results
- thumb.Jpegsave(savePath, q: quality, strip: true, subsampleMode: NetVips.Enums.ForeignSubsample.Off);
- }
-
- private static void ReencodeImageResize__MagicScaler(string fileName, Size targetSize, int quality)
- {
- string loadPath = String.Format(pathTemplate, fileName);
- string savePath = String.Format(pathTemplate, $"magicscaler_resize_{fileName}");
+ // RunJpegEncoderProfilingTests();
+ // RunJpegColorProfilingTests();
+ // RunDecodeJpegProfilingTests();
+ // RunToVector4ProfilingTest();
+ // RunResizeProfilingTest();
- var settings = new ProcessImageSettings()
- {
- Width = targetSize.Width,
- Height = targetSize.Height,
- SaveFormat = FileFormat.Jpeg,
- JpegQuality = quality,
- JpegSubsampleMode = ChromaSubsampleMode.Subsample444,
- Sharpen = false,
- ColorProfileMode = ColorProfileMode.Ignore,
- HybridMode = HybridScaleMode.Turbo,
- };
-
- using var output = new FileStream(savePath, FileMode.Create);
- MagicImageProcessor.ProcessImage(loadPath, output, settings);
+ // Console.ReadLine();
}
private static Version GetNetCoreVersion()
@@ -216,12 +66,6 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
return null;
}
- private static void RunJpegEncoderProfilingTests()
- {
- var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
- benchmarks.EncodeJpeg_SingleMidSize();
- }
-
private static void RunResizeProfilingTest()
{
var test = new ResizeProfilingBenchmarks(new ConsoleOutput());
@@ -233,19 +77,5 @@ namespace SixLabors.ImageSharp.Tests.ProfilingSandbox
var tests = new PixelOperationsTests.Rgba32_OperationsTests(new ConsoleOutput());
tests.Benchmark_ToVector4();
}
-
- private static void RunDecodeJpegProfilingTests()
- {
- Console.WriteLine("RunDecodeJpegProfilingTests...");
- var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput());
- foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData)
- {
- string fileName = (string)data[0];
- int executionCount = (int)data[1];
- benchmarks.DecodeJpeg(fileName, executionCount);
- }
-
- Console.WriteLine("DONE.");
- }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
index af07338fb5..60f45664d3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
@@ -17,6 +18,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Trait("Format", "Jpg")]
public partial class JpegEncoderTests
{
+ public static readonly TheoryData RatioFiles =
+ new()
+ {
+ { TestImages.Jpeg.Baseline.Ratio1x1, 1, 1, PixelResolutionUnit.AspectRatio },
+ { TestImages.Jpeg.Baseline.Snake, 300, 300, PixelResolutionUnit.PixelsPerInch },
+ { TestImages.Jpeg.Baseline.GammaDalaiLamaGray, 72, 72, PixelResolutionUnit.PixelsPerInch }
+ };
+
+ public static readonly TheoryData QualityFiles =
+ new()
+ {
+ { TestImages.Jpeg.Baseline.Calliphora, 80 },
+ { TestImages.Jpeg.Progressive.Fb, 75 }
+ };
+
[Fact]
public void Encode_PreservesIptcProfile()
{
@@ -95,5 +111,71 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Null(ex);
}
+
+ [Theory]
+ [MemberData(nameof(RatioFiles))]
+ public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
+ {
+ var testFile = TestFile.Create(imagePath);
+ using (Image input = testFile.CreateRgba32Image())
+ {
+ using (var memStream = new MemoryStream())
+ {
+ input.Save(memStream, JpegEncoder);
+
+ memStream.Position = 0;
+ using (var output = Image.Load(memStream))
+ {
+ ImageMetadata meta = output.Metadata;
+ Assert.Equal(xResolution, meta.HorizontalResolution);
+ Assert.Equal(yResolution, meta.VerticalResolution);
+ Assert.Equal(resolutionUnit, meta.ResolutionUnits);
+ }
+ }
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(QualityFiles))]
+ public void Encode_PreservesQuality(string imagePath, int quality)
+ {
+ var testFile = TestFile.Create(imagePath);
+ using (Image input = testFile.CreateRgba32Image())
+ {
+ using (var memStream = new MemoryStream())
+ {
+ input.Save(memStream, JpegEncoder);
+
+ memStream.Position = 0;
+ using (var output = Image.Load(memStream))
+ {
+ JpegMetadata meta = output.Metadata.GetJpegMetadata();
+ Assert.Equal(quality, meta.Quality);
+ }
+ }
+ }
+ }
+
+ [Theory]
+ [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgb24, JpegEncodingColor.Luminance)]
+ [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)]
+ [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)]
+ [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgb24, JpegEncodingColor.Rgb)]
+ public void Encode_PreservesColorType(TestImageProvider provider, JpegEncodingColor expectedColorType)
+ where TPixel : unmanaged, IPixel
+ {
+ // arrange
+ using Image input = provider.GetImage(JpegDecoder);
+ using var memoryStream = new MemoryStream();
+
+ // act
+ input.Save(memoryStream, JpegEncoder);
+
+ // assert
+ memoryStream.Position = 0;
+ using var output = Image.Load(memoryStream);
+ JpegMetadata meta = output.Metadata.GetJpegMetadata();
+ Assert.Equal(expectedColorType, meta.ColorType);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index 4f16282f47..8a78ef6485 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -5,7 +5,6 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities;
@@ -22,139 +21,122 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private static JpegDecoder JpegDecoder => new();
- public static readonly TheoryData QualityFiles =
- new()
- {
- { TestImages.Jpeg.Baseline.Calliphora, 80 },
- { TestImages.Jpeg.Progressive.Fb, 75 }
- };
-
- public static readonly TheoryData BitsPerPixel_Quality =
- new()
- {
- { JpegEncodingColor.YCbCrRatio420, 40 },
- { JpegEncodingColor.YCbCrRatio420, 60 },
- { JpegEncodingColor.YCbCrRatio420, 100 },
- { JpegEncodingColor.YCbCrRatio444, 40 },
- { JpegEncodingColor.YCbCrRatio444, 60 },
- { JpegEncodingColor.YCbCrRatio444, 100 },
- { JpegEncodingColor.Rgb, 40 },
- { JpegEncodingColor.Rgb, 60 },
- { JpegEncodingColor.Rgb, 100 }
- };
+ private static readonly TheoryData TestQualities = new()
+ {
+ 40,
+ 80,
+ 100,
+ };
- public static readonly TheoryData Grayscale_Quality =
- new()
- {
- { 40 },
- { 60 },
- { 100 }
- };
+ public static readonly TheoryData NonSubsampledEncodingSetups = new()
+ {
+ { JpegEncodingColor.Rgb, 100, 0.0238f / 100 },
+ { JpegEncodingColor.Rgb, 80, 1.3044f / 100 },
+ { JpegEncodingColor.Rgb, 40, 2.9879f / 100 },
- public static readonly TheoryData RatioFiles =
- new()
- {
- { TestImages.Jpeg.Baseline.Ratio1x1, 1, 1, PixelResolutionUnit.AspectRatio },
- { TestImages.Jpeg.Baseline.Snake, 300, 300, PixelResolutionUnit.PixelsPerInch },
- { TestImages.Jpeg.Baseline.GammaDalaiLamaGray, 72, 72, PixelResolutionUnit.PixelsPerInch }
- };
+ { JpegEncodingColor.YCbCrRatio444, 100, 0.0780f / 100 },
+ { JpegEncodingColor.YCbCrRatio444, 80, 1.4585f / 100 },
+ { JpegEncodingColor.YCbCrRatio444, 40, 3.1413f / 100 },
+ };
- [Fact]
- public void Quality_1_And_100_Are_Not_Identical()
+ public static readonly TheoryData SubsampledEncodingSetups = new()
{
- var options = new JpegEncoder
- {
- Quality = 1
- };
+ { JpegEncodingColor.YCbCrRatio422, 100, 0.4895f / 100 },
+ { JpegEncodingColor.YCbCrRatio422, 80, 1.6043f / 100 },
+ { JpegEncodingColor.YCbCrRatio422, 40, 3.1996f / 100 },
- var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora);
+ { JpegEncodingColor.YCbCrRatio420, 100, 0.5790f / 100 },
+ { JpegEncodingColor.YCbCrRatio420, 80, 1.6692f / 100 },
+ { JpegEncodingColor.YCbCrRatio420, 40, 3.2324f / 100 },
- using (Image input = testFile.CreateRgba32Image())
- using (var memStream0 = new MemoryStream())
- using (var memStream1 = new MemoryStream())
- {
- input.SaveAsJpeg(memStream0, options);
+ { JpegEncodingColor.YCbCrRatio411, 100, 0.6868f / 100 },
+ { JpegEncodingColor.YCbCrRatio411, 80, 1.7139f / 100 },
+ { JpegEncodingColor.YCbCrRatio411, 40, 3.2634f / 100 },
- options.Quality = 100;
- input.SaveAsJpeg(memStream1, options);
+ { JpegEncodingColor.YCbCrRatio410, 100, 0.7357f / 100 },
+ { JpegEncodingColor.YCbCrRatio410, 80, 1.7495f / 100 },
+ { JpegEncodingColor.YCbCrRatio410, 40, 3.2911f / 100 },
+ };
- Assert.NotEqual(memStream0.ToArray(), memStream1.ToArray());
- }
- }
+ public static readonly TheoryData CmykEncodingSetups = new()
+ {
+ { JpegEncodingColor.Cmyk, 100, 0.0159f / 100 },
+ { JpegEncodingColor.Cmyk, 80, 0.3922f / 100 },
+ { JpegEncodingColor.Cmyk, 40, 0.6488f / 100 },
+ };
- [Theory]
- [WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgba32, JpegEncodingColor.Luminance)]
- [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32, JpegEncodingColor.YCbCrRatio444)]
- [WithFile(TestImages.Jpeg.Baseline.Jpeg420Small, PixelTypes.Rgba32, JpegEncodingColor.YCbCrRatio420)]
- [WithFile(TestImages.Jpeg.Baseline.JpegRgb, PixelTypes.Rgba32, JpegEncodingColor.Rgb)]
- public void Encode_PreservesColorType(TestImageProvider provider, JpegEncodingColor expectedColorType)
- where TPixel : unmanaged, IPixel
+ public static readonly TheoryData YcckEncodingSetups = new()
{
- // arrange
- using Image input = provider.GetImage(JpegDecoder);
- using var memoryStream = new MemoryStream();
-
- // act
- input.Save(memoryStream, JpegEncoder);
-
- // assert
- memoryStream.Position = 0;
- using var output = Image.Load(memoryStream);
- JpegMetadata meta = output.Metadata.GetJpegMetadata();
- Assert.Equal(expectedColorType, meta.ColorType);
- }
+ { JpegEncodingColor.Ycck, 100, 0.0356f / 100 },
+ { JpegEncodingColor.Ycck, 80, 0.1245f / 100 },
+ { JpegEncodingColor.Ycck, 40, 0.2663f / 100 },
+ };
+
+ public static readonly TheoryData LuminanceEncodingSetups = new()
+ {
+ { JpegEncodingColor.Luminance, 100, 0.0175f / 100 },
+ { JpegEncodingColor.Luminance, 80, 0.6730f / 100 },
+ { JpegEncodingColor.Luminance, 40, 0.9941f / 100 },
+ };
+
+ [Theory]
+ [WithFile(TestImages.Png.CalliphoraPartial, nameof(NonSubsampledEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Png.CalliphoraPartial, nameof(SubsampledEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)]
+ [WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)]
+ public void EncodeBaseline_Interleaved(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
+ where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, tolerance);
[Theory]
- [MemberData(nameof(QualityFiles))]
- public void Encode_PreservesQuality(string imagePath, int quality)
+ [WithFile(TestImages.Png.CalliphoraPartial, nameof(NonSubsampledEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Png.CalliphoraPartial, nameof(SubsampledEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)]
+ [WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)]
+ [WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)]
+ public void EncodeBaseline_NonInterleavedMode(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
+ where TPixel : unmanaged, IPixel
{
- var testFile = TestFile.Create(imagePath);
- using (Image input = testFile.CreateRgba32Image())
+ using Image image = provider.GetImage();
+
+ var encoder = new JpegEncoder
{
- using (var memStream = new MemoryStream())
- {
- input.Save(memStream, JpegEncoder);
-
- memStream.Position = 0;
- using (var output = Image.Load(memStream))
- {
- JpegMetadata meta = output.Metadata.GetJpegMetadata();
- Assert.Equal(quality, meta.Quality);
- }
- }
- }
- }
+ Quality = quality,
+ ColorType = colorType,
+ Interleaved = false,
+ };
+ string info = $"{colorType}-Q{quality}";
- [Theory]
- [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)]
- public void EncodeBaseline_CalliphoraPartial(TestImageProvider provider, JpegEncodingColor colorType, int quality)
- where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality);
+ ImageComparer comparer = new TolerantImageComparer(tolerance);
+
+ // Does DebugSave & load reference CompareToReferenceInput():
+ image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg");
+ }
[Theory]
- [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 158, 24, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 153, 21, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 600, 400, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 138, 24, PixelTypes.Rgba32)]
- public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality)
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 600, 400, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 158, 24, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 153, 21, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 143, 81, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 138, 24, PixelTypes.Rgb24)]
+ public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality);
[Theory]
- [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)]
- [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 143, 81, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 96, 48, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 73, 71, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 24, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 46, 8, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 51, 7, PixelTypes.Rgba32)]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)]
- public void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality)
- where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, comparer: ImageComparer.Tolerant(0.15f));
+ [WithSolidFilledImages(nameof(NonSubsampledEncodingSetups), 1, 1, 100, 100, 100, 255, PixelTypes.L8)]
+ [WithSolidFilledImages(nameof(NonSubsampledEncodingSetups), 1, 1, 255, 100, 50, 255, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 143, 81, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 7, 5, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 96, 48, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 73, 71, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 24, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 46, 8, PixelTypes.Rgb24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 51, 7, PixelTypes.Rgb24)]
+ public void EncodeBaseline_WithSmallImages_WorksWithDifferentSizes(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
+ where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, ImageComparer.Tolerant(0.12f));
[Theory]
- [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)]
- [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgba32, 100)]
+ [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgb24, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L8, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L16, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)]
@@ -163,20 +145,21 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, JpegEncodingColor.Luminance, quality);
[Theory]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 96, 96, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
- public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality)
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 96, 96, PixelTypes.Rgb24 | PixelTypes.Bgr24)]
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 48, PixelTypes.Rgb24 | PixelTypes.Bgr24)]
+ public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality);
[Theory]
- [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
- public void EncodeBaseline_WithSmallImages_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality)
+ [WithTestPatternImages(nameof(NonSubsampledEncodingSetups), 48, 48, PixelTypes.Rgb24 | PixelTypes.Bgr24)]
+ public void EncodeBaseline_WithSmallImages_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
where TPixel : unmanaged, IPixel => TestJpegEncoderCore(provider, colorType, quality, comparer: ImageComparer.Tolerant(0.06f));
[Theory]
- [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, JpegEncodingColor.YCbCrRatio444)]
- [WithTestPatternImages(587, 821, PixelTypes.Rgba32, JpegEncodingColor.YCbCrRatio444)]
- [WithTestPatternImages(677, 683, PixelTypes.Bgra32, JpegEncodingColor.YCbCrRatio420)]
- [WithSolidFilledImages(400, 400, "Red", PixelTypes.Bgr24, JpegEncodingColor.YCbCrRatio420)]
+ [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)]
+ [WithTestPatternImages(587, 821, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)]
+ [WithTestPatternImages(677, 683, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)]
+ [WithSolidFilledImages(400, 400, nameof(Color.Red), PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)]
public void EncodeBaseline_WorksWithDiscontiguousBuffers(TestImageProvider provider, JpegEncodingColor colorType)
where TPixel : unmanaged, IPixel
{
@@ -188,96 +171,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestJpegEncoderCore(provider, colorType, 100, comparer);
}
- [Theory]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.L8, JpegEncodingColor.Luminance)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.Rgb)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.Cmyk)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio422)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio411)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio410)]
- public void EncodeBaseline_WorksWithAllColorTypes(TestImageProvider provider, JpegEncodingColor colorType)
- where TPixel : unmanaged, IPixel
- {
- // all reference output images are saved with quality=100
- const int quality = 100;
-
- using Image image = provider.GetImage();
-
- // There is no alpha in Jpeg!
- image.Mutate(c => c.MakeOpaque());
-
- var encoder = new JpegEncoder
- {
- Quality = quality,
- ColorType = colorType
- };
- string info = $"{colorType}-Q{quality}";
-
- ImageComparer comparer = GetComparer(quality, colorType);
-
- // Does DebugSave & load reference CompareToReferenceInput():
- image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg");
- }
-
- [Theory]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.L8, JpegEncodingColor.Luminance)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.Rgb)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.Cmyk)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.Ycck)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio444)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio422)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio420)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio411)]
- [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgb24, JpegEncodingColor.YCbCrRatio410)]
- public void EncodeBaseline_WorksInNonInterleavedMode(TestImageProvider provider, JpegEncodingColor colorType)
- where TPixel : unmanaged, IPixel
- {
- // all reference output images are saved with quality=100
- const int quality = 100;
-
- using Image image = provider.GetImage();
-
- // There is no alpha in Jpeg!
- image.Mutate(c => c.MakeOpaque());
-
- var encoder = new JpegEncoder
- {
- ColorType = colorType,
- Interleaved = false
- };
- string info = $"{colorType}-Q{quality}";
-
- ImageComparer comparer = GetComparer(quality, colorType);
-
- // Does DebugSave & load reference CompareToReferenceInput():
- image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg");
- }
-
- [Theory]
- [MemberData(nameof(RatioFiles))]
- public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
- {
- var testFile = TestFile.Create(imagePath);
- using (Image input = testFile.CreateRgba32Image())
- {
- using (var memStream = new MemoryStream())
- {
- input.Save(memStream, JpegEncoder);
-
- memStream.Position = 0;
- using (var output = Image.Load(memStream))
- {
- ImageMetadata meta = output.Metadata;
- Assert.Equal(xResolution, meta.HorizontalResolution);
- Assert.Equal(yResolution, meta.VerticalResolution);
- Assert.Equal(resolutionUnit, meta.ResolutionUnits);
- }
- }
- }
- }
-
[Theory]
[InlineData(JpegEncodingColor.YCbCrRatio420)]
[InlineData(JpegEncodingColor.YCbCrRatio444)]
@@ -331,18 +224,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
return ImageComparer.Tolerant(tolerance);
}
- private static void TestJpegEncoderCore(
- TestImageProvider provider,
- JpegEncodingColor colorType = JpegEncodingColor.YCbCrRatio420,
- int quality = 100,
- ImageComparer comparer = null)
+ private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality)
+ where TPixel : unmanaged, IPixel
+ => TestJpegEncoderCore(provider, colorType, quality, GetComparer(quality, colorType));
+
+ private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality, float tolerance)
+ where TPixel : unmanaged, IPixel
+ => TestJpegEncoderCore(provider, colorType, quality, new TolerantImageComparer(tolerance));
+
+ private static void TestJpegEncoderCore(TestImageProvider provider, JpegEncodingColor colorType, int quality, ImageComparer comparer)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage();
- // There is no alpha in Jpeg!
- image.Mutate(c => c.MakeOpaque());
-
var encoder = new JpegEncoder
{
Quality = quality,
@@ -350,8 +244,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
};
string info = $"{colorType}-Q{quality}";
- comparer ??= GetComparer(quality, colorType);
-
// Does DebugSave & load reference CompareToReferenceInput():
image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png");
}
diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
deleted file mode 100644
index 08b7714ec0..0000000000
--- a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using System;
-using System.IO;
-using System.Linq;
-using System.Numerics;
-using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.PixelFormats;
-
-using Xunit;
-using Xunit.Abstractions;
-
-// in this file, comments are used for disabling stuff for local execution
-#pragma warning disable SA1515
-
-namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
-{
- public class JpegProfilingBenchmarks : MeasureFixture
- {
- public JpegProfilingBenchmarks(ITestOutputHelper output)
- : base(output)
- {
- }
-
- public static readonly TheoryData DecodeJpegData = new TheoryData
- {
- { TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, 20 },
- { TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, 20 },
- { TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, 40 },
- // { TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, 10 },
- // { TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, 5 },
- { TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, 5 }
- };
-
- [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
- [MemberData(nameof(DecodeJpegData))]
- public void DecodeJpeg(string fileName, int executionCount)
- {
- var decoder = new JpegDecoder()
- {
- IgnoreMetadata = true
- };
- this.DecodeJpegBenchmarkImpl(fileName, decoder, executionCount);
- }
-
- private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder, int executionCount)
- {
- // do not run this on CI even by accident
- if (TestEnvironment.RunsOnCI)
- {
- return;
- }
-
- if (!Vector.IsHardwareAccelerated)
- {
- throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)");
- }
-
- string path = TestFile.GetInputFileFullPath(fileName);
- byte[] bytes = File.ReadAllBytes(path);
-
- this.Measure(
- executionCount,
- () =>
- {
- var img = Image.Load(bytes, decoder);
- img.Dispose();
- },
-#pragma warning disable SA1515 // Single-line comment should be preceded by blank line
- // ReSharper disable once ExplicitCallerInfoArgument
- $"Decode {fileName}");
-#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
- }
-
- [Fact(Skip = ProfilingSetup.SkipProfilingTests)]
- public void EncodeJpeg_SingleMidSize()
- {
- string path = TestFile.GetInputFileFullPath(TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr);
- using var image = Image.Load(path);
- image.Metadata.ExifProfile = null;
-
- using var ms = new MemoryStream();
- for (int i = 0; i < 30; i++)
- {
- image.SaveAsJpeg(ms);
- ms.Seek(0, SeekOrigin.Begin);
- }
- }
-
- // Benchmark, enable manually!
- [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
- [InlineData(1, 75, JpegEncodingColor.YCbCrRatio420)]
- [InlineData(30, 75, JpegEncodingColor.YCbCrRatio420)]
- [InlineData(30, 75, JpegEncodingColor.YCbCrRatio444)]
- [InlineData(30, 100, JpegEncodingColor.YCbCrRatio444)]
- public void EncodeJpeg(int executionCount, int quality, JpegEncodingColor colorType)
- {
- // do not run this on CI even by accident
- if (TestEnvironment.RunsOnCI)
- {
- return;
- }
-
- string[] testFiles = TestImages.Bmp.Benchmark
- .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }).ToArray();
-
- Image[] testImages = testFiles.Select(
- tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.Rgba32).GetImage()).ToArray();
-
- using (var ms = new MemoryStream())
- {
- this.Measure(
- executionCount,
- () =>
- {
- foreach (Image img in testImages)
- {
- var options = new JpegEncoder { Quality = quality, ColorType = colorType };
- img.Save(ms, options);
- ms.Seek(0, SeekOrigin.Begin);
- }
- },
-#pragma warning disable SA1515 // Single-line comment should be preceded by blank line
- // ReSharper disable once ExplicitCallerInfoArgument
- $@"Encode {testFiles.Length} images");
-#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
- }
-
- foreach (Image image in testImages)
- {
- image.Dispose();
- }
- }
- }
-}