diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 8a13ad82dd..49b7aa79b0 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
+using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Processing;
@@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp
///
/// Initializes a new instance of the class.
///
- /// A collection of configuration modules to register
+ /// A collection of configuration modules to register.
public Configuration(params IConfigurationModule[] configurationModules)
{
if (configurationModules != null)
@@ -77,7 +78,7 @@ namespace SixLabors.ImageSharp
///
/// Gets or sets the size of the buffer to use when working with streams.
- /// Intitialized with by default.
+ /// Initialized with by default.
///
public int StreamProcessingBufferSize
{
@@ -180,6 +181,7 @@ namespace SixLabors.ImageSharp
///
/// .
/// .
+ /// .
///
/// The default configuration of .
internal static Configuration CreateDefaultInstance()
@@ -189,7 +191,8 @@ namespace SixLabors.ImageSharp
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule(),
- new TgaConfigurationModule());
+ new TgaConfigurationModule(),
+ new TiffConfigurationModule());
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs b/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs
deleted file mode 100644
index 49f87e0905..0000000000
--- a/src/ImageSharp/Formats/Tiff/ConfigurationExtensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-namespace SixLabors.ImageSharp.Formats.Tiff
-{
- ///
- /// Helper methods for the Configuration.
- ///
- public static class ConfigurationExtensions
- {
- ///
- /// Registers the tiff format detector, encoder and decoder.
- ///
- /// The configuration.
- public static void AddTiff(this Configuration configuration)
- {
- configuration.ImageFormatsManager.AddImageFormat(TiffFormat.Instance);
- configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector());
- configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder());
- configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder());
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
index a30890a69e..6fe412b925 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffConstants.cs
@@ -96,10 +96,30 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
public static readonly ushort[] BitsPerSample8Bit = { 8 };
///
- /// The bits per sample for images with 8 bits for each color channel.
+ /// The bits per sample for color images with 2 bits for each color channel.
+ ///
+ public static readonly ushort[] BitsPerSampleRgb2Bit = { 2, 2, 2 };
+
+ ///
+ /// The bits per sample for color images with 4 bits for each color channel.
+ ///
+ public static readonly ushort[] BitsPerSampleRgb4Bit = { 4, 4, 4 };
+
+ ///
+ /// The bits per sample for color images with 8 bits for each color channel.
///
public static readonly ushort[] BitsPerSampleRgb8Bit = { 8, 8, 8 };
+ ///
+ /// The bits per sample for color images with 10 bits for each color channel.
+ ///
+ public static readonly ushort[] BitsPerSampleRgb10Bit = { 10, 10, 10 };
+
+ ///
+ /// The bits per sample for color images with 14 bits for each color channel.
+ ///
+ public static readonly ushort[] BitsPerSampleRgb14Bit = { 14, 14, 14 };
+
///
/// The list of mimetypes that equate to a tiff.
///
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..d8c48942f8
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb444TiffColor{TPixel}.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation for 4 bits per color channel images.
+ ///
+ internal class Rgb444TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ var color = default(TPixel);
+
+ int offset = 0;
+
+ var bgra = default(Bgra4444);
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.GetRowSpan(y);
+
+ for (int x = left; x < left + width; x += 2)
+ {
+ byte r = (byte)((data[offset] & 0xF0) >> 4);
+ byte g = (byte)(data[offset] & 0xF);
+ offset++;
+ byte b = (byte)((data[offset] & 0xF0) >> 4);
+
+ bgra.PackedValue = ToBgraPackedValue(b, g, r);
+ color.FromScaledVector4(bgra.ToScaledVector4());
+ pixelRow[x] = color;
+ if (x + 1 >= pixelRow.Length)
+ {
+ offset++;
+ break;
+ }
+
+ r = (byte)(data[offset] & 0xF);
+ offset++;
+ g = (byte)((data[offset] & 0xF0) >> 4);
+ b = (byte)(data[offset] & 0xF);
+ offset++;
+
+ bgra.PackedValue = ToBgraPackedValue(b, g, r);
+ color.FromScaledVector4(bgra.ToScaledVector4());
+ pixelRow[x + 1] = color;
+ }
+ }
+ }
+
+ private static ushort ToBgraPackedValue(byte b, byte g, byte r) => (ushort)(b | (g << 4) | (r << 8) | (0xF << 12));
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
index e45dd44bdc..b40158fcee 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
@@ -27,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
private readonly ushort bitsPerSampleB;
public RgbPlanarTiffColor(ushort[] bitsPerSample)
- /* : base(bitsPerSample, null) */
{
this.bitsPerSampleR = bitsPerSample[0];
this.bitsPerSampleG = bitsPerSample[1];
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index 0a7941dfbc..5555eb537c 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -57,16 +57,56 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
+ case TiffColorType.Rgb222:
+ DebugGuard.IsTrue(
+ bitsPerSample.Length == 3
+ && bitsPerSample[2] == 2
+ && bitsPerSample[1] == 2
+ && bitsPerSample[0] == 2,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgb444:
+ DebugGuard.IsTrue(
+ bitsPerSample.Length == 3
+ && bitsPerSample[2] == 4
+ && bitsPerSample[1] == 4
+ && bitsPerSample[0] == 4,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgb444TiffColor();
+
case TiffColorType.Rgb888:
DebugGuard.IsTrue(
bitsPerSample.Length == 3
- && bitsPerSample[0] == 8
+ && bitsPerSample[2] == 8
&& bitsPerSample[1] == 8
- && bitsPerSample[2] == 8,
+ && bitsPerSample[0] == 8,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb888TiffColor();
+ case TiffColorType.Rgb101010:
+ DebugGuard.IsTrue(
+ bitsPerSample.Length == 3
+ && bitsPerSample[2] == 10
+ && bitsPerSample[1] == 10
+ && bitsPerSample[0] == 10,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgb141414:
+ DebugGuard.IsTrue(
+ bitsPerSample.Length == 3
+ && bitsPerSample[2] == 14
+ && bitsPerSample[1] == 14
+ && bitsPerSample[0] == 14,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
case TiffColorType.PaletteColor:
DebugGuard.NotNull(bitsPerSample, "bitsPerSample");
DebugGuard.NotNull(colorMap, "colorMap");
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
index 484d231633..22d8199533 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -58,11 +58,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
Rgb,
+ ///
+ /// RGB color image with 2 bits for each channel.
+ ///
+ Rgb222,
+
+ ///
+ /// RGB color image with 4 bits for each channel.
+ ///
+ Rgb444,
+
///
/// RGB Full Color. Optimized implementation for 8-bit images.
///
Rgb888,
+ ///
+ /// RGB color image with 10 bits for each channel.
+ ///
+ Rgb101010,
+
+ ///
+ /// RGB color image with 14 bits for each channel.
+ ///
+ Rgb141414,
+
///
/// RGB Full Color. Planar configuration of data.
///
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
index 35a9a590bb..ab9f3cbec0 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerPixel.cs
@@ -18,14 +18,42 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
Bit4 = 4,
+ ///
+ /// 6 bits per pixel. 2 bit for each color channel.
+ ///
+ /// Note: The TiffEncoder does not yet support 2 bits per color channel and will default to 24 bits per pixel instead.
+ ///
+ Bit6 = 6,
+
///
/// 8 bits per pixel, grayscale or color palette images.
///
Bit8 = 8,
+ ///
+ /// 12 bits per pixel. 4 bit for each color channel.
+ ///
+ /// Note: The TiffEncoder does not yet support 4 bits per color channel and will default to 24 bits per pixel instead.
+ ///
+ Bit12 = 12,
+
///
/// 24 bits per pixel. One byte for each color channel.
///
Bit24 = 24,
+
+ ///
+ /// 30 bits per pixel. 10 bit for each color channel.
+ ///
+ /// Note: The TiffEncoder does not yet support 10 bits per color channel and will default to 24 bits per pixel instead.
+ ///
+ Bit30 = 30,
+
+ ///
+ /// 42 bits per pixel. 14 bit for each color channel.
+ ///
+ /// Note: The TiffEncoder does not yet support 14 bits per color channel and will default to 24 bits per pixel instead.
+ ///
+ Bit42 = 42,
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
index bc74cbc5fb..088ef5d6f8 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
@@ -28,9 +28,29 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
Bit8 = 8,
+ ///
+ /// Six bits per sample, each channel has 2 bits.
+ ///
+ Bit6 = 6,
+
+ ///
+ /// Twelve bits per sample, each channel has 4 bits.
+ ///
+ Bit12 = 12,
+
///
/// 24 bits per sample, each color channel has 8 Bits.
///
Bit24 = 24,
+
+ ///
+ /// Thirty bits per sample, each channel has 10 bits.
+ ///
+ Bit30 = 30,
+
+ ///
+ /// Forty two bits per sample, each channel has 14 bits.
+ ///
+ Bit42 = 42,
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
index 5c4c374bef..ca0f0befcc 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSampleExtensions.cs
@@ -21,10 +21,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return TiffConstants.BitsPerSample1Bit;
case TiffBitsPerSample.Bit4:
return TiffConstants.BitsPerSample4Bit;
+ case TiffBitsPerSample.Bit6:
+ return TiffConstants.BitsPerSampleRgb2Bit;
case TiffBitsPerSample.Bit8:
return TiffConstants.BitsPerSample8Bit;
+ case TiffBitsPerSample.Bit12:
+ return TiffConstants.BitsPerSampleRgb4Bit;
case TiffBitsPerSample.Bit24:
return TiffConstants.BitsPerSampleRgb8Bit;
+ case TiffBitsPerSample.Bit30:
+ return TiffConstants.BitsPerSampleRgb10Bit;
+ case TiffBitsPerSample.Bit42:
+ return TiffConstants.BitsPerSampleRgb14Bit;
default:
return Array.Empty();
@@ -41,13 +49,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (bitsPerSample.Length)
{
case 3:
- if (bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0] &&
+ if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb14Bit[2] &&
+ bitsPerSample[1] == TiffConstants.BitsPerSampleRgb14Bit[1] &&
+ bitsPerSample[0] == TiffConstants.BitsPerSampleRgb14Bit[0])
+ {
+ return TiffBitsPerSample.Bit42;
+ }
+
+ if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb10Bit[2] &&
+ bitsPerSample[1] == TiffConstants.BitsPerSampleRgb10Bit[1] &&
+ bitsPerSample[0] == TiffConstants.BitsPerSampleRgb10Bit[0])
+ {
+ return TiffBitsPerSample.Bit30;
+ }
+
+ if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2] &&
bitsPerSample[1] == TiffConstants.BitsPerSampleRgb8Bit[1] &&
- bitsPerSample[2] == TiffConstants.BitsPerSampleRgb8Bit[2])
+ bitsPerSample[0] == TiffConstants.BitsPerSampleRgb8Bit[0])
{
return TiffBitsPerSample.Bit24;
}
+ if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb4Bit[2] &&
+ bitsPerSample[1] == TiffConstants.BitsPerSampleRgb4Bit[1] &&
+ bitsPerSample[0] == TiffConstants.BitsPerSampleRgb4Bit[0])
+ {
+ return TiffBitsPerSample.Bit12;
+ }
+
+ if (bitsPerSample[2] == TiffConstants.BitsPerSampleRgb2Bit[2] &&
+ bitsPerSample[1] == TiffConstants.BitsPerSampleRgb2Bit[1] &&
+ bitsPerSample[0] == TiffConstants.BitsPerSampleRgb2Bit[0])
+ {
+ return TiffBitsPerSample.Bit6;
+ }
+
break;
case 1:
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index b7b7640072..fadb4f7c2e 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
if (this.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
- DebugGuard.IsTrue(plane == -1, "Excepted Chunky planar.");
+ DebugGuard.IsTrue(plane == -1, "Expected Chunky planar.");
bitsPerPixel = this.BitsPerPixel;
}
else
@@ -294,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
int top = rowsPerStrip * stripIndex;
if (top + stripHeight > frame.Height)
{
- // Make sure we ignore any strips that are not needed for the image (if too many are present)
+ // Make sure we ignore any strips that are not needed for the image (if too many are present).
break;
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index b5f3e7cf1e..eeac6a33c2 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Linq;
using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
@@ -179,7 +180,29 @@ namespace SixLabors.ImageSharp.Formats.Tiff
if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky)
{
- options.ColorType = options.BitsPerSample == TiffBitsPerSample.Bit24 ? TiffColorType.Rgb888 : TiffColorType.Rgb;
+ switch (options.BitsPerSample)
+ {
+ case TiffBitsPerSample.Bit42:
+ options.ColorType = TiffColorType.Rgb141414;
+ break;
+
+ case TiffBitsPerSample.Bit30:
+ options.ColorType = TiffColorType.Rgb101010;
+ break;
+
+ case TiffBitsPerSample.Bit24:
+ options.ColorType = TiffColorType.Rgb888;
+ break;
+ case TiffBitsPerSample.Bit12:
+ options.ColorType = TiffColorType.Rgb444;
+ break;
+ case TiffBitsPerSample.Bit6:
+ options.ColorType = TiffColorType.Rgb222;
+ break;
+ default:
+ TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported.");
+ break;
+ }
}
else
{
@@ -273,9 +296,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
{
TiffBitsPerPixel.Bit1 => TiffBitsPerSample.Bit1,
TiffBitsPerPixel.Bit4 => TiffBitsPerSample.Bit4,
+ TiffBitsPerPixel.Bit6 => TiffBitsPerSample.Bit6,
TiffBitsPerPixel.Bit8 => TiffBitsPerSample.Bit8,
+ TiffBitsPerPixel.Bit12 => TiffBitsPerSample.Bit12,
TiffBitsPerPixel.Bit24 => TiffBitsPerSample.Bit24,
- _ => TiffBitsPerSample.Bit24,
+ TiffBitsPerPixel.Bit30 => TiffBitsPerSample.Bit30,
+ TiffBitsPerPixel.Bit42 => TiffBitsPerSample.Bit42,
+ _ => throw new NotSupportedException("The bits per pixel are not supported"),
};
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 74c516f63b..d5137c4357 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -306,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffBitsPerPixel.Bit1:
if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.CcittGroup4Fax)
{
- // The normal PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero.
+ // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero.
this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None);
break;
}
@@ -319,6 +319,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffBitsPerPixel.Bit8:
this.SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor);
break;
+ case TiffBitsPerPixel.Bit6:
+ case TiffBitsPerPixel.Bit12:
+ case TiffBitsPerPixel.Bit30:
+ case TiffBitsPerPixel.Bit42:
+ // Encoding 42, 30, 12 and 6 bits per pixel is not yet supported. Default to 24 bits.
+ this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None);
+ break;
default:
this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor);
break;
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
index 09605bc690..9bc0792c40 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
@@ -66,8 +66,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Value = SoftwareValue
};
- this.collector.Add(width);
- this.collector.Add(height);
+ this.collector.AddOrReplace(width);
+ this.collector.AddOrReplace(height);
this.ProcessResolution(image.Metadata, rootFrameExifProfile);
this.ProcessProfiles(image.Metadata, rootFrameExifProfile, rootFrameXmpBytes);
@@ -227,7 +227,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
exifProfile.RemoveValue(ExifTag.IccProfile);
}
- TiffMetadata tiffMetadata = imageMetadata.GetTiffMetadata();
if (xmpProfile != null)
{
var xmp = new ExifByteArray(ExifTagValue.XMP, ExifDataType.Byte)
@@ -252,6 +251,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public void Process(TiffEncoderCore encoder)
{
+ var planarConfig = new ExifShort(ExifTagValue.PlanarConfiguration)
+ {
+ Value = (ushort)TiffPlanarConfiguration.Chunky
+ };
+
var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel)
{
Value = GetSamplesPerPixel(encoder)
@@ -274,6 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Value = (ushort)encoder.PhotometricInterpretation
};
+ this.collector.AddOrReplace(planarConfig);
this.collector.AddOrReplace(samplesPerPixel);
this.collector.AddOrReplace(bitPerSample);
this.collector.AddOrReplace(compression);
diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
index 712578f81a..d1a3dd1ea3 100644
--- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
@@ -55,23 +55,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
///
protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor)
{
- Span pixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height);
+ Span indexedPixels = GetStripPixels(((IPixelSource)this.quantizedImage).PixelBuffer, y, height);
if (this.BitsPerPixel == 4)
{
- using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(pixels.Length / 2);
+ int width = this.Image.Width;
+ int halfWidth = width >> 1;
+ int excess = (width & 1) * height; // (width % 2) * height
+ int rows4BitBufferLength = (halfWidth * height) + excess;
+ using IMemoryOwner rows4bitBuffer = this.MemoryAllocator.Allocate(rows4BitBufferLength);
Span rows4bit = rows4bitBuffer.GetSpan();
- int idx = 0;
- for (int i = 0; i < rows4bit.Length; i++)
+ int idxPixels = 0;
+ int idx4bitRows = 0;
+ for (int row = 0; row < height; row++)
{
- rows4bit[i] = (byte)((pixels[idx] << 4) | (pixels[idx + 1] & 0xF));
- idx += 2;
+ for (int x = 0; x < halfWidth; x++)
+ {
+ rows4bit[idx4bitRows] = (byte)((indexedPixels[idxPixels] << 4) | (indexedPixels[idxPixels + 1] & 0xF));
+ idxPixels += 2;
+ idx4bitRows++;
+ }
+
+ // Make sure rows are byte-aligned.
+ if (width % 2 != 0)
+ {
+ rows4bit[idx4bitRows++] = (byte)(indexedPixels[idxPixels++] << 4);
+ }
}
- compressor.CompressStrip(rows4bit, height);
+ compressor.CompressStrip(rows4bit.Slice(0, idx4bitRows), height);
}
else
{
- compressor.CompressStrip(pixels, height);
+ compressor.CompressStrip(indexedPixels, height);
}
}
@@ -91,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
PixelOperations.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48);
// It can happen that the quantized colors are less than the expected maximum per channel.
- var diffToMaxColors = this.maxColors - quantizedColors.Length;
+ int diffToMaxColors = this.maxColors - quantizedColors.Length;
// In a TIFF ColorMap, all the Red values come first, followed by the Green values,
// then the Blue values. Convert the quantized palette to this format.
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index fbb3ec2069..ce6aa69b58 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
@@ -19,6 +20,8 @@ namespace SixLabors.ImageSharp
///
public abstract partial class Image : IImage, IConfigurationProvider
{
+ private bool isDisposed;
+
private Size size;
private readonly Configuration configuration;
@@ -80,8 +83,15 @@ namespace SixLabors.ImageSharp
///
public void Dispose()
{
+ if (this.isDisposed)
+ {
+ return;
+ }
+
this.Dispose(true);
GC.SuppressFinalize(this);
+
+ this.isDisposed = true;
}
///
@@ -89,7 +99,7 @@ namespace SixLabors.ImageSharp
///
/// The stream to save the image to.
/// The encoder to save the image with.
- /// Thrown if the stream or encoder is null.
+ /// Thrown if the stream or encoder is null.
public void Save(Stream stream, IImageEncoder encoder)
{
Guard.NotNull(stream, nameof(stream));
@@ -148,7 +158,13 @@ namespace SixLabors.ImageSharp
///
/// Throws if the image is disposed.
///
- internal abstract void EnsureNotDisposed();
+ internal void EnsureNotDisposed()
+ {
+ if (this.isDisposed)
+ {
+ ThrowObjectDisposedException(this.GetType());
+ }
+ }
///
/// Accepts a .
@@ -167,6 +183,9 @@ namespace SixLabors.ImageSharp
/// The token to monitor for cancellation requests.
internal abstract Task AcceptAsync(IImageVisitorAsync visitor, CancellationToken cancellationToken);
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void ThrowObjectDisposedException(Type type) => throw new ObjectDisposedException(type.Name);
+
private class EncodeVisitor : IImageVisitor, IImageVisitorAsync
{
private readonly IImageEncoder encoder;
diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs
index 62ecc71f55..07ba8c87f3 100644
--- a/src/ImageSharp/ImageFrameCollection.cs
+++ b/src/ImageSharp/ImageFrameCollection.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp
{
@@ -11,8 +12,10 @@ namespace SixLabors.ImageSharp
/// Encapsulates a pixel-agnostic collection of instances
/// that make up an .
///
- public abstract class ImageFrameCollection : IEnumerable
+ public abstract class ImageFrameCollection : IDisposable, IEnumerable
{
+ private bool isDisposed;
+
///
/// Gets the number of frames.
///
@@ -21,7 +24,15 @@ namespace SixLabors.ImageSharp
///
/// Gets the root frame.
///
- public ImageFrame RootFrame => this.NonGenericRootFrame;
+ public ImageFrame RootFrame
+ {
+ get
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericRootFrame;
+ }
+ }
///
/// Gets the root frame. (Implements .)
@@ -36,7 +47,15 @@ namespace SixLabors.ImageSharp
///
/// The index.
/// The at the specified index.
- public ImageFrame this[int index] => this.NonGenericGetFrame(index);
+ public ImageFrame this[int index]
+ {
+ get
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericGetFrame(index);
+ }
+ }
///
/// Determines the index of a specific in the .
@@ -52,14 +71,24 @@ namespace SixLabors.ImageSharp
/// The to clone and insert into the .
/// Frame must have the same dimensions as the image.
/// The cloned .
- public ImageFrame InsertFrame(int index, ImageFrame source) => this.NonGenericInsertFrame(index, source);
+ public ImageFrame InsertFrame(int index, ImageFrame source)
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericInsertFrame(index, source);
+ }
///
/// Clones the frame and appends the clone to the end of the collection.
///
/// The raw pixel data to generate the from.
/// The cloned .
- public ImageFrame AddFrame(ImageFrame source) => this.NonGenericAddFrame(source);
+ public ImageFrame AddFrame(ImageFrame source)
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericAddFrame(source);
+ }
///
/// Removes the frame at the specified index and frees all freeable resources associated with it.
@@ -91,7 +120,12 @@ namespace SixLabors.ImageSharp
/// The zero-based index of the frame to export.
/// Cannot remove last frame.
/// The new with the specified frame.
- public Image ExportFrame(int index) => this.NonGenericExportFrame(index);
+ public Image ExportFrame(int index)
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericExportFrame(index);
+ }
///
/// Creates an with only the frame at the specified index
@@ -99,7 +133,12 @@ namespace SixLabors.ImageSharp
///
/// The zero-based index of the frame to clone.
/// The new with the specified frame.
- public Image CloneFrame(int index) => this.NonGenericCloneFrame(index);
+ public Image CloneFrame(int index)
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericCloneFrame(index);
+ }
///
/// Creates a new and appends it to the end of the collection.
@@ -107,7 +146,12 @@ namespace SixLabors.ImageSharp
///
/// The new .
///
- public ImageFrame CreateFrame() => this.NonGenericCreateFrame();
+ public ImageFrame CreateFrame()
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericCreateFrame();
+ }
///
/// Creates a new and appends it to the end of the collection.
@@ -116,14 +160,55 @@ namespace SixLabors.ImageSharp
///
/// The new .
///
- public ImageFrame CreateFrame(Color backgroundColor) => this.NonGenericCreateFrame(backgroundColor);
+ public ImageFrame CreateFrame(Color backgroundColor)
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericCreateFrame(backgroundColor);
+ }
///
- public IEnumerator GetEnumerator() => this.NonGenericGetEnumerator();
+ public void Dispose()
+ {
+ if (this.isDisposed)
+ {
+ return;
+ }
+
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+
+ this.isDisposed = true;
+ }
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ this.EnsureNotDisposed();
+
+ return this.NonGenericGetEnumerator();
+ }
///
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+ ///
+ /// Throws if the image frame is disposed.
+ ///
+ protected void EnsureNotDisposed()
+ {
+ if (this.isDisposed)
+ {
+ ThrowObjectDisposedException(this.GetType());
+ }
+ }
+
+ ///
+ /// Disposes the object and frees resources for the Garbage Collector.
+ ///
+ /// Whether to dispose of managed and unmanaged objects.
+ protected abstract void Dispose(bool disposing);
+
///
/// Implements .
///
@@ -178,5 +263,8 @@ namespace SixLabors.ImageSharp
/// The background color.
/// The new frame.
protected abstract ImageFrame NonGenericCreateFrame(Color backgroundColor);
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void ThrowObjectDisposedException(Type type) => throw new ObjectDisposedException(type.Name);
}
}
diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs
index 36c3ee481f..da024c9176 100644
--- a/src/ImageSharp/ImageFrameCollection{TPixel}.cs
+++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs
@@ -67,7 +67,26 @@ namespace SixLabors.ImageSharp
///
/// Gets the root frame.
///
- public new ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
+ public new ImageFrame RootFrame
+ {
+ get
+ {
+ this.EnsureNotDisposed();
+
+ // frame collection would always contain at least 1 frame
+ // the only exception is when collection is disposed what is checked via EnsureNotDisposed() call
+ return this.frames[0];
+ }
+ }
+
+ ///
+ /// Gets root frame accessor in unsafe manner without any checks.
+ ///
+ ///
+ /// This property is most likely to be called from for indexing pixels.
+ /// already checks if it was disposed before querying for root frame.
+ ///
+ internal ImageFrame RootFrameUnsafe => this.frames[0];
///
protected override ImageFrame NonGenericRootFrame => this.RootFrame;
@@ -80,12 +99,22 @@ namespace SixLabors.ImageSharp
///
/// The index.
/// The at the specified index.
- public new ImageFrame this[int index] => this.frames[index];
+ public new ImageFrame this[int index]
+ {
+ get
+ {
+ this.EnsureNotDisposed();
+
+ return this.frames[index];
+ }
+ }
///
public override int IndexOf(ImageFrame frame)
{
- return frame is ImageFrame specific ? this.IndexOf(specific) : -1;
+ this.EnsureNotDisposed();
+
+ return frame is ImageFrame specific ? this.frames.IndexOf(specific) : -1;
}
///
@@ -93,7 +122,12 @@ namespace SixLabors.ImageSharp
///
/// The to locate in the .
/// The index of item if found in the list; otherwise, -1.
- public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame);
+ public int IndexOf(ImageFrame frame)
+ {
+ this.EnsureNotDisposed();
+
+ return this.frames.IndexOf(frame);
+ }
///
/// Clones and inserts the into the at the specified .
@@ -104,6 +138,8 @@ namespace SixLabors.ImageSharp
/// The cloned .
public ImageFrame InsertFrame(int index, ImageFrame source)
{
+ this.EnsureNotDisposed();
+
this.ValidateFrame(source);
ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
this.frames.Insert(index, clonedFrame);
@@ -117,6 +153,8 @@ namespace SixLabors.ImageSharp
/// The cloned .
public ImageFrame AddFrame(ImageFrame source)
{
+ this.EnsureNotDisposed();
+
this.ValidateFrame(source);
ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
this.frames.Add(clonedFrame);
@@ -131,6 +169,8 @@ namespace SixLabors.ImageSharp
/// The new .
public ImageFrame AddFrame(ReadOnlySpan source)
{
+ this.EnsureNotDisposed();
+
var frame = ImageFrame.LoadPixelData(
this.parent.GetConfiguration(),
source,
@@ -149,6 +189,7 @@ namespace SixLabors.ImageSharp
public ImageFrame AddFrame(TPixel[] source)
{
Guard.NotNull(source, nameof(source));
+
return this.AddFrame(source.AsSpan());
}
@@ -159,6 +200,8 @@ namespace SixLabors.ImageSharp
/// Cannot remove last frame.
public override void RemoveFrame(int index)
{
+ this.EnsureNotDisposed();
+
if (index == 0 && this.Count == 1)
{
throw new InvalidOperationException("Cannot remove last frame.");
@@ -170,8 +213,12 @@ namespace SixLabors.ImageSharp
}
///
- public override bool Contains(ImageFrame frame) =>
- frame is ImageFrame specific && this.Contains(specific);
+ public override bool Contains(ImageFrame frame)
+ {
+ this.EnsureNotDisposed();
+
+ return frame is ImageFrame specific && this.frames.Contains(specific);
+ }
///
/// Determines whether the contains the .
@@ -180,7 +227,12 @@ namespace SixLabors.ImageSharp
///
/// true if the contains the specified frame; otherwise, false.
///
- public bool Contains(ImageFrame frame) => this.frames.Contains(frame);
+ public bool Contains(ImageFrame frame)
+ {
+ this.EnsureNotDisposed();
+
+ return this.frames.Contains(frame);
+ }
///
/// Moves an from to .
@@ -189,6 +241,8 @@ namespace SixLabors.ImageSharp
/// The index to move the frame to.
public override void MoveFrame(int sourceIndex, int destinationIndex)
{
+ this.EnsureNotDisposed();
+
if (sourceIndex == destinationIndex)
{
return;
@@ -208,6 +262,8 @@ namespace SixLabors.ImageSharp
/// The new with the specified frame.
public new Image ExportFrame(int index)
{
+ this.EnsureNotDisposed();
+
ImageFrame frame = this[index];
if (this.Count == 1 && this.frames.Contains(frame))
@@ -228,6 +284,8 @@ namespace SixLabors.ImageSharp
/// The new with the specified frame.
public new Image CloneFrame(int index)
{
+ this.EnsureNotDisposed();
+
ImageFrame frame = this[index];
ImageFrame clonedFrame = frame.Clone();
return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame });
@@ -241,6 +299,8 @@ namespace SixLabors.ImageSharp
///
public new ImageFrame CreateFrame()
{
+ this.EnsureNotDisposed();
+
var frame = new ImageFrame(
this.parent.GetConfiguration(),
this.RootFrame.Width,
@@ -335,14 +395,18 @@ namespace SixLabors.ImageSharp
}
}
- internal void Dispose()
+ ///
+ protected override void Dispose(bool disposing)
{
- foreach (ImageFrame f in this.frames)
+ if (disposing)
{
- f.Dispose();
- }
+ foreach (ImageFrame f in this.frames)
+ {
+ f.Dispose();
+ }
- this.frames.Clear();
+ this.frames.Clear();
+ }
}
private ImageFrame CopyNonCompatibleFrame(ImageFrame source)
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 83ecc37530..b43ff0422b 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp
public sealed class Image : Image
where TPixel : unmanaged, IPixel
{
- private bool isDisposed;
+ private readonly ImageFrameCollection frames;
///
/// Initializes a new instance of the class
@@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, int width, int height, ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create(), metadata, width, height)
{
- this.Frames = new ImageFrameCollection(this, width, height, default(TPixel));
+ this.frames = new ImageFrameCollection(this, width, height, default(TPixel));
}
///
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp
ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create(), metadata, width, height)
{
- this.Frames = new ImageFrameCollection(this, width, height, memoryGroup);
+ this.frames = new ImageFrameCollection(this, width, height, memoryGroup);
}
///
@@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp
ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create(), metadata, width, height)
{
- this.Frames = new ImageFrameCollection(this, width, height, backgroundColor);
+ this.frames = new ImageFrameCollection(this, width, height, backgroundColor);
}
///
@@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp
internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames)
: base(configuration, PixelTypeInfo.Create(), metadata, ValidateFramesAndGetSize(frames))
{
- this.Frames = new ImageFrameCollection(this, frames);
+ this.frames = new ImageFrameCollection(this, frames);
}
///
@@ -146,12 +146,19 @@ namespace SixLabors.ImageSharp
///
/// Gets the collection of image frames.
///
- public new ImageFrameCollection Frames { get; }
+ public new ImageFrameCollection Frames
+ {
+ get
+ {
+ this.EnsureNotDisposed();
+ return this.frames;
+ }
+ }
///
/// Gets the root frame.
///
- private IPixelSource PixelSource => this.Frames?.RootFrame ?? throw new ObjectDisposedException(nameof(Image));
+ private IPixelSource PixelSourceUnsafe => this.frames.RootFrameUnsafe;
///
/// Gets or sets the pixel at the specified position.
@@ -165,15 +172,19 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
get
{
+ this.EnsureNotDisposed();
+
this.VerifyCoords(x, y);
- return this.PixelSource.PixelBuffer.GetElementUnsafe(x, y);
+ return this.PixelSourceUnsafe.PixelBuffer.GetElementUnsafe(x, y);
}
[MethodImpl(InliningOptions.ShortMethod)]
set
{
+ this.EnsureNotDisposed();
+
this.VerifyCoords(x, y);
- this.PixelSource.PixelBuffer.GetElementUnsafe(x, y) = value;
+ this.PixelSourceUnsafe.PixelBuffer.GetElementUnsafe(x, y) = value;
}
}
@@ -189,7 +200,9 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));
- return this.PixelSource.PixelBuffer.GetRowSpan(rowIndex);
+ this.EnsureNotDisposed();
+
+ return this.PixelSourceUnsafe.PixelBuffer.GetRowSpan(rowIndex);
}
///
@@ -226,10 +239,10 @@ namespace SixLabors.ImageSharp
{
this.EnsureNotDisposed();
- var clonedFrames = new ImageFrame[this.Frames.Count];
+ var clonedFrames = new ImageFrame[this.frames.Count];
for (int i = 0; i < clonedFrames.Length; i++)
{
- clonedFrames[i] = this.Frames[i].Clone(configuration);
+ clonedFrames[i] = this.frames[i].Clone(configuration);
}
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
@@ -245,10 +258,10 @@ namespace SixLabors.ImageSharp
{
this.EnsureNotDisposed();
- var clonedFrames = new ImageFrame[this.Frames.Count];
+ var clonedFrames = new ImageFrame[this.frames.Count];
for (int i = 0; i < clonedFrames.Length; i++)
{
- clonedFrames[i] = this.Frames[i].CloneAs(configuration);
+ clonedFrames[i] = this.frames[i].CloneAs(configuration);
}
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
@@ -257,25 +270,9 @@ namespace SixLabors.ImageSharp
///
protected override void Dispose(bool disposing)
{
- if (this.isDisposed)
- {
- return;
- }
-
if (disposing)
{
- this.Frames.Dispose();
- }
-
- this.isDisposed = true;
- }
-
- ///
- internal override void EnsureNotDisposed()
- {
- if (this.isDisposed)
- {
- throw new ObjectDisposedException("Trying to execute an operation on a disposed image.");
+ this.frames.Dispose();
}
}
@@ -306,9 +303,12 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(pixelSource, nameof(pixelSource));
- for (int i = 0; i < this.Frames.Count; i++)
+ this.EnsureNotDisposed();
+
+ ImageFrameCollection sourceFrames = pixelSource.Frames;
+ for (int i = 0; i < this.frames.Count; i++)
{
- this.Frames[i].SwapOrCopyPixelsBufferFrom(pixelSource.Frames[i]);
+ this.frames[i].SwapOrCopyPixelsBufferFrom(sourceFrames[i]);
}
this.UpdateSize(pixelSource.Size());
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
index 56593acb84..9b28a8fdd8 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
@@ -22,10 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
bool clipHistogram,
int clipLimit,
int numberOfTiles)
- : base(luminanceLevels, clipHistogram, clipLimit)
- {
- this.NumberOfTiles = numberOfTiles;
- }
+ : base(luminanceLevels, clipHistogram, clipLimit) => this.NumberOfTiles = numberOfTiles;
///
/// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization.
@@ -34,8 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
- {
- return new AdaptiveHistogramEqualizationProcessor(
+ => new AdaptiveHistogramEqualizationProcessor(
configuration,
this.LuminanceLevels,
this.ClipHistogram,
@@ -43,6 +39,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
this.NumberOfTiles,
source,
sourceRectangle);
- }
}
}
diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
index 14687426d0..91ed9f5de4 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
@@ -459,10 +459,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly Configuration configuration;
private readonly MemoryAllocator memoryAllocator;
- // Used for storing the minimum value for each CDF entry.
+ ///
+ /// Used for storing the minimum value for each CDF entry.
+ ///
private readonly Buffer2D cdfMinBuffer2D;
- // Used for storing the LUT for each CDF entry.
+ ///
+ /// Used for storing the LUT for each CDF entry.
+ ///
private readonly Buffer2D cdfLutBuffer2D;
private readonly int pixelsInTile;
private readonly int sourceWidth;
@@ -596,6 +600,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int y = this.tileYStartPositions[index].y;
int endY = Math.Min(y + this.tileHeight, this.sourceHeight);
Span cdfMinSpan = this.cdfMinBuffer2D.GetRowSpan(cdfY);
+ cdfMinSpan.Clear();
using IMemoryOwner histogramBuffer = this.allocator.Allocate(this.luminanceLevels);
Span histogram = histogramBuffer.GetSpan();
diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
index 60686f4014..f93334beb0 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
@@ -49,44 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
///
/// The .
/// The .
- public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options)
+ public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) => options.Method switch
{
- HistogramEqualizationProcessor processor;
+ HistogramEqualizationMethod.Global
+ => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit),
- switch (options.Method)
- {
- case HistogramEqualizationMethod.Global:
- processor = new GlobalHistogramEqualizationProcessor(
- options.LuminanceLevels,
- options.ClipHistogram,
- options.ClipLimit);
- break;
+ HistogramEqualizationMethod.AdaptiveTileInterpolation
+ => new AdaptiveHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles),
- case HistogramEqualizationMethod.AdaptiveTileInterpolation:
- processor = new AdaptiveHistogramEqualizationProcessor(
- options.LuminanceLevels,
- options.ClipHistogram,
- options.ClipLimit,
- options.NumberOfTiles);
- break;
+ HistogramEqualizationMethod.AdaptiveSlidingWindow
+ => new AdaptiveHistogramEqualizationSlidingWindowProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles),
- case HistogramEqualizationMethod.AdaptiveSlidingWindow:
- processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor(
- options.LuminanceLevels,
- options.ClipHistogram,
- options.ClipLimit,
- options.NumberOfTiles);
- break;
-
- default:
- processor = new GlobalHistogramEqualizationProcessor(
- options.LuminanceLevels,
- options.ClipHistogram,
- options.ClipLimit);
- break;
- }
-
- return processor;
- }
+ _ => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit),
+ };
}
}
diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
index 59df3058d9..9227cb0c01 100644
--- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
@@ -142,6 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetLuminance(TPixel sourcePixel, int luminanceLevels)
{
+ // TODO: We need a bulk per span equivalent.
var vector = sourcePixel.ToVector4();
return ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels);
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs
index b388b5d70a..e77bb8b3e4 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs
@@ -9,7 +9,6 @@ using System.IO;
using BenchmarkDotNet.Attributes;
-using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests;
@@ -23,7 +22,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
[Config(typeof(Config.ShortMultiFramework))]
public class DecodeTiff
{
- private string prevImage = null;
+ private string prevImage;
private byte[] data;
@@ -68,7 +67,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
if (this.configuration == null)
{
this.configuration = new Configuration();
- this.configuration.AddTiff();
this.configuration.StreamProcessingBufferSize = BufferSize;
}
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs
index 7154b2310b..39055faf50 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs
@@ -43,8 +43,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
if (this.core == null)
{
this.configuration = new Configuration();
- this.configuration.AddTiff();
-
this.core = Image.Load(this.configuration, this.TestImageFullPath);
this.drawing = System.Drawing.Image.FromFile(this.TestImageFullPath);
}
diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
index 9159475326..fe3b16450c 100644
--- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
+++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
@@ -13,6 +13,7 @@
false
false
Debug;Release;Release-InnerLoop;Debug-InnerLoop
+ false
diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs
index 655e98c7f6..3ad8ef2f8a 100644
--- a/tests/ImageSharp.Tests/ConfigurationTests.cs
+++ b/tests/ImageSharp.Tests/ConfigurationTests.cs
@@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.Tests
public Configuration DefaultConfiguration { get; }
- private readonly int expectedDefaultConfigurationCount = 5;
+ private readonly int expectedDefaultConfigurationCount = 6;
public ConfigurationTests()
{
- // the shallow copy of configuration should behave exactly like the default configuration,
+ // The shallow copy of configuration should behave exactly like the default configuration,
// so by using the copy, we test both the default and the copy.
this.DefaultConfiguration = Configuration.CreateDefaultInstance().Clone();
this.ConfigurationEmpty = new Configuration();
diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
index a171d6d523..c0843a51bb 100644
--- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
+++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats
public class GeneralFormatTests
{
///
- /// A collection made up of one file for each image format
+ /// A collection made up of one file for each image format.
///
public static readonly IEnumerable DefaultFiles =
new[]
@@ -149,6 +149,11 @@ namespace SixLabors.ImageSharp.Tests.Formats
{
image.SaveAsTga(output);
}
+
+ using (FileStream output = File.OpenWrite(Path.Combine(path, $"{file.FileNameWithoutExtension}.tiff")))
+ {
+ image.SaveAsTga(output);
+ }
}
}
}
@@ -171,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.Formats
using (var image2 = Image.Load(serialized))
{
- image2.Save($"{path}/{file.FileName}");
+ image2.Save($"{path}{Path.DirectorySeparatorChar}{file.FileName}");
}
}
}
@@ -196,6 +201,10 @@ namespace SixLabors.ImageSharp.Tests.Formats
[InlineData(100, 100, "tga")]
[InlineData(100, 10, "tga")]
[InlineData(10, 100, "tga")]
+ [InlineData(100, 100, "tiff")]
+ [InlineData(100, 10, "tiff")]
+ [InlineData(10, 100, "tiff")]
+
public void CanIdentifyImageLoadedFromBytes(int width, int height, string extension)
{
using (var image = Image.LoadPixelData(new Rgba32[width * height], width, height))
diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs
index 6ec1162c46..c4be71d2ab 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifGraphicControlExtensionTests.cs
@@ -1,11 +1,11 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Gif;
using Xunit;
-namespace SixLabors.ImageSharp.Tests.Formats.Gif
+namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections
{
public class GifGraphicControlExtensionTests
{
@@ -18,4 +18,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(GifDisposalMethod.RestoreToPrevious, true, false));
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs
index db88cf5b3f..41ec1c7e8d 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifImageDescriptorTests.cs
@@ -1,11 +1,11 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Gif;
using Xunit;
-namespace SixLabors.ImageSharp.Tests.Formats.Gif
+namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections
{
public class GifImageDescriptorTests
{
@@ -21,4 +21,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
Assert.Equal(232, GifImageDescriptor.GetPackedValue(true, true, true, 8));
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs
index 9773bcd612..6efa680c8c 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/Sections/GifLogicalScreenDescriptorTests.cs
@@ -1,11 +1,11 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Gif;
using Xunit;
-namespace SixLabors.ImageSharp.Tests.Formats.Gif
+namespace SixLabors.ImageSharp.Tests.Formats.Gif.Sections
{
public class GifLogicalScreenDescriptorTests
{
@@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
Assert.Equal(55, GifLogicalScreenDescriptor.GetPackedValue(false, 3, false, 7));
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
index dea8c62e19..1e00bfff8c 100644
--- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
+++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
@@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
+using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -36,12 +37,14 @@ namespace SixLabors.ImageSharp.Tests.Formats
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count());
+ Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
+ Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count());
}
[Fact]
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
index 3910b2c498..67df6a8814 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
@@ -12,6 +12,7 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
+using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
@@ -117,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Floorplan, PixelTypes.Rgba32)]
[WithFile(TestImages.Jpeg.Progressive.Festzug, PixelTypes.Rgba32)]
- public async Task DecodeAsnc_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider)
+ public async Task DecodeAsync_DegenerateMemoryRequest_ShouldTranslateTo_ImageFormatException(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
provider.LimitAllocatorBufferCapacity().InBytesSqrt(10);
@@ -127,60 +128,53 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Theory]
- [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, 0)]
- [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 1)]
- [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 15)]
- [InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 30)]
- [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 1)]
- [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 15)]
- [InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 30)]
- public async Task Decode_IsCancellable(string fileName, int cancellationDelayMs)
+ [InlineData(0)]
+ [InlineData(0.5)]
+ [InlineData(0.9)]
+ public async Task Decode_IsCancellable(int percentageOfStreamReadToCancel)
{
- // Decoding these huge files took 300ms on i7-8650U in 2020. 30ms should be safe for cancellation delay.
- string hugeFile = Path.Combine(
- TestEnvironment.InputImagesDirectoryFullPath,
- fileName);
-
- const int NumberOfRuns = 5;
-
- for (int i = 0; i < NumberOfRuns; i++)
+ var cts = new CancellationTokenSource();
+ var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
+ using var pausedStream = new PausedStream(file);
+ pausedStream.OnWaiting(s =>
{
- var cts = new CancellationTokenSource();
- if (cancellationDelayMs == 0)
+ if (s.Position >= s.Length * percentageOfStreamReadToCancel)
{
cts.Cancel();
+ pausedStream.Release();
}
else
{
- cts.CancelAfter(cancellationDelayMs);
- }
-
- try
- {
- using var image = await Image.LoadAsync(hugeFile, cts.Token);
- }
- catch (TaskCanceledException)
- {
- // Succesfully observed a cancellation
- return;
+ // allows this/next wait to unblock
+ pausedStream.Next();
}
- }
+ });
- throw new Exception($"No cancellation happened out of {NumberOfRuns} runs!");
+ var config = Configuration.CreateDefaultInstance();
+ config.FileSystem = new SingleStreamFileSystem(pausedStream);
+ await Assert.ThrowsAsync(async () =>
+ {
+ using Image image = await Image.LoadAsync(config, "someFakeFile", cts.Token);
+ });
}
- [Theory(Skip = "Identify is too fast, doesn't work reliably.")]
- [InlineData(TestImages.Jpeg.Baseline.Exif)]
- [InlineData(TestImages.Jpeg.Progressive.Bad.ExifUndefType)]
- public async Task Identify_IsCancellable(string fileName)
+ [Fact]
+ public async Task Identify_IsCancellable()
{
- string file = Path.Combine(
- TestEnvironment.InputImagesDirectoryFullPath,
- fileName);
-
var cts = new CancellationTokenSource();
- cts.CancelAfter(TimeSpan.FromTicks(1));
- await Assert.ThrowsAsync(() => Image.IdentifyAsync(file, cts.Token));
+
+ var file = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImages.Jpeg.Baseline.Jpeg420Small);
+ using var pausedStream = new PausedStream(file);
+ pausedStream.OnWaiting(s =>
+ {
+ cts.Cancel();
+ pausedStream.Release();
+ });
+
+ var config = Configuration.CreateDefaultInstance();
+ config.FileSystem = new SingleStreamFileSystem(pausedStream);
+
+ await Assert.ThrowsAsync(async () => await Image.IdentifyAsync(config, "someFakeFile", cts.Token));
}
// DEBUG ONLY!
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index 9a1d423a6d..3c48865c71 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -13,6 +13,7 @@ using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
@@ -310,28 +311,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Theory]
- [InlineData(JpegSubsample.Ratio420, 0)]
- [InlineData(JpegSubsample.Ratio420, 3)]
- [InlineData(JpegSubsample.Ratio420, 10)]
- [InlineData(JpegSubsample.Ratio444, 0)]
- [InlineData(JpegSubsample.Ratio444, 3)]
- [InlineData(JpegSubsample.Ratio444, 10)]
- public async Task Encode_IsCancellable(JpegSubsample subsample, int cancellationDelayMs)
+ [InlineData(JpegSubsample.Ratio420)]
+ [InlineData(JpegSubsample.Ratio444)]
+ public async Task Encode_IsCancellable(JpegSubsample subsample)
{
- using var image = new Image(5000, 5000);
- using var stream = new MemoryStream();
var cts = new CancellationTokenSource();
- if (cancellationDelayMs == 0)
- {
- cts.Cancel();
- }
- else
+ using var pausedStream = new PausedStream(new MemoryStream());
+ pausedStream.OnWaiting(s =>
{
- cts.CancelAfter(cancellationDelayMs);
- }
+ // after some writing
+ if (s.Position >= 500)
+ {
+ cts.Cancel();
+ pausedStream.Release();
+ }
+ else
+ {
+ // allows this/next wait to unblock
+ pausedStream.Next();
+ }
+ });
- var encoder = new JpegEncoder() { Subsample = subsample };
- await Assert.ThrowsAsync(() => image.SaveAsync(stream, encoder, cts.Token));
+ using var image = new Image(5000, 5000);
+ await Assert.ThrowsAsync(async () =>
+ {
+ var encoder = new JpegEncoder() { Subsample = subsample };
+ await image.SaveAsync(pausedStream, encoder, cts.Token);
+ });
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs
index 5f7b4f8327..80bfd34975 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngFilterTests.cs
@@ -7,7 +7,6 @@ using System;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Png.Filters;
-using SixLabors.ImageSharp.Tests.Formats.Png.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -142,7 +141,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
HwIntrinsics.DisableSIMD);
}
-
[Fact]
public void UpAvx2()
{
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
index f9ff41df1e..b4307af5d1 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
@@ -90,12 +90,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
using (Image image = provider.GetImage(new PngDecoder()))
{
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("leading space"));
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("trailing space"));
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("space"));
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("empty"));
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("invalid characters"));
- Assert.DoesNotContain(meta.TextData, m => m.Value.Equals("too large"));
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "leading space");
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "trailing space");
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "space");
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "empty");
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "invalid characters");
+ Assert.DoesNotContain(meta.TextData, m => m.Value is "too large");
}
}
@@ -277,20 +277,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
private static void VerifyTextDataIsPresent(PngMetadata meta)
{
Assert.NotNull(meta);
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("International") && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'") &&
- m.LanguageTag.Equals("x-klingon") && m.TranslatedKeyword.Equals("warning"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational") && m.Value.Equals("la plume de la mante") &&
- m.LanguageTag.Equals("fra") && m.TranslatedKeyword.Equals("foobar"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") &&
- m.LanguageTag.Equals("chinese"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
- Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
+ Assert.Contains(meta.TextData, m => m.Keyword is "Comment" && m.Value is "comment");
+ Assert.Contains(meta.TextData, m => m.Keyword is "Author" && m.Value is "ImageSharp");
+ Assert.Contains(meta.TextData, m => m.Keyword is "Copyright" && m.Value is "ImageSharp");
+ Assert.Contains(meta.TextData, m => m.Keyword is "Title" && m.Value is "unittest");
+ Assert.Contains(meta.TextData, m => m.Keyword is "Description" && m.Value is "compressed-text");
+ Assert.Contains(meta.TextData, m => m.Keyword is "International" && m.Value is "'e', mu'tlheghvam, ghaH yu'" && m.LanguageTag is "x-klingon" && m.TranslatedKeyword is "warning");
+ Assert.Contains(meta.TextData, m => m.Keyword is "International2" && m.Value is "ИМАГЕШАРП" && m.LanguageTag is "rus");
+ Assert.Contains(meta.TextData, m => m.Keyword is "CompressedInternational" && m.Value is "la plume de la mante" && m.LanguageTag is "fra" && m.TranslatedKeyword is "foobar");
+ Assert.Contains(meta.TextData, m => m.Keyword is "CompressedInternational2" && m.Value is "這是一個考驗" && m.LanguageTag is "chinese");
+ Assert.Contains(meta.TextData, m => m.Keyword is "NoLang" && m.Value is "this text chunk is missing a language tag");
+ Assert.Contains(meta.TextData, m => m.Keyword is "NoTranslatedKeyword" && m.Value is "dieser chunk hat kein übersetztes Schlüßelwort");
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
index dd8ecc096d..a9b53e16e8 100644
--- a/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/ReferenceImplementations.cs
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
-namespace SixLabors.ImageSharp.Tests.Formats.Png.Utils
+namespace SixLabors.ImageSharp.Tests.Formats.Png
{
///
/// This class contains reference implementations to produce verification data for unit tests
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
index e2aeeb9e84..3365a1eb39 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/ImageExtensionsTest.cs
@@ -13,26 +13,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Trait("Format", "Tiff")]
public class ImageExtensionsTest
{
- private readonly Configuration configuration;
-
- public ImageExtensionsTest()
- {
- this.configuration = new Configuration();
- this.configuration.AddTiff();
- }
-
[Fact]
public void SaveAsTiff_Path()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTiff_Path.tiff");
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
image.SaveAsTiff(file);
}
- using (Image.Load(this.configuration, file, out IImageFormat mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -44,12 +36,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsTiffAsync_Path.tiff");
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
await image.SaveAsTiffAsync(file);
}
- using (Image.Load(this.configuration, file, out IImageFormat mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -61,12 +53,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTiff_Path_Encoder.tiff");
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
image.SaveAsTiff(file, new TiffEncoder());
}
- using (Image.Load(this.configuration, file, out IImageFormat mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -78,12 +70,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsTiffAsync_Path_Encoder.tiff");
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
await image.SaveAsTiffAsync(file, new TiffEncoder());
}
- using (Image.Load(this.configuration, file, out IImageFormat mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -94,14 +86,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using var memoryStream = new MemoryStream();
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
image.SaveAsTiff(memoryStream);
}
memoryStream.Position = 0;
- using (Image.Load(this.configuration, memoryStream, out IImageFormat mime))
+ using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -112,14 +104,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using var memoryStream = new MemoryStream();
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
await image.SaveAsTiffAsync(memoryStream);
}
memoryStream.Position = 0;
- using (Image.Load(this.configuration, memoryStream, out IImageFormat mime))
+ using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -130,14 +122,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using var memoryStream = new MemoryStream();
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
image.SaveAsTiff(memoryStream, new TiffEncoder());
}
memoryStream.Position = 0;
- using (Image.Load(this.configuration, memoryStream, out IImageFormat mime))
+ using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
@@ -148,14 +140,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
using var memoryStream = new MemoryStream();
- using (var image = new Image(this.configuration, 10, 10))
+ using (var image = new Image(10, 10))
{
await image.SaveAsTiffAsync(memoryStream, new TiffEncoder());
}
memoryStream.Position = 0;
- using (Image.Load(this.configuration, memoryStream, out IImageFormat mime))
+ using (Image.Load(memoryStream, out IImageFormat mime))
{
Assert.Equal("image/tiff", mime.DefaultMimeType);
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index bffb603028..02b7f97d94 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -4,7 +4,7 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
-
+using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -28,14 +28,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder();
- private readonly Configuration configuration;
-
- public TiffDecoderTests()
- {
- this.configuration = new Configuration();
- this.configuration.AddTiff();
- }
-
[Theory]
[WithFileCollection(nameof(NotSupportedImages), PixelTypes.Rgba32)]
public void ThrowsNotSupported(TestImageProvider provider)
@@ -45,12 +37,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[InlineData(RgbUncompressed, 24, 256, 256, 300, 300, PixelResolutionUnit.PixelsPerInch)]
[InlineData(SmallRgbDeflate, 24, 32, 32, 96, 96, PixelResolutionUnit.PixelsPerInch)]
[InlineData(Calliphora_GrayscaleUncompressed, 8, 804, 1198, 96, 96, PixelResolutionUnit.PixelsPerInch)]
+ [InlineData(Flower4BitPalette, 4, 73, 43, 72, 72, PixelResolutionUnit.PixelsPerInch)]
public void Identify(string imagePath, int expectedPixelSize, int expectedWidth, int expectedHeight, double expectedHResolution, double expectedVResolution, PixelResolutionUnit expectedResolutionUnit)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
- IImageInfo info = Image.Identify(this.configuration, stream);
+ IImageInfo info = Image.Identify(stream);
Assert.Equal(expectedPixelSize, info.PixelType?.BitsPerPixel);
Assert.Equal(expectedWidth, info.Width);
@@ -70,14 +63,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
- IImageInfo info = Image.Identify(this.configuration, stream);
+ IImageInfo info = Image.Identify(stream);
Assert.NotNull(info.Metadata);
Assert.Equal(expectedByteOrder, info.Metadata.GetTiffMetadata().ByteOrder);
stream.Seek(0, SeekOrigin.Begin);
- using var img = Image.Load(this.configuration, stream);
+ using var img = Image.Load(stream);
Assert.Equal(expectedByteOrder, img.Metadata.GetTiffMetadata().ByteOrder);
}
}
@@ -99,6 +92,37 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_WithPalette(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgb4BitPalette, PixelTypes.Rgba32)]
+ [WithFile(Flower4BitPalette, PixelTypes.Rgba32)]
+ [WithFile(Flower4BitPaletteGray, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_4Bit_WithPalette(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, ReferenceDecoder, useExactComparer: false, 0.01f);
+
+ [Theory]
+ [WithFile(FlowerRgb222Contiguous, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb222Planar, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_6Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(FlowerRgb444Contiguous, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_12Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(FlowerRgb101010Contiguous, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb101010Planar, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_30Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(FlowerRgb141414Contiguous, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb141414Planar, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_42Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
[Theory]
[WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)]
[WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)]
@@ -163,12 +187,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder);
}
- private static void TestTiffDecoder(TestImageProvider provider)
+ private static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
where TPixel : unmanaged, IPixel
{
using Image image = provider.GetImage(TiffDecoder);
image.DebugSave(provider);
- image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
+ image.CompareToOriginal(
+ provider,
+ useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance),
+ referenceDecoder ?? ReferenceDecoder);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index a40ca04bda..7c386a6a9a 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -22,14 +22,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
- private static readonly Configuration Configuration;
-
- static TiffEncoderTests()
- {
- Configuration = new Configuration();
- Configuration.AddTiff();
- }
-
[Theory]
[InlineData(null, TiffBitsPerPixel.Bit24)]
[InlineData(TiffPhotometricInterpretation.Rgb, TiffBitsPerPixel.Bit24)]
@@ -55,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
Assert.Equal(TiffCompression.None, frameMetaData.Compression);
@@ -78,13 +70,36 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel);
Assert.Equal(TiffCompression.None, frameMetaData.Compression);
}
+ [Theory]
+ [InlineData(TiffBitsPerPixel.Bit42)]
+ [InlineData(TiffBitsPerPixel.Bit30)]
+ [InlineData(TiffBitsPerPixel.Bit12)]
+ [InlineData(TiffBitsPerPixel.Bit6)]
+ public void EncoderOptions_UnsupportedBitPerPixel_DefaultTo24Bits(TiffBitsPerPixel bitsPerPixel)
+ {
+ // arrange
+ var tiffEncoder = new TiffEncoder { BitsPerPixel = bitsPerPixel };
+ using Image input = new Image(10, 10);
+ using var memStream = new MemoryStream();
+
+ // act
+ input.Save(memStream, tiffEncoder);
+
+ // assert
+ memStream.Position = 0;
+ using var output = Image.Load(memStream);
+
+ TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
+ Assert.Equal(TiffBitsPerPixel.Bit24, frameMetaData.BitsPerPixel);
+ }
+
[Theory]
[InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)]
[InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)]
@@ -117,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel);
Assert.Equal(expectedCompression, rootFrameMetaData.Compression);
@@ -143,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
}
@@ -162,7 +177,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
}
@@ -185,7 +200,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
Assert.Equal(expectedCompression, output.Frames.RootFrame.Metadata.GetTiffMetadata().Compression);
}
@@ -207,7 +222,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetadata = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(expectedPredictor, frameMetadata.Predictor);
}
@@ -230,12 +245,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel);
Assert.Equal(expectedCompression, frameMetaData.Compression);
}
+ // This makes sure, that when decoding a planar tiff, the planar configuration is not carried over to the encoded image.
+ [Theory]
+ [WithFile(FlowerRgb444Planar, PixelTypes.Rgba32)]
+ public void TiffEncoder_EncodePlanar_AndReload_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, imageDecoder: new TiffDecoder());
+
[Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_Works(TestImageProvider provider)
@@ -304,10 +325,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(Rgb4BitPalette, PixelTypes.Rgba32)]
+ [WithFile(Flower4BitPalette, PixelTypes.Rgba32)]
+ [WithFile(Flower4BitPaletteGray, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_With4Bit_Works(TestImageProvider provider)
where TPixel : unmanaged, IPixel =>
- //// Note: The magick reference decoder does not support 4 bit tiff's, so we use our TIFF decoder instead.
- TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.001f, imageDecoder: new TiffDecoder());
+ TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit4, TiffPhotometricInterpretation.PaletteColor, useExactComparer: false, compareTolerance: 0.003f);
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
@@ -422,7 +444,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// assert
memStream.Position = 0;
- using var output = Image.Load(Configuration, memStream);
+ using var output = Image.Load(memStream);
ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile;
TiffFrameMetadata outputMeta = output.Frames.RootFrame.Metadata.GetTiffMetadata();
ImageFrame rootFrame = output.Frames.RootFrame;
@@ -468,7 +490,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
TiffCompression compression = TiffCompression.None,
TiffPredictor predictor = TiffPredictor.None,
bool useExactComparer = true,
- float compareTolerance = 0.01f,
+ float compareTolerance = 0.001f,
IImageDecoder imageDecoder = null)
where TPixel : unmanaged, IPixel
{
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
index f52b74e833..ab350f720e 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
@@ -20,16 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Trait("Format", "Tiff")]
public class TiffMetadataTests
{
- private readonly Configuration configuration;
-
private static TiffDecoder TiffDecoder => new TiffDecoder();
- public TiffMetadataTests()
- {
- this.configuration = new Configuration();
- this.configuration.AddTiff();
- }
-
[Fact]
public void TiffMetadata_CloneIsDeep()
{
@@ -89,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
- IImageInfo imageInfo = Image.Identify(this.configuration, stream);
+ IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata();
@@ -105,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var testFile = TestFile.Create(imagePath);
using var stream = new MemoryStream(testFile.Bytes, false);
- IImageInfo imageInfo = Image.Identify(this.configuration, stream);
+ IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
TiffMetadata tiffMetadata = imageInfo.Metadata.GetTiffMetadata();
@@ -265,7 +257,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
// Assert
ms.Position = 0;
- using var encodedImage = Image.Load(this.configuration, ms);
+ using var encodedImage = Image.Load(ms);
ImageMetadata encodedImageMetaData = encodedImage.Metadata;
ImageFrame rootFrameEncodedImage = encodedImage.Frames.RootFrame;
@@ -296,10 +288,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal("This is Изготовитель камеры", exifProfileInput.GetValue(ExifTag.Make).Value);
Assert.Equal("This is Авторские права", exifProfileInput.GetValue(ExifTag.Copyright).Value);
- Assert.Equal(exifProfileInput.Values.Count, encodedImageExifProfile.Values.Count);
Assert.Equal(exifProfileInput.GetValue(ExifTag.ImageDescription).Value, encodedImageExifProfile.GetValue(ExifTag.ImageDescription).Value);
Assert.Equal(exifProfileInput.GetValue(ExifTag.Make).Value, encodedImageExifProfile.GetValue(ExifTag.Make).Value);
Assert.Equal(exifProfileInput.GetValue(ExifTag.Copyright).Value, encodedImageExifProfile.GetValue(ExifTag.Copyright).Value);
+
+ // Note that the encoded profile has PlanarConfiguration explicitly set, which is missing in the original image profile.
+ Assert.Equal((ushort)TiffPlanarConfiguration.Chunky, encodedImageExifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value);
+ Assert.Equal(exifProfileInput.Values.Count + 1, encodedImageExifProfile.Values.Count);
}
}
}
diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs
index ecbc331b28..dbc5af536d 100644
--- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs
+++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs
@@ -28,7 +28,8 @@ namespace SixLabors.ImageSharp.Tests
ArgumentException ex = Assert.Throws(
() =>
{
- this.Collection.AddFrame(new ImageFrame(Configuration.Default, 1, 1));
+ using var frame = new ImageFrame(Configuration.Default, 1, 1);
+ using ImageFrame addedFrame = this.Collection.AddFrame(frame);
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
@@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentNullException ex = Assert.Throws(
() =>
{
- this.Collection.AddFrame((ImageFrame)null);
+ using ImageFrame addedFrame = this.Collection.AddFrame((ImageFrame)null);
});
Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message);
@@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentNullException ex = Assert.Throws(
() =>
{
- this.Collection.AddFrame(data);
+ using ImageFrame addedFrame = this.Collection.AddFrame(data);
});
Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message);
@@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentOutOfRangeException ex = Assert.Throws(
() =>
{
- this.Collection.AddFrame(new Rgba32[0]);
+ using ImageFrame addedFrame = this.Collection.AddFrame(Array.Empty());
});
Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message);
@@ -78,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests
ArgumentException ex = Assert.Throws(
() =>
{
- this.Collection.InsertFrame(1, new ImageFrame(Configuration.Default, 1, 1));
+ using var frame = new ImageFrame(Configuration.Default, 1, 1);
+ using ImageFrame insertedFrame = this.Collection.InsertFrame(1, frame);
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
@@ -90,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentNullException ex = Assert.Throws(
() =>
{
- this.Collection.InsertFrame(1, null);
+ using ImageFrame insertedFrame = this.Collection.InsertFrame(1, null);
});
Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message);
@@ -102,9 +104,11 @@ namespace SixLabors.ImageSharp.Tests
ArgumentException ex = Assert.Throws(
() =>
{
+ using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10);
+ using var imageFrame2 = new ImageFrame(Configuration.Default, 1, 1);
new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 1, 1) });
+ new[] { imageFrame1, imageFrame2 });
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
@@ -113,24 +117,24 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void RemoveAtFrame_ThrowIfRemovingLastFrame()
{
+ using var imageFrame = new ImageFrame(Configuration.Default, 10, 10);
var collection = new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10) });
+ new[] { imageFrame });
InvalidOperationException ex = Assert.Throws(
- () =>
- {
- collection.RemoveFrame(0);
- });
+ () => collection.RemoveFrame(0));
Assert.Equal("Cannot remove last frame.", ex.Message);
}
[Fact]
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
+ using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10);
+ using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10);
var collection = new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) });
+ new[] { imageFrame1, imageFrame2 });
collection.RemoveFrame(0);
Assert.Equal(1, collection.Count);
@@ -139,9 +143,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void RootFrameIsFrameAtIndexZero()
{
+ using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10);
+ using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10);
var collection = new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) });
+ new[] { imageFrame1, imageFrame2 });
Assert.Equal(collection.RootFrame, collection[0]);
}
@@ -149,9 +155,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void ConstructorPopulatesFrames()
{
+ using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10);
+ using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10);
var collection = new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) });
+ new[] { imageFrame1, imageFrame2 });
Assert.Equal(2, collection.Count);
}
@@ -159,9 +167,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void DisposeClearsCollection()
{
+ using var imageFrame1 = new ImageFrame(Configuration.Default, 10, 10);
+ using var imageFrame2 = new ImageFrame(Configuration.Default, 10, 10);
var collection = new ImageFrameCollection(
this.Image,
- new[] { new ImageFrame(Configuration.Default, 10, 10), new ImageFrame(Configuration.Default, 10, 10) });
+ new[] { imageFrame1, imageFrame2 });
collection.Dispose();
@@ -171,9 +181,11 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Dispose_DisposesAllInnerFrames()
{
+ using var imageFrame1 = new ImageFrame