Skip to content

Commit a531a2d

Browse files
committed
Remove JpegSubsample and use JpegColorType instead
1 parent 19d02eb commit a531a2d

File tree

12 files changed

+100
-124
lines changed

12 files changed

+100
-124
lines changed

src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,7 @@ internal interface IJpegEncoderOptions
1616
public int? Quality { get; set; }
1717

1818
/// <summary>
19-
/// Gets the subsample ration, that will be used to encode the image.
20-
/// </summary>
21-
/// <value>The subsample ratio of the jpg image.</value>
22-
JpegSubsample? Subsample { get; }
23-
24-
/// <summary>
25-
/// Gets the color type.
19+
/// Gets the color type, that will be used to encode the image.
2620
/// </summary>
2721
JpegColorType? ColorType { get; }
2822
}

src/ImageSharp/Formats/Jpeg/JpegColorType.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,26 @@ public enum JpegColorType : byte
1010
{
1111
/// <summary>
1212
/// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification.
13+
/// Medium Quality - The horizontal sampling is halved and the Cb and Cr channels are only
14+
/// sampled on each alternate line.
1315
/// </summary>
14-
YCbCr = 0,
16+
YCbCrRatio420 = 0,
17+
18+
/// <summary>
19+
/// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification.
20+
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
21+
/// thus there is no chroma subsampling.
22+
/// </summary>
23+
YCbCrRatio444 = 1,
1524

1625
/// <summary>
1726
/// Single channel, luminance.
1827
/// </summary>
19-
Luminance = 1
28+
Luminance = 2,
29+
30+
/// <summary>
31+
/// The pixel data will be preserved as RGB without any sub sampling.
32+
/// </summary>
33+
Rgb,
2034
}
2135
}

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDeco
312312
case JpegConstants.Markers.SOS:
313313
if (!metadataOnly)
314314
{
315-
this.ProcessStartOfScanMarker(stream, remaining, cancellationToken);
315+
this.ProcessStartOfScanMarker(stream, remaining);
316316
break;
317317
}
318318
else
@@ -953,7 +953,18 @@ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining,
953953

954954
this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components);
955955

956-
this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr;
956+
switch (this.ColorSpace)
957+
{
958+
case JpegColorSpace.Grayscale:
959+
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.Luminance;
960+
break;
961+
case JpegColorSpace.RGB:
962+
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.Rgb;
963+
break;
964+
default:
965+
this.Metadata.GetJpegMetadata().ColorType = JpegColorType.YCbCrRatio420;
966+
break;
967+
}
957968

958969
if (!metadataOnly)
959970
{
@@ -1051,7 +1062,7 @@ private void ProcessDefineRestartIntervalMarker(BufferedReadStream stream, int r
10511062
/// <summary>
10521063
/// Processes the SOS (Start of scan marker).
10531064
/// </summary>
1054-
private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining, CancellationToken cancellationToken)
1065+
private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
10551066
{
10561067
if (this.Frame is null)
10571068
{

src/ImageSharp/Formats/Jpeg/JpegEncoder.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,7 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions
1616
/// <inheritdoc/>
1717
public int? Quality { get; set; }
1818

19-
/// <summary>
20-
/// Gets or sets the subsample ration, that will be used to encode the image.
21-
/// </summary>
22-
public JpegSubsample? Subsample { get; set; }
23-
24-
/// <summary>
25-
/// Gets or sets the color type, that will be used to encode the image.
26-
/// </summary>
19+
/// <inheritdoc/>
2720
public JpegColorType? ColorType { get; set; }
2821

2922
/// <summary>
@@ -36,7 +29,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
3629
where TPixel : unmanaged, IPixel<TPixel>
3730
{
3831
var encoder = new JpegEncoderCore(this);
39-
this.InitializeColorType<TPixel>(image);
32+
this.InitializeColorType(image);
4033
encoder.Encode(image, stream);
4134
}
4235

@@ -52,7 +45,7 @@ public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, Cancellation
5245
where TPixel : unmanaged, IPixel<TPixel>
5346
{
5447
var encoder = new JpegEncoderCore(this);
55-
this.InitializeColorType<TPixel>(image);
48+
this.InitializeColorType(image);
5649
return encoder.EncodeAsync(image, stream, cancellationToken);
5750
}
5851

@@ -75,7 +68,7 @@ private void InitializeColorType<TPixel>(Image<TPixel> image)
7568
bool isGrayscale =
7669
typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16) ||
7770
typeof(TPixel) == typeof(La16) || typeof(TPixel) == typeof(La32);
78-
this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCr;
71+
this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCrRatio420;
7972
}
8073
}
8174
}

src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,15 @@ internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals
3333
/// </summary>
3434
private readonly byte[] buffer = new byte[20];
3535

36-
/// <summary>
37-
/// Gets or sets the subsampling method to use.
38-
/// </summary>
39-
private JpegSubsample? subsample;
40-
4136
/// <summary>
4237
/// The quality, that will be used to encode the image.
4338
/// </summary>
4439
private readonly int? quality;
4540

4641
/// <summary>
47-
/// Gets or sets the subsampling method to use.
42+
/// Gets or sets the colorspace to use.
4843
/// </summary>
49-
private readonly JpegColorType? colorType;
44+
private JpegColorType? colorType;
5045

5146
/// <summary>
5247
/// The output stream. All attempted writes after the first error become no-ops.
@@ -56,11 +51,10 @@ internal sealed unsafe class JpegEncoderCore : IImageEncoderInternals
5651
/// <summary>
5752
/// Initializes a new instance of the <see cref="JpegEncoderCore"/> class.
5853
/// </summary>
59-
/// <param name="options">The options</param>
54+
/// <param name="options">The options.</param>
6055
public JpegEncoderCore(IJpegEncoderOptions options)
6156
{
6257
this.quality = options.Quality;
63-
this.subsample = options.Subsample;
6458
this.colorType = options.ColorType;
6559
}
6660

@@ -118,21 +112,22 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
118112
var scanEncoder = new HuffmanScanEncoder(stream);
119113
if (this.colorType == JpegColorType.Luminance)
120114
{
121-
// luminance quantization table only
115+
// luminance quantization table only.
122116
scanEncoder.EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken);
123117
}
124118
else
125119
{
126-
// luminance and chrominance quantization tables
127-
switch (this.subsample)
120+
// luminance and chrominance quantization tables.
121+
switch (this.colorType)
128122
{
129-
case JpegSubsample.Ratio444:
123+
case JpegColorType.YCbCrRatio444:
124+
case JpegColorType.Luminance:
130125
scanEncoder.Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
131126
break;
132-
case JpegSubsample.Ratio420:
127+
case JpegColorType.YCbCrRatio420:
133128
scanEncoder.Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
134129
break;
135-
case JpegSubsample.Rgb:
130+
case JpegColorType.Rgb:
136131
scanEncoder.EncodeRgb(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken);
137132
break;
138133
}
@@ -151,7 +146,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
151146
/// <returns>The component Ids.</returns>
152147
private byte[] GetComponentIds()
153148
{
154-
if (this.subsample == JpegSubsample.Rgb)
149+
if (this.colorType == JpegColorType.Rgb)
155150
{
156151
return new byte[] { 82, 71, 66 };
157152
}
@@ -268,7 +263,7 @@ private void WriteDefineHuffmanTables(int componentCount)
268263
/// </summary>
269264
private void WriteDefineQuantizationTables(ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable)
270265
{
271-
// Marker + quantization table lengths
266+
// Marker + quantization table lengths.
272267
int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size));
273268
this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen);
274269

@@ -404,7 +399,7 @@ private void WriteAppHeader(int length, byte appMarker)
404399
/// </summary>
405400
/// <param name="iccProfile">The ICC profile to write.</param>
406401
/// <exception cref="ImageFormatException">
407-
/// Thrown if any of the ICC profiles size exceeds the limit
402+
/// Thrown if any of the ICC profiles size exceeds the limit.
408403
/// </exception>
409404
private void WriteIccProfile(IccProfile iccProfile)
410405
{
@@ -424,7 +419,7 @@ private void WriteIccProfile(IccProfile iccProfile)
424419
return;
425420
}
426421

427-
// Calculate the number of markers we'll need, rounding up of course
422+
// Calculate the number of markers we'll need, rounding up of course.
428423
int dataLength = data.Length;
429424
int count = dataLength / MaxData;
430425

@@ -531,18 +526,18 @@ private void WriteStartOfFrame(int width, int height, int componentCount, byte[]
531526
}
532527
else
533528
{
534-
switch (this.subsample)
529+
switch (this.colorType)
535530
{
536-
case JpegSubsample.Rgb:
537-
case JpegSubsample.Ratio444:
531+
case JpegColorType.YCbCrRatio444:
532+
case JpegColorType.Rgb:
538533
subsamples = stackalloc byte[]
539534
{
540535
0x11,
541536
0x11,
542537
0x11
543538
};
544539
break;
545-
case JpegSubsample.Ratio420:
540+
case JpegColorType.YCbCrRatio420:
546541
subsamples = stackalloc byte[]
547542
{
548543
0x22,
@@ -685,9 +680,9 @@ private void InitQuantizationTables(int componentCount, JpegMetadata metadata, o
685680
chromaQuality = Numerics.Clamp(chromaQuality, 1, 100);
686681
chrominanceQuantTable = Quantization.ScaleChrominanceTable(chromaQuality);
687682

688-
if (!this.subsample.HasValue)
683+
if (!this.colorType.HasValue)
689684
{
690-
this.subsample = chromaQuality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420;
685+
this.colorType = chromaQuality >= 91 ? JpegColorType.YCbCrRatio444 : JpegColorType.YCbCrRatio420;
691686
}
692687
}
693688
}

src/ImageSharp/Formats/Jpeg/JpegSubsample.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public override void CompressStrip(Span<byte> rows, int height)
3535
var image = Image.LoadPixelData<Rgb24>(rows, width, height);
3636
image.Save(memoryStream, new JpegEncoder()
3737
{
38-
Subsample = JpegSubsample.Rgb
38+
ColorType = JpegColorType.Rgb
3939
});
4040
memoryStream.Position = 0;
4141
memoryStream.WriteTo(this.Output);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public void ReadImages()
4040

4141
this.bmpCore = Image.Load<Rgba32>(this.bmpStream);
4242
this.bmpCore.Metadata.ExifProfile = null;
43-
this.encoder420 = new JpegEncoder { Quality = this.Quality, Subsample = JpegSubsample.Ratio420 };
44-
this.encoder444 = new JpegEncoder { Quality = this.Quality, Subsample = JpegSubsample.Ratio444 };
43+
this.encoder420 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
44+
this.encoder444 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio444 };
4545

4646
this.bmpStream.Position = 0;
4747
this.bmpDrawing = SDImage.FromStream(this.bmpStream);

0 commit comments

Comments
 (0)