Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into MakerProfileResolver
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBobSquarePants committed Nov 29, 2023
2 parents 412e451 + cc4c0b1 commit ca1fd46
Show file tree
Hide file tree
Showing 33 changed files with 125 additions and 116 deletions.
28 changes: 26 additions & 2 deletions src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ public static bool VerifyAndParse(this TiffDecoderCore options, ExifProfile exif
options.OldJpegCompressionStartOfImageMarker = jpegInterchangeFormatValue.Value;
}

options.ParseColorType(exifProfile);
options.ParseCompression(frameMetadata.Compression, exifProfile);
options.ParseColorType(exifProfile);

bool isTiled = VerifyRequiredFieldsArePresent(exifProfile, frameMetadata, options.PlanarConfiguration);

Expand Down Expand Up @@ -194,7 +194,9 @@ private static bool VerifyRequiredFieldsArePresent(ExifProfile exifProfile, Tiff
}
}

if (frameMetadata.BitsPerPixel == null)
// For BiColor compressed images, the BitsPerPixel value will be set explicitly to 1, so we don't throw in those cases.
// See: https://github.com/SixLabors/ImageSharp/issues/2587
if (frameMetadata.BitsPerPixel == null && !IsBiColorCompression(frameMetadata.Compression))
{
TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!");
}
Expand Down Expand Up @@ -570,6 +572,11 @@ private static void ParseCompression(this TiffDecoderCore options, TiffCompressi
options.FaxCompressionOptions = FaxCompressionOptions.None;
}

// Some encoders do not set the BitsPerSample correctly, so we set those values here to the required values:
// https://github.com/SixLabors/ImageSharp/issues/2587
options.BitsPerSample = new TiffBitsPerSample(1, 0, 0);
options.BitsPerPixel = 1;

break;
}

Expand All @@ -585,12 +592,18 @@ private static void ParseCompression(this TiffDecoderCore options, TiffCompressi
options.FaxCompressionOptions = FaxCompressionOptions.None;
}

options.BitsPerSample = new TiffBitsPerSample(1, 0, 0);
options.BitsPerPixel = 1;

break;
}

case TiffCompression.Ccitt1D:
{
options.CompressionType = TiffDecoderCompressionType.HuffmanRle;
options.BitsPerSample = new TiffBitsPerSample(1, 0, 0);
options.BitsPerPixel = 1;

break;
}

Expand Down Expand Up @@ -645,4 +658,15 @@ private static void ParseCompression(this TiffDecoderCore options, TiffCompressi
}
}
}

private static bool IsBiColorCompression(TiffCompression? compression)
{
if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or
TiffCompression.CcittGroup4Fax)
{
return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public OctreeQuantizer(Configuration configuration, QuantizerOptions options)
public QuantizerOptions Options { get; }

/// <inheritdoc/>
public ReadOnlyMemory<TPixel> Palette
public readonly ReadOnlyMemory<TPixel> Palette
{
get
{
Expand All @@ -72,16 +72,14 @@ public ReadOnlyMemory<TPixel> Palette
/// <inheritdoc/>
public void AddPaletteColors(Buffer2DRegion<TPixel> pixelRegion)
{
Rectangle bounds = pixelRegion.Rectangle;
Buffer2D<TPixel> source = pixelRegion.Buffer;
using (IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(bounds.Width))
using (IMemoryOwner<Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate<Rgba32>(pixelRegion.Width))
{
Span<Rgba32> bufferSpan = buffer.GetSpan();

// Loop through each row
for (int y = bounds.Top; y < bounds.Bottom; y++)
for (int y = 0; y < pixelRegion.Height; y++)
{
Span<TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
Span<TPixel> row = pixelRegion.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgba32(this.Configuration, row, bufferSpan);

for (int x = 0; x < bufferSpan.Length; x++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public QuantizeProcessor(Configuration configuration, IQuantizer quantizer, Imag
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);
Rectangle interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);

Configuration configuration = this.Configuration;
using IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(configuration);
Expand All @@ -43,14 +43,14 @@ protected override void OnFrameApply(ImageFrame<TPixel> source)
int offsetX = interest.Left;
Buffer2D<TPixel> sourceBuffer = source.PixelBuffer;

for (int y = interest.Y; y < interest.Height; y++)
for (int y = 0; y < quantized.Height; y++)
{
Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y);
ReadOnlySpan<byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);
ReadOnlySpan<byte> quantizedRow = quantized.DangerousGetRowSpan(y);
Span<TPixel> row = sourceBuffer.DangerousGetRowSpan(y + offsetY);

for (int x = interest.Left; x < interest.Right; x++)
for (int x = 0; x < quantized.Width; x++)
{
row[x] = paletteSpan[quantizedRow[x - offsetX]];
row[x + offsetX] = paletteSpan[quantizedRow[x]];
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,14 @@ private static void SecondPass<TFrameQuantizer, TPixel>(
int offsetY = bounds.Top;
int offsetX = bounds.Left;

for (int y = bounds.Y; y < bounds.Height; y++)
for (int y = 0; y < destination.Height; y++)
{
Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y);
Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y - offsetY);
Span<TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y + offsetY);
Span<byte> destinationRow = destination.GetWritablePixelRowSpanUnsafe(y);

for (int x = bounds.Left; x < bounds.Right; x++)
for (int x = 0; x < destination.Width; x++)
{
destinationRow[x - offsetX] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x], out TPixel _);
destinationRow[x] = Unsafe.AsRef(quantizer).GetQuantizedColor(sourceRow[x + offsetX], out TPixel _);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ public readonly ReadOnlyMemory<TPixel> Palette
/// <inheritdoc/>
public void AddPaletteColors(Buffer2DRegion<TPixel> pixelRegion)
{
Rectangle bounds = pixelRegion.Rectangle;
Buffer2D<TPixel> source = pixelRegion.Buffer;

this.Build3DHistogram(source, bounds);
// TODO: Something is destroying the existing palette when adding new colors.
// When the QuantizingImageEncoder.PixelSamplingStrategy is DefaultPixelSamplingStrategy
// this leads to performance issues + the palette is not preserved.
// https://github.com/SixLabors/ImageSharp/issues/2498
this.Build3DHistogram(pixelRegion);
this.Get3DMoments(this.memoryAllocator);
this.BuildCube();

Expand Down Expand Up @@ -360,19 +361,18 @@ private static Moment Top(ref Box cube, int direction, int position, ReadOnlySpa
/// <summary>
/// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>.
/// </summary>
/// <param name="source">The source data.</param>
/// <param name="bounds">The bounds within the source image to quantize.</param>
private readonly void Build3DHistogram(Buffer2D<TPixel> source, Rectangle bounds)
/// <param name="source">The source pixel data.</param>
private readonly void Build3DHistogram(Buffer2DRegion<TPixel> source)
{
Span<Moment> momentSpan = this.momentsOwner.GetSpan();

// Build up the 3-D color histogram
using IMemoryOwner<Rgba32> buffer = this.memoryAllocator.Allocate<Rgba32>(bounds.Width);
using IMemoryOwner<Rgba32> buffer = this.memoryAllocator.Allocate<Rgba32>(source.Width);
Span<Rgba32> bufferSpan = buffer.GetSpan();

for (int y = bounds.Top; y < bounds.Bottom; y++)
for (int y = 0; y < source.Height; y++)
{
Span<TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
Span<TPixel> row = source.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgba32(this.Configuration, row, bufferSpan);

for (int x = 0; x < bufferSpan.Length; x++)
Expand Down
6 changes: 6 additions & 0 deletions tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,12 @@ public void TiffDecoder_CanDecode_Fax4CompressedWithStrips<TPixel>(TestImageProv
public void TiffDecoder_CanDecode_TiledWithNonEqualWidthAndHeight<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);

// https://github.com/SixLabors/ImageSharp/issues/2587
[Theory]
[WithFile(Issues2587, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_BiColorWithMissingBitsPerSample<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);

[Theory]
[WithFile(JpegCompressedGray0000539558, PixelTypes.Rgba32)]
public void TiffDecoder_ThrowsException_WithCircular_IFD_Offsets<TPixel>(TestImageProvider<TPixel> provider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,74 +12,66 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization;
[Trait("Category", "Processors")]
public class QuantizerTests
{
/// <summary>
/// Something is causing tests to fail on NETFX in CI.
/// Could be a JIT error as everything runs well and is identical to .NET Core output.
/// Not worth investigating for now.
/// <see href="https://github.com/SixLabors/ImageSharp/pull/1114/checks?check_run_id=448891164#step:11:631"/>
/// </summary>
private static readonly bool SkipAllQuantizerTests = TestEnvironment.IsFramework;

public static readonly string[] CommonTestImages =
{
TestImages.Png.CalliphoraPartial,
TestImages.Png.Bike
};

private static readonly QuantizerOptions NoDitherOptions = new QuantizerOptions { Dither = null };
private static readonly QuantizerOptions DiffuserDitherOptions = new QuantizerOptions { Dither = KnownDitherings.FloydSteinberg };
private static readonly QuantizerOptions OrderedDitherOptions = new QuantizerOptions { Dither = KnownDitherings.Bayer8x8 };
private static readonly QuantizerOptions NoDitherOptions = new() { Dither = null };
private static readonly QuantizerOptions DiffuserDitherOptions = new() { Dither = KnownDitherings.FloydSteinberg };
private static readonly QuantizerOptions OrderedDitherOptions = new() { Dither = KnownDitherings.Bayer8x8 };

private static readonly QuantizerOptions Diffuser0_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Diffuser0_ScaleDitherOptions = new()
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = 0F
};

private static readonly QuantizerOptions Diffuser0_25_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Diffuser0_25_ScaleDitherOptions = new()
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .25F
};

private static readonly QuantizerOptions Diffuser0_5_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Diffuser0_5_ScaleDitherOptions = new()
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .5F
};

private static readonly QuantizerOptions Diffuser0_75_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Diffuser0_75_ScaleDitherOptions = new()
{
Dither = KnownDitherings.FloydSteinberg,
DitherScale = .75F
};

private static readonly QuantizerOptions Ordered0_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Ordered0_ScaleDitherOptions = new()
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = 0F
};

private static readonly QuantizerOptions Ordered0_25_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Ordered0_25_ScaleDitherOptions = new()
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .25F
};

private static readonly QuantizerOptions Ordered0_5_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Ordered0_5_ScaleDitherOptions = new()
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .5F
};

private static readonly QuantizerOptions Ordered0_75_ScaleDitherOptions = new QuantizerOptions
private static readonly QuantizerOptions Ordered0_75_ScaleDitherOptions = new()
{
Dither = KnownDitherings.Bayer8x8,
DitherScale = .75F
};

public static readonly TheoryData<IQuantizer> Quantizers
= new TheoryData<IQuantizer>
= new()
{
// Known uses error diffusion by default.
KnownQuantizers.Octree,
Expand All @@ -97,7 +89,7 @@ public static readonly TheoryData<IQuantizer> Quantizers
};

public static readonly TheoryData<IQuantizer> DitherScaleQuantizers
= new TheoryData<IQuantizer>
= new()
{
new OctreeQuantizer(Diffuser0_ScaleDitherOptions),
new WebSafePaletteQuantizer(Diffuser0_ScaleDitherOptions),
Expand Down Expand Up @@ -151,7 +143,7 @@ public static readonly TheoryData<IQuantizer> DitherScaleQuantizers
};

public static readonly TheoryData<IDither> DefaultInstanceDitherers
= new TheoryData<IDither>
= new()
{
default(ErrorDither),
default(OrderedDither)
Expand All @@ -164,11 +156,6 @@ public static readonly TheoryData<IDither> DefaultInstanceDitherers
public void ApplyQuantizationInBox<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel<TPixel>
{
if (SkipAllQuantizerTests)
{
return;
}

string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
string testOutputDetails = $"{quantizerName}_{ditherName}";
Expand All @@ -185,11 +172,6 @@ public void ApplyQuantizationInBox<TPixel>(TestImageProvider<TPixel> provider, I
public void ApplyQuantization<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel<TPixel>
{
if (SkipAllQuantizerTests)
{
return;
}

string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither?.GetType()?.Name ?? "NoDither";
string testOutputDetails = $"{quantizerName}_{ditherName}";
Expand All @@ -206,11 +188,6 @@ public void ApplyQuantization<TPixel>(TestImageProvider<TPixel> provider, IQuant
public void ApplyQuantizationWithDitheringScale<TPixel>(TestImageProvider<TPixel> provider, IQuantizer quantizer)
where TPixel : unmanaged, IPixel<TPixel>
{
if (SkipAllQuantizerTests)
{
return;
}

string quantizerName = quantizer.GetType().Name;
string ditherName = quantizer.Options.Dither.GetType().Name;
float ditherScale = quantizer.Options.DitherScale;
Expand All @@ -229,8 +206,8 @@ public void ShouldThrowForDefaultDitherInstance(IDither dither)
{
void Command()
{
using var image = new Image<Rgba32>(10, 10);
var quantizer = new WebSafePaletteQuantizer();
using Image<Rgba32> image = new(10, 10);
WebSafePaletteQuantizer quantizer = new();
quantizer.Options.Dither = dither;
image.Mutate(x => x.Quantize(quantizer));
}
Expand Down
1 change: 1 addition & 0 deletions tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,7 @@ public static class Tiff
public const string Issues2149 = "Tiff/Issues/Group4CompressionWithStrips.tiff";
public const string Issues2255 = "Tiff/Issues/Issue2255.png";
public const string Issues2435 = "Tiff/Issues/Issue2435.tiff";
public const string Issues2587 = "Tiff/Issues/Issue2587.tiff";
public const string JpegCompressedGray0000539558 = "Tiff/Issues/JpegCompressedGray-0000539558.tiff";
public const string Tiled0000023664 = "Tiff/Issues/tiled-0000023664.tiff";

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ca1fd46

Please sign in to comment.