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);
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.
int scale;
if (qlty < 50)
@ -921,32 +918,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
0x01
};
switch (this.subsample)
if (this.colorType == JpegColorType.Luminance)
{
case JpegSubsample.Grayscale:
subsamples = stackalloc byte[]
{
0x11,
0x00,
0x00
};
break;
case JpegSubsample.Ratio444:
subsamples = stackalloc byte[]
{
0x11,
0x11,
0x11
};
break;
case JpegSubsample.Ratio420:
subsamples = stackalloc byte[]
{
0x22,
0x11,
0x11
};
break;
subsamples = stackalloc byte[]
{
0x11,
0x00,
0x00
};
}
else
{
switch (this.subsample)
{
case JpegSubsample.Ratio444:
subsamples = stackalloc byte[]
{
0x11,
0x11,
0x11
};
break;
case JpegSubsample.Ratio420:
subsamples = stackalloc byte[]
{
0x22,
0x11,
0x11
};
break;
}
}
// 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);
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);
break;
case JpegSubsample.Ratio444:
this.Encode444(image, cancellationToken, ref emitBufferBase);
break;
case JpegSubsample.Ratio420:
this.Encode420(image, cancellationToken, ref emitBufferBase);
break;
this.EncodeGrayscale(image, cancellationToken, ref emitBufferBase);
}
else
{
switch (this.subsample)
{
case JpegSubsample.Ratio444:
this.Encode444(image, cancellationToken, ref emitBufferBase);
break;
case JpegSubsample.Ratio420:
this.Encode420(image, cancellationToken, ref emitBufferBase);
break;
}
}
// Pad the last byte with 1's.

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

@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
public enum JpegSubsample
{
/// <summary>
/// Only luminance - No chrome channels.
/// </summary>
Grayscale,
/// <summary>
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
/// 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 },
};
public static readonly TheoryData<int> Grayscale_Quality =
new TheoryData<int>
{
{ 40 },
{ 60 },
{ 100 }
};
public static readonly TheoryData<string, int, int, PixelResolutionUnit> RatioFiles =
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);
[Theory]
[WithFile(TestImages.Png.BikeGrayscale, nameof(BitsPerPixel_Quality), PixelTypes.L8)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, 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.L16)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La16)]
[WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 100, 100, 100, 255, PixelTypes.La32)]
public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, JpegSubsample subsample, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, subsample, quality, JpegColorType.Luminance);
[WithFile(TestImages.Png.BikeGrayscale, nameof(Grayscale_Quality), PixelTypes.L8)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.Rgba32, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L8, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.L16, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La16, 100)]
[WithSolidFilledImages(1, 1, 100, 100, 100, 255, PixelTypes.La32, 100)]
public void EncodeBaseline_Grayscale<TPixel>(TestImageProvider<TPixel> provider, int quality)
where TPixel : unmanaged, IPixel<TPixel> => TestJpegEncoderCore(provider, null, quality, JpegColorType.Luminance);
[Theory]
[WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
/// <summary>
/// Anton's SUPER-SCIENTIFIC tolerance threshold calculation
/// </summary>
private static ImageComparer GetComparer(int quality, JpegSubsample subsample)
private static ImageComparer GetComparer(int quality, JpegSubsample? subsample)
{
float tolerance = 0.015f; // ~1.5%
@ -140,7 +148,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
private static void TestJpegEncoderCore<TPixel>(
TestImageProvider<TPixel> provider,
JpegSubsample subsample,
JpegSubsample? subsample,
int quality = 100,
JpegColorType colorType = JpegColorType.YCbCr,
ImageComparer comparer = null)
@ -311,7 +319,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs)
{
using var image = new Image<Rgba32>(5000, 5000);
using MemoryStream stream = new MemoryStream();
using var stream = new MemoryStream();
var cts = new CancellationTokenSource();
if (cancellationDelayMs == 0)
{

Loading…
Cancel
Save