diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
index 2deb3f62d..2912a8719 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
@@ -125,6 +125,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
private readonly byte[] huffmanBuffer = new byte[179];
+ ///
+ /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
+ ///
+ private readonly bool ignoreMetadata;
+
+ ///
+ /// The quality, that will be used to encode the image.
+ ///
+ private readonly int quality;
+
+ ///
+ /// Gets or sets the subsampling method to use.
+ ///
+ private readonly JpegSubsample? subsample;
+
///
/// The accumulated bits to write to the stream.
///
@@ -150,37 +165,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
private Stream outputStream;
- ///
- /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
- ///
- private bool ignoreMetadata = false;
-
- ///
- /// Gets or sets the quality, that will be used to encode the image. Quality
- /// index must be between 0 and 100 (compression from max to min).
- ///
- /// The quality of the jpg image from 0 to 100.
- private int quality = 0;
-
- ///
- /// Gets or sets the subsampling method to use.
- ///
- private JpegSubsample? subsample;
-
///
/// Initializes a new instance of the class.
///
/// The options
public JpegEncoderCore(IJpegEncoderOptions options)
{
- int quality = options.Quality;
- if (quality == 0)
- {
- quality = 75;
- }
-
- this.quality = quality;
- this.subsample = options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
+ // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
+ this.quality = options.Quality.Clamp(1, 100);
+ this.subsample = options.Subsample ?? (this.quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
this.ignoreMetadata = options.IgnoreMetadata;
}
@@ -205,17 +198,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.outputStream = stream;
- int quality = this.quality.Clamp(1, 100);
-
// Convert from a quality rating to a scaling factor.
int scale;
if (this.quality < 50)
{
- scale = 5000 / quality;
+ scale = 5000 / this.quality;
}
else
{
- scale = 200 - (quality * 2);
+ scale = 200 - (this.quality * 2);
}
// Initialize the quantization tables.
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
index 3d79faabc..8850f581c 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
@@ -21,13 +21,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// Gets or sets the quality, that will be used to encode the image. Quality
/// index must be between 0 and 100 (compression from max to min).
///
- /// The quality of the jpg image from 0 to 100.
- public int Quality { get; set; }
+ public int Quality { get; set; } = 75;
///
/// Gets or sets the subsample ration, that will be used to encode the image.
///
- /// The subsample ratio of the jpg image.
public JpegSubsample? Subsample { get; set; }
///
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index 3bd1ed265..c8d416bea 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample)
where TPixel : struct, IPixel
{
- using (Image image = provider.GetImage(x=>x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })))
+ using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })))
{
image.MetaData.ExifProfile = null; // Reduce the size of the file
@@ -62,8 +62,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
image.Save(outputStream, new JpegEncoder()
{
- Subsample = subSample,
- Quality = quality
+ Subsample = subSample,
+ Quality = quality
});
}
}
@@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using (MemoryStream memStream = new MemoryStream())
{
- input.Save(memStream, options);
+ input.Save(memStream, options);
memStream.Position = 0;
using (Image output = Image.Load(memStream))
@@ -118,5 +118,51 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
}
+
+ [Fact]
+ public void Encode_Quality_0_And_1_Are_Identical()
+ {
+ var options = new JpegEncoder
+ {
+ Quality = 0
+ };
+
+ var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora);
+
+ using (Image input = testFile.CreateImage())
+ using (var memStream0 = new MemoryStream())
+ using (var memStream1 = new MemoryStream())
+ {
+ input.SaveAsJpeg(memStream0, options);
+
+ options.Quality = 1;
+ input.SaveAsJpeg(memStream1, options);
+
+ Assert.Equal(memStream0.ToArray(), memStream1.ToArray());
+ }
+ }
+
+ [Fact]
+ public void Encode_Quality_0_And_100_Are_Not_Identical()
+ {
+ var options = new JpegEncoder
+ {
+ Quality = 0
+ };
+
+ var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora);
+
+ using (Image input = testFile.CreateImage())
+ using (var memStream0 = new MemoryStream())
+ using (var memStream1 = new MemoryStream())
+ {
+ input.SaveAsJpeg(memStream0, options);
+
+ options.Quality = 100;
+ input.SaveAsJpeg(memStream1, options);
+
+ Assert.NotEqual(memStream0.ToArray(), memStream1.ToArray());
+ }
+ }
}
}
\ No newline at end of file