Browse Source

Refactor out JpegSubsample.Grayscale

pull/1586/head
Ynse Hoornenborg 5 years ago
parent
commit
096ef3d3aa
  1. 81
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  2. 5
      src/ImageSharp/Formats/Jpeg/JpegSubsample.cs
  3. 30
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

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

@ -189,9 +189,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100); int qlty = Numerics.Clamp(this.quality ?? metadata.GetJpegMetadata().Quality, 1, 100);
this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420; this.subsample ??= qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420;
// Force SubSample into Grayscale for single component ColorType.
this.subsample = (componentCount == 1) ? JpegSubsample.Grayscale : this.subsample;
// Convert from a quality rating to a scaling factor. // Convert from a quality rating to a scaling factor.
int scale; int scale;
if (qlty < 50) if (qlty < 50)
@ -921,32 +918,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
0x01 0x01
}; };
switch (this.subsample) if (this.colorType == JpegColorType.Luminance)
{ {
case JpegSubsample.Grayscale: subsamples = stackalloc byte[]
subsamples = stackalloc byte[] {
{ 0x11,
0x11, 0x00,
0x00, 0x00
0x00 };
}; }
break; else
case JpegSubsample.Ratio444: {
subsamples = stackalloc byte[] switch (this.subsample)
{ {
0x11, case JpegSubsample.Ratio444:
0x11, subsamples = stackalloc byte[]
0x11 {
}; 0x11,
break; 0x11,
case JpegSubsample.Ratio420: 0x11
subsamples = stackalloc byte[] };
{ break;
0x22, case JpegSubsample.Ratio420:
0x11, subsamples = stackalloc byte[]
0x11 {
}; 0x22,
break; 0x11,
0x11
};
break;
}
} }
// Length (high byte, low byte), 8 + components * 3. // Length (high byte, low byte), 8 + components * 3.
@ -1025,17 +1026,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream.Write(this.buffer, 0, sosSize + 2); this.outputStream.Write(this.buffer, 0, sosSize + 2);
ref byte emitBufferBase = ref MemoryMarshal.GetReference<byte>(this.emitBuffer); ref byte emitBufferBase = ref MemoryMarshal.GetReference<byte>(this.emitBuffer);
switch (this.subsample) if (this.colorType == JpegColorType.Luminance)
{ {
case JpegSubsample.Grayscale: this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase);
this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase); }
break; else
case JpegSubsample.Ratio444: {
this.Encode444(image, cancellationToken, ref emitBufferBase); switch (this.subsample)
break; {
case JpegSubsample.Ratio420: case JpegSubsample.Ratio444:
this.Encode420(image, cancellationToken, ref emitBufferBase); this.Encode444(image, cancellationToken, ref emitBufferBase);
break; break;
case JpegSubsample.Ratio420:
this.Encode420(image, cancellationToken, ref emitBufferBase);
break;
}
} }
// Pad the last byte with 1's. // Pad the last byte with 1's.

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

@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
public enum JpegSubsample public enum JpegSubsample
{ {
/// <summary>
/// Only luminance - No chrome channels.
/// </summary>
Grayscale,
/// <summary> /// <summary>
/// High Quality - Each of the three Y'CbCr components have the same sample rate, /// High Quality - Each of the three Y'CbCr components have the same sample rate,
/// thus there is no chroma subsampling. /// thus there is no chroma subsampling.

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

@ -40,6 +40,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ JpegSubsample.Ratio444, 100 }, { JpegSubsample.Ratio444, 100 },
}; };
public static readonly TheoryData<int> Grayscale_Quality =
new TheoryData<int>
{
{ 40 },
{ 60 },
{ 100 }
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles = public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
new TheoryData<string, int, int, PixelResolutionUnit> new TheoryData<string, int, int, PixelResolutionUnit>
{ {
@ -85,14 +93,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality); where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality);
[Theory] [Theory]
[WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)] [WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.Rgba32)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgba32, 100)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L8)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L8, 100)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.L16)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L16, 100)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La16)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La32)] [WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)]
public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, JpegSubsample subsample, int quality) public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance); where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, null, quality, JpegColorType.Luminance);
[Theory] [Theory]
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
/// <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, JpegSubsample? subsample)
{ {
float tolerance = 0.015f; // ~1.5% float tolerance = 0.015f; // ~1.5%
@ -140,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private static void TestJpegEncoderCore<TPixel>( private static void TestJpegEncoderCore<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
JpegSubsample subsample, JpegSubsample? subsample,
int quality = 100, int quality = 100,
JpegColorType colorType = JpegColorType.YCbCr, JpegColorType colorType = JpegColorType.YCbCr,
ImageComparer comparer = null) ImageComparer comparer = null)
@ -311,7 +319,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs) public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs)
{ {
using var image = new Image<Rgba32>(5000, 5000); using var image = new Image<Rgba32>(5000, 5000);
using MemoryStream stream = new MemoryStream(); using var stream = new MemoryStream();
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
if (cancellationDelayMs == 0) if (cancellationDelayMs == 0)
{ {

Loading…
Cancel
Save