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/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 3bca4b261..7149b74d8 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -1124,12 +1124,23 @@ namespace SixLabors.ImageSharp.Formats.Png
{
var chunk = new PngChunk();
this.ReadChunkLength(chunk);
- if (chunk.Length < 0)
+
+ if (chunk.Length == -1)
{
+ // IEND
return null;
}
+ if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position)
+ {
+ // Not a valid chunk so we skip back all but one of the four bytes we have just read.
+ // That lets us read one byte at a time until we reach a known chunk.
+ this.currentStream.Position -= 3;
+ return chunk;
+ }
+
this.ReadChunkType(chunk);
+
if (chunk.Type == PngChunkTypes.Data)
{
return chunk;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
index a7fb400ac..86a0c7360 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -87,6 +88,30 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
}
+ ///
+ protected override void AfterImageApply(Image source, Rectangle sourceRectangle)
+ {
+ ExifProfile profile = source.MetaData.ExifProfile;
+ if (profile == null)
+ {
+ return;
+ }
+
+ if (MathF.Abs(this.Angle) < Constants.Epsilon)
+ {
+ // No need to do anything so return.
+ return;
+ }
+
+ profile.RemoveValue(ExifTag.Orientation);
+
+ if (this.Expand && profile.GetValue(ExifTag.PixelXDimension) != null)
+ {
+ profile.SetValue(ExifTag.PixelXDimension, source.Width);
+ profile.SetValue(ExifTag.PixelYDimension, source.Height);
+ }
+ }
+
///
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
///
diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
index 49adce23b..8766f1042 100644
--- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
@@ -61,6 +61,7 @@ namespace SixLabors.ImageSharp.Quantizers
this.colors = (byte)maxColors.Clamp(1, 255);
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
this.palette = null;
+ this.colorMap.Clear();
return base.Quantize(image, this.colors);
}
diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
index ca11a042f..0b95c09a6 100644
--- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
@@ -58,6 +58,7 @@ namespace SixLabors.ImageSharp.Quantizers
public override QuantizedImage Quantize(ImageFrame image, int maxColors)
{
Array.Resize(ref this.colors, maxColors.Clamp(1, 255));
+ this.colorMap.Clear();
return base.Quantize(image, maxColors);
}
diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
index 7f58ff1bf..20ba2e637 100644
--- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
+++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
@@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
public bool Dither { get; set; } = true;
///
- public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser();
+ public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser();
///
public virtual QuantizedImage Quantize(ImageFrame image, int maxColors)
diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
index 77c421468..8ab390f4e 100644
--- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
+++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
@@ -139,6 +139,7 @@ namespace SixLabors.ImageSharp.Quantizers
this.colors = maxColors.Clamp(1, 255);
this.palette = null;
+ this.colorMap.Clear();
try
{
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
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index fc759fb56..d39d0651d 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Tests
TestImages.Png.Splash, TestImages.Png.Indexed,
TestImages.Png.FilterVar,
TestImages.Png.Bad.ChunkLength1,
+ TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.VimImage1,
TestImages.Png.VersioningImage1,
diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
index e8a6e8c59..2f45e4c83 100644
--- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
+++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
@@ -17,10 +17,9 @@
-
-
-
-
+
+
+
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 3fa07819f..948f519d4 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -56,6 +56,7 @@ namespace SixLabors.ImageSharp.Tests
// Odd chunk lengths
public const string ChunkLength1 = "Png/chunklength1.png";
public const string ChunkLength2 = "Png/chunklength2.png";
+ public const string CorruptedChunk = "Png/big-corrupted-chunk.png";
}
public static readonly string[] All =
diff --git a/tests/Images/Input/Png/big-corrupted-chunk.png b/tests/Images/Input/Png/big-corrupted-chunk.png
new file mode 100644
index 000000000..830268977
Binary files /dev/null and b/tests/Images/Input/Png/big-corrupted-chunk.png differ