Browse Source

Merge remote-tracking branch 'origin/master' into bp/tiffjpegcompression

# Conflicts:
#	src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
pull/1734/head
Brian Popow 5 years ago
parent
commit
19d02ebadb
  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

@ -407,10 +407,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
/// <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 component id's.
/// </summary>
/// <returns>The <see cref="JpegColorSpace"/></returns>
private JpegColorSpace DeduceJpegColorSpace(byte componentCount)
private JpegColorSpace DeduceJpegColorSpace(byte componentCount, JpegComponent[] components)
{
if (componentCount == 1)
{
@ -424,6 +424,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
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.
// We ignore that and always fall back to the default colorspace.
return JpegColorSpace.YCbCr;
@ -898,60 +904,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// 1 byte: Number of components
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);
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
const int componentBytes = 3;
if (remaining != componentCount * componentBytes)
{
JpegThrowHelper.ThrowBadMarker("SOFn", remaining);
}
// components*3 bytes: component data
stream.Read(this.temp, 0, remaining);
// No need to pool this. They max out at 4
this.Frame.ComponentIds = new byte[componentCount];
this.Frame.ComponentOrder = new byte[componentCount];
this.Frame.Components = new JpegComponent[componentCount];
// components*3 bytes: component data
stream.Read(this.temp, 0, remaining);
int maxH = 0;
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
this.Frame.ComponentIds = new byte[componentCount];
this.Frame.ComponentOrder = new byte[componentCount];
this.Frame.Components = new JpegComponent[componentCount];
if (maxH < h)
{
maxH = h;
}
int maxH = 0;
int maxV = 0;
int index = 0;
for (int i = 0; i < componentCount; i++)
if (maxV < v)
{
byte hv = this.temp[index + 1];
int h = (hv >> 4) & 15;
int v = hv & 15;
maxV = v;
}
if (maxH < h)
{
maxH = h;
}
var component = new JpegComponent(this.Configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i);
if (maxV < v)
{
maxV = v;
}
this.Frame.Components[i] = component;
this.Frame.ComponentIds[i] = component.Id;
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.Frame.ComponentIds[i] = component.Id;
this.ColorSpace = this.DeduceJpegColorSpace(componentCount, this.Frame.Components);
index += componentBytes;
}
this.Metadata.GetJpegMetadata().ColorType = this.ColorSpace == JpegColorSpace.Grayscale ? JpegColorType.Luminance : JpegColorType.YCbCr;
if (!metadataOnly)
{
this.Frame.Init(maxH, maxV);
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));
}
// 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!
// The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm"
// 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 BadSubSampling1076 = "Jpg/issues/issue-1076-invalid-subsampling.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
{

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