Browse Source

Merge pull request #1732 from SixLabors/bp/deduceColorSpaceFix

If component id's are R, G, B in ASCII the color space should be RGB
pull/1742/head
Brian Popow 5 years ago
committed by GitHub
parent
commit
9b09775d5e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 86
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 13
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  3. 1
      tests/ImageSharp.Tests/TestImages.cs
  4. 3
      tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg

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

@ -345,10 +345,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
/// <summary> /// <summary>
/// Returns the correct colorspace based on the image component count /// Returns the correct colorspace based on the image component count and the jpeg frame components.
/// </summary> /// </summary>
/// <returns>The <see cref="JpegColorSpace"/></returns> /// <returns>The <see cref="JpegColorSpace"/></returns>
private JpegColorSpace DeduceJpegColorSpace(byte componentCount) private JpegColorSpace DeduceJpegColorSpace(byte componentCount, JpegComponent[] components)
{ {
if (componentCount == 1) if (componentCount == 1)
{ {
@ -362,6 +362,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return JpegColorSpace.RGB; return JpegColorSpace.RGB;
} }
// If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr.
if (components[2].Id == 66 && components[1].Id == 71 && components[0].Id == 82)
{
return JpegColorSpace.RGB;
}
// Some images are poorly encoded and contain incorrect colorspace transform metadata. // Some images are poorly encoded and contain incorrect colorspace transform metadata.
// We ignore that and always fall back to the default colorspace. // We ignore that and always fall back to the default colorspace.
return JpegColorSpace.YCbCr; return JpegColorSpace.YCbCr;
@ -836,60 +842,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// 1 byte: Number of components // 1 byte: Number of components
byte componentCount = this.temp[5]; byte componentCount = this.temp[5];
this.ColorSpace = this.DeduceJpegColorSpace(componentCount);
this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr;
this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount); this.Frame = new JpegFrame(frameMarker, precision, frameWidth, frameHeight, componentCount);
if (!metadataOnly) remaining -= length;
// Validate: remaining part must be equal to components * 3
const int componentBytes = 3;
if (remaining != componentCount * componentBytes)
{ {
remaining -= length; JpegThrowHelper.ThrowBadMarker("SOFn", remaining);
}
// Validate: remaining part must be equal to components * 3 // components*3 bytes: component data
const int componentBytes = 3; stream.Read(this.temp, 0, remaining);
if (remaining != componentCount * componentBytes)
{ // No need to pool this. They max out at 4
JpegThrowHelper.ThrowBadMarker("SOFn", remaining); this.Frame.ComponentIds = new byte[componentCount];
} this.Frame.ComponentOrder = new byte[componentCount];
this.Frame.Components = new JpegComponent[componentCount];
// components*3 bytes: component data int maxH = 0;
stream.Read(this.temp, 0, remaining); int maxV = 0;
int index = 0;
for (int i = 0; i < componentCount; i++)
{
byte hv = this.temp[index + 1];
int h = (hv >> 4) & 15;
int v = hv & 15;
// No need to pool this. They max out at 4 if (maxH < h)
this.Frame.ComponentIds = new byte[componentCount]; {
this.Frame.ComponentOrder = new byte[componentCount]; maxH = h;
this.Frame.Components = new JpegComponent[componentCount]; }
int maxH = 0; if (maxV < v)
int maxV = 0;
int index = 0;
for (int i = 0; i < componentCount; i++)
{ {
byte hv = this.temp[index + 1]; maxV = v;
int h = (hv >> 4) & 15; }
int v = hv & 15;
if (maxH < h) var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
{
maxH = h;
}
if (maxV < v) this.Frame.Components[i] = component;
{ this.Frame.ComponentIds[i] = component.Id;
maxV = v;
}
var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); index += componentBytes;
}
this.Frame.Components[i] = component; this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components);
this.Frame.ComponentIds[i] = component.Id;
index += componentBytes; this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr;
}
if (!metadataOnly)
{
this.Frame.Init(maxH, maxV); this.Frame.Init(maxH, maxV);
this.scanDecoder.InjectFrameData(this.Frame, this); this.scanDecoder.InjectFrameData(this.Frame, this);
} }
} }

13
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -174,6 +174,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
await Assert.ThrowsAsync<TaskCanceledException>(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token)); await Assert.ThrowsAsync<TaskCanceledException>(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token));
} }
// https://github.com/SixLabors/ImageSharp/pull/1732
[Theory]
[WithFile(TestImages.Jpeg.Issues.WrongColorSpace, PixelTypes.Rgba32)]
public void Issue1732_DecodesWithRgbColorSpace<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new JpegDecoder()))
{
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
}
// DEBUG ONLY! // DEBUG ONLY!
// The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm"
// into "\tests\Images\ActualOutput\JpegDecoderTests\" // into "\tests\Images\ActualOutput\JpegDecoderTests\"

1
tests/ImageSharp.Tests/TestImages.cs

@ -237,6 +237,7 @@ namespace SixLabors.ImageSharp.Tests
public const string ExifResize1049 = "Jpg/issues/issue1049-exif-resize.jpg"; public const string ExifResize1049 = "Jpg/issues/issue1049-exif-resize.jpg";
public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg"; public const string BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.jpg";
public const string IdentifyMultiFrame1211 = "Jpg/issues/issue-1221-identify-multi-frame.jpg"; public const string IdentifyMultiFrame1211 = "Jpg/issues/issue-1221-identify-multi-frame.jpg";
public const string WrongColorSpace = "Jpg/issues/Issue1732-WrongColorSpace.jpg";
public static class Fuzz public static class Fuzz
{ {

3
tests/Images/Input/Jpg/issues/Issue1732-WrongColorSpace.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3c72235954cdfb9d0cc7f09c537704e617313dc77708b4dca27b47c94c5e67a6
size 2852
Loading…
Cancel
Save