Browse Source

Merge branch 'master' into master

pull/292/head
Nikita Balabaev 8 years ago
committed by GitHub
parent
commit
d0e802decd
  1. 49
      src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs
  2. 4
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  3. 25
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  4. 1
      src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs
  5. 1
      src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs
  6. 2
      src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
  7. 1
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  8. 54
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  9. 7
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj

49
src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs

@ -125,6 +125,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary> /// </summary>
private readonly byte[] huffmanBuffer = new byte[179]; private readonly byte[] huffmanBuffer = new byte[179];
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private readonly bool ignoreMetadata;
/// <summary>
/// The quality, that will be used to encode the image.
/// </summary>
private readonly int quality;
/// <summary>
/// Gets or sets the subsampling method to use.
/// </summary>
private readonly JpegSubsample? subsample;
/// <summary> /// <summary>
/// The accumulated bits to write to the stream. /// The accumulated bits to write to the stream.
/// </summary> /// </summary>
@ -150,37 +165,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
/// </summary> /// </summary>
private Stream outputStream; private Stream outputStream;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private bool ignoreMetadata = false;
/// <summary>
/// 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).
/// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value>
private int quality = 0;
/// <summary>
/// Gets or sets the subsampling method to use.
/// </summary>
private JpegSubsample? subsample;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegEncoderCore"/> class. /// Initializes a new instance of the <see cref="JpegEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The options</param> /// <param name="options">The options</param>
public JpegEncoderCore(IJpegEncoderOptions options) public JpegEncoderCore(IJpegEncoderOptions options)
{ {
int quality = options.Quality; // System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
if (quality == 0) this.quality = options.Quality.Clamp(1, 100);
{ this.subsample = options.Subsample ?? (this.quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
quality = 75;
}
this.quality = quality;
this.subsample = options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
this.ignoreMetadata = options.IgnoreMetadata; this.ignoreMetadata = options.IgnoreMetadata;
} }
@ -205,17 +198,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
this.outputStream = stream; this.outputStream = stream;
int quality = this.quality.Clamp(1, 100);
// Convert from a quality rating to a scaling factor. // Convert from a quality rating to a scaling factor.
int scale; int scale;
if (this.quality < 50) if (this.quality < 50)
{ {
scale = 5000 / quality; scale = 5000 / this.quality;
} }
else else
{ {
scale = 200 - (quality * 2); scale = 200 - (this.quality * 2);
} }
// Initialize the quantization tables. // Initialize the quantization tables.

4
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 /// 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). /// index must be between 0 and 100 (compression from max to min).
/// </summary> /// </summary>
/// <value>The quality of the jpg image from 0 to 100.</value> public int Quality { get; set; } = 75;
public int Quality { get; set; }
/// <summary> /// <summary>
/// Gets or sets the subsample ration, that will be used to encode the image. /// Gets or sets the subsample ration, that will be used to encode the image.
/// </summary> /// </summary>
/// <value>The subsample ratio of the jpg image.</value>
public JpegSubsample? Subsample { get; set; } public JpegSubsample? Subsample { get; set; }
/// <summary> /// <summary>

25
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -87,6 +88,30 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
} }
/// <inheritdoc/>
protected override void AfterImageApply(Image<TPixel> 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);
}
}
/// <summary> /// <summary>
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
/// </summary> /// </summary>

1
src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs

@ -61,6 +61,7 @@ namespace SixLabors.ImageSharp.Quantizers
this.colors = (byte)maxColors.Clamp(1, 255); this.colors = (byte)maxColors.Clamp(1, 255);
this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors));
this.palette = null; this.palette = null;
this.colorMap.Clear();
return base.Quantize(image, this.colors); return base.Quantize(image, this.colors);
} }

1
src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs

@ -58,6 +58,7 @@ namespace SixLabors.ImageSharp.Quantizers
public override QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors) public override QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)
{ {
Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); Array.Resize(ref this.colors, maxColors.Clamp(1, 255));
this.colorMap.Clear();
return base.Quantize(image, maxColors); return base.Quantize(image, maxColors);
} }

2
src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
public bool Dither { get; set; } = true; public bool Dither { get; set; } = true;
/// <inheritdoc /> /// <inheritdoc />
public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser(); public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser();
/// <inheritdoc/> /// <inheritdoc/>
public virtual QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors) public virtual QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)

1
src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

@ -139,6 +139,7 @@ namespace SixLabors.ImageSharp.Quantizers
this.colors = maxColors.Clamp(1, 255); this.colors = maxColors.Clamp(1, 255);
this.palette = null; this.palette = null;
this.colorMap.Clear();
try try
{ {

54
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void LoadResizeSave<TPixel>(TestImageProvider<TPixel> provider, int quality, JpegSubsample subsample) public void LoadResizeSave<TPixel>(TestImageProvider<TPixel> provider, int quality, JpegSubsample subsample)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(x=>x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) using (Image<TPixel> 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 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() image.Save(outputStream, new JpegEncoder()
{ {
Subsample = subSample, Subsample = subSample,
Quality = quality Quality = quality
}); });
} }
} }
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
using (MemoryStream memStream = new MemoryStream()) using (MemoryStream memStream = new MemoryStream())
{ {
input.Save(memStream, options); input.Save(memStream, options);
memStream.Position = 0; memStream.Position = 0;
using (Image<Rgba32> output = Image.Load<Rgba32>(memStream)) using (Image<Rgba32> output = Image.Load<Rgba32>(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<Rgba32> 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<Rgba32> 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());
}
}
} }
} }

7
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -17,10 +17,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CoreCompat.System.Drawing" Version="1.0.0-beta006" /> <PackageReference Include="CoreCompat.System.Drawing" Version="1.0.0-beta006" />
<PackageReference Include="xunit" Version="2.3.0-beta4-build3742" /> <PackageReference Include="xunit" Version="2.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta4-build3742" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" /> <PackageReference Include="Moq" Version="4.7.137" />
<PackageReference Include="Moq" Version="4.7.99" />
<!--<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta001"> <!--<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta001">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference>--> </PackageReference>-->

Loading…
Cancel
Save