Browse Source

Remove JpegSubsample and use JpegColorType instead

pull/1734/head
Brian Popow 5 years ago
parent
commit
a531a2db24
  1. 8
      src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs
  2. 18
      src/ImageSharp/Formats/Jpeg/JpegColorType.cs
  3. 17
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  4. 15
      src/ImageSharp/Formats/Jpeg/JpegEncoder.cs
  5. 45
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  6. 28
      src/ImageSharp/Formats/Jpeg/JpegSubsample.cs
  7. 2
      src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs
  8. 4
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
  9. 65
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  10. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs
  11. 6
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  12. 14
      tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs

8
src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

@ -16,13 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public int? Quality { get; set; } public int? Quality { get; set; }
/// <summary> /// <summary>
/// Gets the subsample ration, that will be used to encode the image. /// Gets the color type, that will be used to encode the image.
/// </summary>
/// <value>The subsample ratio of the jpg image.</value>
JpegSubsample? Subsample { get; }
/// <summary>
/// Gets the color type.
/// </summary> /// </summary>
JpegColorType? ColorType { get; } JpegColorType? ColorType { get; }
} }

18
src/ImageSharp/Formats/Jpeg/JpegColorType.cs

@ -10,12 +10,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
/// <summary> /// <summary>
/// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification.
/// Medium Quality - The horizontal sampling is halved and the Cb and Cr channels are only
/// sampled on each alternate line.
/// </summary> /// </summary>
YCbCr = 0, YCbCrRatio420 = 0,
/// <summary>
/// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification.
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
/// thus there is no chroma subsampling.
/// </summary>
YCbCrRatio444 = 1,
/// <summary> /// <summary>
/// Single channel, luminance. /// Single channel, luminance.
/// </summary> /// </summary>
Luminance = 1 Luminance = 2,
/// <summary>
/// The pixel data will be preserved as RGB without any sub sampling.
/// </summary>
Rgb,
} }
} }

17
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.SOS: case JpegConstants.Markers.SOS:
if (!metadataOnly) if (!metadataOnly)
{ {
this.ProcessStartOfScanMarker(stream, remaining, cancellationToken); this.ProcessStartOfScanMarker(stream, remaining);
break; break;
} }
else else
@ -953,7 +953,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components); this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components);
this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; switch (this.ColorSpace)
{
case JpegColorSpace.Grayscale:
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.Luminance;
break;
case JpegColorSpace.RGB:
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.Rgb;
break;
default:
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.YCbCrRatio420;
break;
}
if (!metadataOnly) if (!metadataOnly)
{ {
@ -1051,7 +1062,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Processes the SOS (Start of scan marker). /// Processes the SOS (Start of scan marker).
/// </summary> /// </summary>
private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining, CancellationToken cancellationToken) private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
{ {
if (this.Frame is null) if (this.Frame is null)
{ {

15
src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

@ -16,14 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/> /// <inheritdoc/>
public int? Quality { get; set; } public int? Quality { get; set; }
/// <summary> /// <inheritdoc/>
/// Gets or sets the subsample ration, that will be used to encode the image.
/// </summary>
public JpegSubsample? Subsample { get; set; }
/// <summary>
/// Gets or sets the color type, that will be used to encode the image.
/// </summary>
public JpegColorType? ColorType { get; set; } public JpegColorType? ColorType { get; set; }
/// <summary> /// <summary>
@ -36,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var encoder = new JpegEncoderCore(this); var encoder = new JpegEncoderCore(this);
this.InitializeColorType<TPixel>(image); this.InitializeColorType(image);
encoder.Encode(image, stream); encoder.Encode(image, stream);
} }
@ -52,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var encoder = new JpegEncoderCore(this); var encoder = new JpegEncoderCore(this);
this.InitializeColorType<TPixel>(image); this.InitializeColorType(image);
return encoder.EncodeAsync(image, stream, cancellationToken); return encoder.EncodeAsync(image, stream, cancellationToken);
} }
@ -75,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
bool isGrayscale = bool isGrayscale =
typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16) || typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16) ||
typeof(TPixel) == typeof(La16) || typeof(TPixel) == typeof(La32); typeof(TPixel) == typeof(La16) || typeof(TPixel) == typeof(La32);
this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCrRatio420;
} }
} }
} }

45
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -33,20 +33,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
private readonly byte[] buffer = new byte[20]; private readonly byte[] buffer = new byte[20];
/// <summary>
/// Gets or sets the subsampling method to use.
/// </summary>
private JpegSubsample? subsample;
/// <summary> /// <summary>
/// The quality, that will be used to encode the image. /// The quality, that will be used to encode the image.
/// </summary> /// </summary>
private readonly int? quality; private readonly int? quality;
/// <summary> /// <summary>
/// Gets or sets the subsampling method to use. /// Gets or sets the colorspace to use.
/// </summary> /// </summary>
private readonly JpegColorType? colorType; private JpegColorType? colorType;
/// <summary> /// <summary>
/// The output stream. All attempted writes after the first error become no-ops. /// The output stream. All attempted writes after the first error become no-ops.
@ -56,11 +51,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <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)
{ {
this.quality = options.Quality; this.quality = options.Quality;
this.subsample = options.Subsample;
this.colorType = options.ColorType; this.colorType = options.ColorType;
} }
@ -118,21 +112,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
var scanEncoder = new HuffmanScanEncoder(stream); var scanEncoder = new HuffmanScanEncoder(stream);
if (this.colorType == JpegColorType.Luminance) if (this.colorType == JpegColorType.Luminance)
{ {
// luminance quantization table only // luminance quantization table only.
scanEncoder.EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken); scanEncoder.EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken);
} }
else else
{ {
// luminance and chrominance quantization tables // luminance and chrominance quantization tables.
switch (this.subsample) switch (this.colorType)
{ {
case JpegSubsample.Ratio444: case JpegColorType.YCbCrRatio444:
case JpegColorType.Luminance:
scanEncoder.Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); scanEncoder.Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
break; break;
case JpegSubsample.Ratio420: case JpegColorType.YCbCrRatio420:
scanEncoder.Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); scanEncoder.Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
break; break;
case JpegSubsample.Rgb: case JpegColorType.Rgb:
scanEncoder.EncodeRgb(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); scanEncoder.EncodeRgb(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
break; break;
} }
@ -151,7 +146,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <returns>The component Ids.</returns> /// <returns>The component Ids.</returns>
private byte[] GetComponentIds() private byte[] GetComponentIds()
{ {
if (this.subsample == JpegSubsample.Rgb) if (this.colorType == JpegColorType.Rgb)
{ {
return new byte[] { 82, 71, 66 }; return new byte[] { 82, 71, 66 };
} }
@ -268,7 +263,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
private void WriteDefineQuantizationTables(ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable) private void WriteDefineQuantizationTables(ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable)
{ {
// Marker + quantization table lengths // Marker + quantization table lengths.
int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size)); int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size));
this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen); this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen);
@ -404,7 +399,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="iccProfile">The ICC profile to write.</param> /// <param name="iccProfile">The ICC profile to write.</param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if any of the ICC profiles size exceeds the limit /// Thrown if any of the ICC profiles size exceeds the limit.
/// </exception> /// </exception>
private void WriteIccProfile(IccProfile iccProfile) private void WriteIccProfile(IccProfile iccProfile)
{ {
@ -424,7 +419,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return; return;
} }
// Calculate the number of markers we'll need, rounding up of course // Calculate the number of markers we'll need, rounding up of course.
int dataLength = data.Length; int dataLength = data.Length;
int count = dataLength / MaxData; int count = dataLength / MaxData;
@ -531,10 +526,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
else else
{ {
switch (this.subsample) switch (this.colorType)
{ {
case JpegSubsample.Rgb: case JpegColorType.YCbCrRatio444:
case JpegSubsample.Ratio444: case JpegColorType.Rgb:
subsamples = stackalloc byte[] subsamples = stackalloc byte[]
{ {
0x11, 0x11,
@ -542,7 +537,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
0x11 0x11
}; };
break; break;
case JpegSubsample.Ratio420: case JpegColorType.YCbCrRatio420:
subsamples = stackalloc byte[] subsamples = stackalloc byte[]
{ {
0x22, 0x22,
@ -685,9 +680,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
chromaQuality = Numerics.Clamp(chromaQuality, 1, 100); chromaQuality = Numerics.Clamp(chromaQuality, 1, 100);
chrominanceQuantTable = Quantization.ScaleChrominanceTable(chromaQuality); chrominanceQuantTable = Quantization.ScaleChrominanceTable(chromaQuality);
if (!this.subsample.HasValue) if (!this.colorType.HasValue)
{ {
this.subsample = chromaQuality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; this.colorType = chromaQuality >= 91 ? JpegColorType.YCbCrRatio444 : JpegColorType.YCbCrRatio420;
} }
} }
} }

28
src/ImageSharp/Formats/Jpeg/JpegSubsample.cs

@ -1,28 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Jpeg
{
/// <summary>
/// Enumerates the chroma subsampling method applied to the image.
/// </summary>
public enum JpegSubsample
{
/// <summary>
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
/// thus there is no chroma subsampling.
/// </summary>
Ratio444,
/// <summary>
/// Medium Quality - The horizontal sampling is halved and the Cb and Cr channels are only
/// sampled on each alternate line.
/// </summary>
Ratio420,
/// <summary>
/// The pixel data will be preserved as RGB without any sub sampling.
/// </summary>
Rgb,
}
}

2
src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
var image = Image.LoadPixelData<Rgb24>(rows, width, height); var image = Image.LoadPixelData<Rgb24>(rows, width, height);
image.Save(memoryStream, new JpegEncoder() image.Save(memoryStream, new JpegEncoder()
{ {
Subsample = JpegSubsample.Rgb ColorType = JpegColorType.Rgb
}); });
memoryStream.Position = 0; memoryStream.Position = 0;
memoryStream.WriteTo(this.Output); memoryStream.WriteTo(this.Output);

4
tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs

@ -40,8 +40,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
this.bmpCore = Image.Load<Rgba32>(this.bmpStream); this.bmpCore = Image.Load<Rgba32>(this.bmpStream);
this.bmpCore.Metadata.ExifProfile = null; this.bmpCore.Metadata.ExifProfile = null;
this.encoder420 = new JpegEncoder { Quality = this.Quality, Subsample = JpegSubsample.Ratio420 }; this.encoder420 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
this.encoder444 = new JpegEncoder { Quality = this.Quality, Subsample = JpegSubsample.Ratio444 }; this.encoder444 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio444 };
this.bmpStream.Position = 0; this.bmpStream.Position = 0;
this.bmpDrawing = SDImage.FromStream(this.bmpStream); this.bmpDrawing = SDImage.FromStream(this.bmpStream);

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

@ -31,15 +31,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ TestImages.Jpeg.Progressive.Fb, 75 } { TestImages.Jpeg.Progressive.Fb, 75 }
}; };
public static readonly TheoryData<JpegSubsample, int> BitsPerPixel_Quality = public static readonly TheoryData<JpegColorType, int> BitsPerPixel_Quality =
new TheoryData<JpegSubsample, int> new TheoryData<JpegColorType, int>
{ {
{ JpegSubsample.Ratio420, 40 }, { JpegColorType.Rgb, 40 },
{ JpegSubsample.Ratio420, 60 }, { JpegColorType.Rgb, 60 },
{ JpegSubsample.Ratio420, 100 }, { JpegColorType.Rgb, 100 },
{ JpegSubsample.Ratio444, 40 }, { JpegColorType.YCbCrRatio420, 40 },
{ JpegSubsample.Ratio444, 60 }, { JpegColorType.YCbCrRatio420, 60 },
{ JpegSubsample.Ratio444, 100 }, { JpegColorType.YCbCrRatio420, 100 },
{ JpegColorType.YCbCrRatio444, 40 },
{ JpegColorType.YCbCrRatio444, 60 },
{ JpegColorType.YCbCrRatio444, 100 },
}; };
public static readonly TheoryData<int> Grayscale_Quality = public static readonly TheoryData<int> Grayscale_Quality =
@ -91,8 +94,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 600, 400, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 600, 400, PixelTypes.Rgba32)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)]
public void EncodeBaseline_WorksWithDifferentSizes<TPixel>(TestImageProvider<TPixel> provider, JpegSubsample subsample, int quality) public void EncodeBaseline_WorksWithDifferentSizes<TPixel>(TestImageProvider<TPixel> provider, JpegColorType colorType, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality); where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, colorType, quality);
[Theory] [Theory]
[WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)] [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)]
@ -102,33 +105,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)]
public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, int quality) public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, null, quality, JpegColorType.Luminance); where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, JpegColorType.Luminance, quality);
[Theory] [Theory]
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
public void EncodeBaseline_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider, JpegSubsample subsample, int quality) public void EncodeBaseline_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider, JpegColorType colorType, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality); where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, colorType, quality);
[Theory] [Theory]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, JpegSubsample.Ratio444)] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, JpegColorType.YCbCrRatio444)]
[WithTestPatternImages(587, 821, PixelTypes.Rgba32, JpegSubsample.Ratio444)] [WithTestPatternImages(587, 821, PixelTypes.Rgba32, JpegColorType.YCbCrRatio444)]
[WithTestPatternImages(677, 683, PixelTypes.Bgra32, JpegSubsample.Ratio420)] [WithTestPatternImages(677, 683, PixelTypes.Bgra32, JpegColorType.YCbCrRatio420)]
[WithSolidFilledImages(400, 400, "Red", PixelTypes.Bgr24, JpegSubsample.Ratio420)] [WithSolidFilledImages(400, 400, "Red", PixelTypes.Bgr24, JpegColorType.YCbCrRatio420)]
public void EncodeBaseline_WorksWithDiscontiguousBuffers<TPixel>(TestImageProvider<TPixel> provider, JpegSubsample subsample) public void EncodeBaseline_WorksWithDiscontiguousBuffers<TPixel>(TestImageProvider<TPixel> provider, JpegColorType colorType)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
ImageComparer comparer = subsample == JpegSubsample.Ratio444 ImageComparer comparer = colorType == JpegColorType.YCbCrRatio444
? ImageComparer.TolerantPercentage(0.1f) ? ImageComparer.TolerantPercentage(0.1f)
: ImageComparer.TolerantPercentage(5f); : ImageComparer.TolerantPercentage(5f);
provider.LimitAllocatorBufferCapacity().InBytesSqrt(200); provider.LimitAllocatorBufferCapacity().InBytesSqrt(200);
TestJpegEncoderCore(provider, subsample, 100, JpegColorType.YCbCr, comparer); TestJpegEncoderCore(provider, colorType, 100, comparer);
} }
/// <summary> /// <summary>
/// Anton's SUPER-SCIENTIFIC tolerance threshold calculation /// Anton's SUPER-SCIENTIFIC tolerance threshold calculation
/// </summary> /// </summary>
private static ImageComparer GetComparer(int quality, JpegSubsample? subsample) private static ImageComparer GetComparer(int quality, JpegColorType? colorType)
{ {
float tolerance = 0.015f; // ~1.5% float tolerance = 0.015f; // ~1.5%
@ -136,10 +139,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
tolerance *= 10f; tolerance *= 10f;
} }
else if (quality < 75 || subsample == JpegSubsample.Ratio420) else if (quality < 75 || colorType == JpegColorType.YCbCrRatio420)
{ {
tolerance *= 5f; tolerance *= 5f;
if (subsample == JpegSubsample.Ratio420) if (colorType == JpegColorType.YCbCrRatio420)
{ {
tolerance *= 2f; tolerance *= 2f;
} }
@ -150,9 +153,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private static void TestJpegEncoderCore<TPixel>( private static void TestJpegEncoderCore<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
JpegSubsample? subsample, JpegColorType colorType = JpegColorType.YCbCrRatio420,
int quality = 100, int quality = 100,
JpegColorType colorType = JpegColorType.YCbCr,
ImageComparer comparer = null) ImageComparer comparer = null)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
@ -163,13 +165,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var encoder = new JpegEncoder var encoder = new JpegEncoder
{ {
Subsample = subsample,
Quality = quality, Quality = quality,
ColorType = colorType ColorType = colorType
}; };
string info = $"{subsample}-Q{quality}"; string info = $"{colorType}-Q{quality}";
comparer ??= GetComparer(quality, subsample); comparer ??= GetComparer(quality, colorType);
// Does DebugSave & load reference CompareToReferenceInput(): // Does DebugSave & load reference CompareToReferenceInput():
image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png"); image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png");
@ -312,9 +313,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
[Theory] [Theory]
[InlineData(JpegSubsample.Ratio420)] [InlineData(JpegColorType.YCbCrRatio420)]
[InlineData(JpegSubsample.Ratio444)] [InlineData(JpegColorType.YCbCrRatio444)]
public async Task Encode_IsCancellable(JpegSubsample subsample) public async Task Encode_IsCancellable(JpegColorType colorType)
{ {
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
using var pausedStream = new PausedStream(new MemoryStream()); using var pausedStream = new PausedStream(new MemoryStream());
@ -336,7 +337,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
using var image = new Image<Rgba32>(5000, 5000); using var image = new Image<Rgba32>(5000, 5000);
await Assert.ThrowsAsync<TaskCanceledException>(async () => await Assert.ThrowsAsync<TaskCanceledException>(async () =>
{ {
var encoder = new JpegEncoder() { Subsample = subsample }; var encoder = new JpegEncoder() { ColorType = colorType };
await image.SaveAsync(pausedStream, encoder, cts.Token); await image.SaveAsync(pausedStream, encoder, cts.Token);
}); });
} }

2
tests/ImageSharp.Tests/Formats/Jpg/JpegMetadataTests.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var clone = (JpegMetadata)meta.DeepClone(); var clone = (JpegMetadata)meta.DeepClone();
clone.Quality = 99; clone.Quality = 99;
clone.ColorType = JpegColorType.YCbCr; clone.ColorType = JpegColorType.YCbCrRatio420;
Assert.False(meta.Quality.Equals(clone.Quality)); Assert.False(meta.Quality.Equals(clone.Quality));
Assert.False(meta.ColorType.Equals(clone.ColorType)); Assert.False(meta.ColorType.Equals(clone.ColorType));

6
tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs

@ -21,10 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Trait("Format", "Jpg")] [Trait("Format", "Jpg")]
public class SpectralJpegTests public class SpectralJpegTests
{ {
public SpectralJpegTests(ITestOutputHelper output) public SpectralJpegTests(ITestOutputHelper output) => this.Output = output;
{
this.Output = output;
}
private ITestOutputHelper Output { get; } private ITestOutputHelper Output { get; }
@ -46,7 +43,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray();
[Theory(Skip = "Debug only, enable manually!")] [Theory(Skip = "Debug only, enable manually!")]
//[Theory]
[WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)]
public void Decoder_ParseStream_SaveSpectralResult<TPixel>(TestImageProvider<TPixel> provider) public void Decoder_ParseStream_SaveSpectralResult<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>

14
tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
img.Dispose(); img.Dispose();
}, },
#pragma warning disable SA1515 // Single-line comment should be preceded by blank line #pragma warning disable SA1515 // Single-line comment should be preceded by blank line
// ReSharper disable once ExplicitCallerInfoArgument // ReSharper disable once ExplicitCallerInfoArgument
$"Decode {fileName}"); $"Decode {fileName}");
#pragma warning restore SA1515 // Single-line comment should be preceded by blank line #pragma warning restore SA1515 // Single-line comment should be preceded by blank line
} }
@ -92,11 +92,11 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
// Benchmark, enable manually! // Benchmark, enable manually!
[Theory(Skip = ProfilingSetup.SkipProfilingTests)] [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
[InlineData(1, 75, JpegSubsample.Ratio420)] [InlineData(1, 75, JpegColorType.YCbCrRatio420)]
[InlineData(30, 75, JpegSubsample.Ratio420)] [InlineData(30, 75, JpegColorType.YCbCrRatio420)]
[InlineData(30, 75, JpegSubsample.Ratio444)] [InlineData(30, 75, JpegColorType.YCbCrRatio444)]
[InlineData(30, 100, JpegSubsample.Ratio444)] [InlineData(30, 100, JpegColorType.YCbCrRatio444)]
public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) public void EncodeJpeg(int executionCount, int quality, JpegColorType colorType)
{ {
// do not run this on CI even by accident // do not run this on CI even by accident
if (TestEnvironment.RunsOnCI) if (TestEnvironment.RunsOnCI)
@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
{ {
foreach (Image<Rgba32> img in testImages) foreach (Image<Rgba32> img in testImages)
{ {
var options = new JpegEncoder { Quality = quality, Subsample = subsample }; var options = new JpegEncoder { Quality = quality, ColorType = colorType };
img.Save(ms, options); img.Save(ms, options);
ms.Seek(0, SeekOrigin.Begin); ms.Seek(0, SeekOrigin.Begin);
} }

Loading…
Cancel
Save