diff --git a/shared-infrastructure b/shared-infrastructure
index 48e73f455f..1f7ee70281 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit 48e73f455f15eafefbe3175efc7433e5f277e506
+Subproject commit 1f7ee702812f3a1713ab7f749c0faae0ef139ed7
diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs
index 37d2a943cc..6bf06150b9 100644
--- a/src/ImageSharp/Common/Helpers/Numerics.cs
+++ b/src/ImageSharp/Common/Helpers/Numerics.cs
@@ -24,22 +24,25 @@ namespace SixLabors.ImageSharp
#endif
#if !SUPPORTS_BITOPERATIONS
+ ///
+ /// Gets the counts the number of bits needed to hold an integer.
+ ///
private static ReadOnlySpan BitCountLut => new byte[]
- {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
- 8, 8, 8,
- };
+ {
+ 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8,
+ };
#endif
///
@@ -775,7 +778,7 @@ namespace SixLabors.ImageSharp
/// widening them to 32-bit integers and performing four additions.
///
///
- /// byte(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+ /// byte(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
/// is widened and added onto as such:
///
/// accumulator += i32(1, 2, 3, 4);
@@ -851,23 +854,18 @@ namespace SixLabors.ImageSharp
/// Unsigned integer to store
/// Minimum number of bits needed to store given value
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int MinimumBitsToStore16(uint number)
+ public static int MinimumBitsToStore(uint number)
{
-#if SUPPORTS_BITOPERATIONS
- const int bitInUnsignedInteger = sizeof(uint) * 8;
- return bitInUnsignedInteger - BitOperations.LeadingZeroCount(number);
-#else
- int bt;
+#if !SUPPORTS_BITOPERATIONS
if (number < 0x100)
{
- bt = BitCountLut[(int)number];
- }
- else
- {
- bt = 8 + BitCountLut[(int)(number >> 8)];
+ return BitCountLut[(int)number];
}
- return bt;
+ return 8 + BitCountLut[(int)number >> 8];
+#else
+ const int bitInUnsignedInteger = sizeof(uint) * 8;
+ return bitInUnsignedInteger - BitOperations.LeadingZeroCount(number);
#endif
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index b7b7640072..50882c0072 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
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.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/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
index f34fc9c787..c0843a51bb 100644
--- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
+++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
@@ -176,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}");
}
}
}
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/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/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index c35311a2a8..1a72046fb3 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;
@@ -37,6 +37,7 @@ 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);
@@ -91,6 +92,19 @@ 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
+ {
+ if (TestEnvironment.IsWindows)
+ {
+ TestTiffDecoder(provider, new SystemDrawingReferenceDecoder(), useExactComparer: false, 0.01f);
+ }
+ }
+
[Theory]
[WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)]
[WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)]
@@ -155,12 +169,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 546508ca54..105514c982 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -296,10 +296,12 @@ 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, imageDecoder: new TiffDecoder());
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
@@ -460,7 +462,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/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(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 });
IPixelSource[] framesSnapShot = collection.OfType>().ToArray();
collection.Dispose();
@@ -194,7 +206,8 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image img = provider.GetImage())
{
- img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10)); // add a frame anyway
+ using var imageFrame = new ImageFrame(Configuration.Default, 10, 10);
+ using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame); // add a frame anyway
using (Image cloned = img.Frames.CloneFrame(0))
{
Assert.Equal(2, img.Frames.Count);
@@ -215,7 +228,8 @@ namespace SixLabors.ImageSharp.Tests
Assert.True(img.TryGetSinglePixelSpan(out Span imgSpan));
TPixel[] sourcePixelData = imgSpan.ToArray();
- img.Frames.AddFrame(new ImageFrame(Configuration.Default, 10, 10));
+ using var imageFrame = new ImageFrame(Configuration.Default, 10, 10);
+ using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame);
using (Image cloned = img.Frames.ExportFrame(0))
{
Assert.Equal(1, img.Frames.Count);
@@ -227,35 +241,37 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void CreateFrame_Default()
{
- this.Image.Frames.CreateFrame();
-
- Assert.Equal(2, this.Image.Frames.Count);
- this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32));
+ using (this.Image.Frames.CreateFrame())
+ {
+ Assert.Equal(2, this.Image.Frames.Count);
+ this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32));
+ }
}
[Fact]
public void CreateFrame_CustomFillColor()
{
- this.Image.Frames.CreateFrame(Color.HotPink);
-
- Assert.Equal(2, this.Image.Frames.Count);
- this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink);
+ using (this.Image.Frames.CreateFrame(Color.HotPink))
+ {
+ Assert.Equal(2, this.Image.Frames.Count);
+ this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink);
+ }
}
[Fact]
public void AddFrameFromPixelData()
{
Assert.True(this.Image.Frames.RootFrame.TryGetSinglePixelSpan(out Span imgSpan));
- var pixelData = imgSpan.ToArray();
- this.Image.Frames.AddFrame(pixelData);
+ Rgba32[] pixelData = imgSpan.ToArray();
+ using ImageFrame addedFrame = this.Image.Frames.AddFrame(pixelData);
Assert.Equal(2, this.Image.Frames.Count);
}
[Fact]
public void AddFrame_clones_sourceFrame()
{
- var otherFrame = new ImageFrame(Configuration.Default, 10, 10);
- ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame);
+ using var otherFrame = new ImageFrame(Configuration.Default, 10, 10);
+ using ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame);
Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan));
addedFrame.ComparePixelBufferTo(otherFrameSpan);
@@ -265,8 +281,8 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void InsertFrame_clones_sourceFrame()
{
- var otherFrame = new ImageFrame(Configuration.Default, 10, 10);
- ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame);
+ using var otherFrame = new ImageFrame(Configuration.Default, 10, 10);
+ using ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame);
Assert.True(otherFrame.TryGetSinglePixelSpan(out Span otherFrameSpan));
addedFrame.ComparePixelBufferTo(otherFrameSpan);
@@ -276,53 +292,95 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void MoveFrame_LeavesFrameInCorrectLocation()
{
- for (var i = 0; i < 9; i++)
+ for (int i = 0; i < 9; i++)
{
this.Image.Frames.CreateFrame();
}
- var frame = this.Image.Frames[4];
+ ImageFrame frame = this.Image.Frames[4];
this.Image.Frames.MoveFrame(4, 7);
- var newIndex = this.Image.Frames.IndexOf(frame);
+ int newIndex = this.Image.Frames.IndexOf(frame);
Assert.Equal(7, newIndex);
}
[Fact]
public void IndexOf_ReturnsCorrectIndex()
{
- for (var i = 0; i < 9; i++)
+ for (int i = 0; i < 9; i++)
{
this.Image.Frames.CreateFrame();
}
- var frame = this.Image.Frames[4];
- var index = this.Image.Frames.IndexOf(frame);
+ ImageFrame frame = this.Image.Frames[4];
+ int index = this.Image.Frames.IndexOf(frame);
Assert.Equal(4, index);
}
[Fact]
public void Contains_TrueIfMember()
{
- for (var i = 0; i < 9; i++)
+ for (int i = 0; i < 9; i++)
{
this.Image.Frames.CreateFrame();
}
- var frame = this.Image.Frames[4];
+ ImageFrame frame = this.Image.Frames[4];
Assert.True(this.Image.Frames.Contains(frame));
}
[Fact]
public void Contains_FalseIfNonMember()
{
- for (var i = 0; i < 9; i++)
+ for (int i = 0; i < 9; i++)
{
this.Image.Frames.CreateFrame();
}
- var frame = new ImageFrame(Configuration.Default, 10, 10);
+ using var frame = new ImageFrame(Configuration.Default, 10, 10);
Assert.False(this.Image.Frames.Contains(frame));
}
+
+ [Fact]
+ public void DisposeCall_NoThrowIfCalledMultiple()
+ {
+ var image = new Image(Configuration.Default, 10, 10);
+ var frameCollection = image.Frames as ImageFrameCollection;
+
+ image.Dispose(); // this should invalidate underlying collection as well
+ frameCollection.Dispose();
+ }
+
+ [Fact]
+ public void PublicProperties_ThrowIfDisposed()
+ {
+ var image = new Image(Configuration.Default, 10, 10);
+ var frameCollection = image.Frames as ImageFrameCollection;
+
+ image.Dispose(); // this should invalidate underlying collection as well
+
+ Assert.Throws(() => { var prop = frameCollection.RootFrame; });
+ }
+
+ [Fact]
+ public void PublicMethods_ThrowIfDisposed()
+ {
+ var image = new Image(Configuration.Default, 10, 10);
+ var frameCollection = image.Frames as ImageFrameCollection;
+
+ image.Dispose(); // this should invalidate underlying collection as well
+
+ Assert.Throws(() => { var res = frameCollection.AddFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.CloneFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.Contains(default); });
+ Assert.Throws(() => { var res = frameCollection.CreateFrame(); });
+ Assert.Throws(() => { var res = frameCollection.CreateFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.ExportFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.GetEnumerator(); });
+ Assert.Throws(() => { var prop = frameCollection.IndexOf(default); });
+ Assert.Throws(() => { var prop = frameCollection.InsertFrame(default, default); });
+ Assert.Throws(() => { frameCollection.RemoveFrame(default); });
+ Assert.Throws(() => { frameCollection.MoveFrame(default, default); });
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs
index 92109ed479..15838f6902 100644
--- a/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs
+++ b/tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs
@@ -263,6 +263,42 @@ namespace SixLabors.ImageSharp.Tests
Assert.False(this.Image.Frames.Contains(frame));
}
+ [Fact]
+ public void PublicProperties_ThrowIfDisposed()
+ {
+ var image = new Image(Configuration.Default, 10, 10);
+ var frameCollection = image.Frames;
+
+ image.Dispose(); // this should invalidate underlying collection as well
+
+ Assert.Throws(() => { var prop = frameCollection.RootFrame; });
+ }
+
+ [Fact]
+ public void PublicMethods_ThrowIfDisposed()
+ {
+ var image = new Image(Configuration.Default, 10, 10);
+ var frameCollection = image.Frames;
+ var rgba32Array = new Rgba32[0];
+
+ image.Dispose(); // this should invalidate underlying collection as well
+
+ Assert.Throws(() => { var res = frameCollection.AddFrame((ImageFrame)null); });
+ Assert.Throws(() => { var res = frameCollection.AddFrame(rgba32Array); });
+ Assert.Throws(() => { var res = frameCollection.AddFrame((ImageFrame)null); });
+ Assert.Throws(() => { var res = frameCollection.AddFrame(rgba32Array.AsSpan()); });
+ Assert.Throws(() => { var res = frameCollection.CloneFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.Contains(default); });
+ Assert.Throws(() => { var res = frameCollection.CreateFrame(); });
+ Assert.Throws(() => { var res = frameCollection.CreateFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.ExportFrame(default); });
+ Assert.Throws(() => { var res = frameCollection.GetEnumerator(); });
+ Assert.Throws(() => { var prop = frameCollection.IndexOf(default); });
+ Assert.Throws(() => { var prop = frameCollection.InsertFrame(default, default); });
+ Assert.Throws(() => { frameCollection.RemoveFrame(default); });
+ Assert.Throws(() => { frameCollection.MoveFrame(default, default); });
+ }
+
///
/// Integration test for end-to end API validation.
///
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
index 3fbe1f70d8..271aa30cf4 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests
private static readonly Size ExpectedImageSize = new Size(108, 202);
- private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
+ private static byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes;
private IImageInfo LocalImageInfo => this.localImageInfoMock.Object;
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void FromBytes_GlobalConfiguration()
{
- IImageInfo info = Image.Identify(this.ActualImageBytes, out IImageFormat type);
+ IImageInfo info = Image.Identify(ActualImageBytes, out IImageFormat type);
Assert.Equal(ExpectedImageSize, info.Size());
Assert.Equal(ExpectedGlobalFormat, type);
@@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void FromStream_GlobalConfiguration()
{
- using (var stream = new MemoryStream(this.ActualImageBytes))
+ using (var stream = new MemoryStream(ActualImageBytes))
{
IImageInfo info = Image.Identify(stream, out IImageFormat type);
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void FromStream_GlobalConfiguration_NoFormat()
{
- using (var stream = new MemoryStream(this.ActualImageBytes))
+ using (var stream = new MemoryStream(ActualImageBytes))
{
IImageInfo info = Image.Identify(stream);
@@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void FromNonSeekableStream_GlobalConfiguration()
{
- using var stream = new MemoryStream(this.ActualImageBytes);
+ using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
IImageInfo info = Image.Identify(nonSeekableStream, out IImageFormat type);
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void FromNonSeekableStream_GlobalConfiguration_NoFormat()
{
- using var stream = new MemoryStream(this.ActualImageBytes);
+ using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
IImageInfo info = Image.Identify(nonSeekableStream);
@@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public async Task FromStreamAsync_GlobalConfiguration_NoFormat()
{
- using (var stream = new MemoryStream(this.ActualImageBytes))
+ using (var stream = new MemoryStream(ActualImageBytes))
{
var asyncStream = new AsyncStreamWrapper(stream, () => false);
IImageInfo info = await Image.IdentifyAsync(asyncStream);
@@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public async Task FromStreamAsync_GlobalConfiguration()
{
- using (var stream = new MemoryStream(this.ActualImageBytes))
+ using (var stream = new MemoryStream(ActualImageBytes))
{
var asyncStream = new AsyncStreamWrapper(stream, () => false);
(IImageInfo ImageInfo, IImageFormat Format) res = await Image.IdentifyWithFormatAsync(asyncStream);
@@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public async Task FromNonSeekableStreamAsync_GlobalConfiguration_NoFormat()
{
- using var stream = new MemoryStream(this.ActualImageBytes);
+ using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false);
@@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public async Task FromNonSeekableStreamAsync_GlobalConfiguration()
{
- using var stream = new MemoryStream(this.ActualImageBytes);
+ using var stream = new MemoryStream(ActualImageBytes);
using var nonSeekableStream = new NonSeekableStream(stream);
var asyncStream = new AsyncStreamWrapper(nonSeekableStream, () => false);
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Save.cs b/tests/ImageSharp.Tests/Image/ImageTests.Save.cs
index ee46807e55..fe3df17215 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.Save.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.Save.cs
@@ -3,9 +3,8 @@
using System;
using System.IO;
-
using Moq;
-
+using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -13,8 +12,6 @@ using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
- using SixLabors.ImageSharp.Formats;
-
public partial class ImageTests
{
public class Save
@@ -23,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests
public void DetectedEncoding()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
- string file = System.IO.Path.Combine(dir, "DetectedEncoding.png");
+ string file = Path.Combine(dir, "DetectedEncoding.png");
using (var image = new Image(10, 10))
{
@@ -40,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests
public void WhenExtensionIsUnknown_Throws()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
- string file = System.IO.Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
+ string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
Assert.Throws(
() =>
@@ -56,14 +53,14 @@ namespace SixLabors.ImageSharp.Tests
public void SetEncoding()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
- string file = System.IO.Path.Combine(dir, "SetEncoding.dat");
+ string file = Path.Combine(dir, "SetEncoding.dat");
using (var image = new Image(10, 10))
{
image.Save(file, new PngEncoder());
}
- using (Image.Load(file, out var mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
@@ -72,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void ThrowsWhenDisposed()
{
- var image = new Image(5, 5);
+ using var image = new Image(5, 5);
image.Dispose();
IImageEncoder encoder = Mock.Of();
using (var stream = new MemoryStream())
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
index 4e6b002d0d..8bb121349f 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
@@ -4,20 +4,18 @@
using System;
using System.IO;
using System.Threading;
+using System.Threading.Tasks;
using Moq;
-using SixLabors.ImageSharp.Formats.Bmp;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests
{
- using System.Threading.Tasks;
- using SixLabors.ImageSharp.Advanced;
- using SixLabors.ImageSharp.Formats;
- using SixLabors.ImageSharp.Tests.TestUtilities;
-
public partial class ImageTests
{
public class SaveAsync
@@ -43,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests
public async Task WhenExtensionIsUnknown_Throws()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
- string file = System.IO.Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
+ string file = Path.Combine(dir, "UnknownExtensionsEncoding_Throws.tmp");
await Assert.ThrowsAsync(
async () =>
@@ -59,14 +57,14 @@ namespace SixLabors.ImageSharp.Tests
public async Task SetEncoding()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests));
- string file = System.IO.Path.Combine(dir, "SetEncoding.dat");
+ string file = Path.Combine(dir, "SetEncoding.dat");
using (var image = new Image(10, 10))
{
await image.SaveAsync(file, new PngEncoder());
}
- using (Image.Load(file, out var mime))
+ using (Image.Load(file, out IImageFormat mime))
{
Assert.Equal("image/png", mime.DefaultMimeType);
}
@@ -142,10 +140,15 @@ namespace SixLabors.ImageSharp.Tests
using var stream = new MemoryStream();
var asyncStream = new AsyncStreamWrapper(stream, () => false);
var cts = new CancellationTokenSource();
- cts.CancelAfter(TimeSpan.FromTicks(1));
- await Assert.ThrowsAnyAsync(() =>
- image.SaveAsync(asyncStream, encoder, cts.Token));
+ var pausedStream = new PausedStream(asyncStream);
+ pausedStream.OnWaiting(s =>
+ {
+ cts.Cancel();
+ pausedStream.Release();
+ });
+
+ await Assert.ThrowsAsync(async () => await image.SaveAsync(pausedStream, encoder, cts.Token));
}
}
}
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs
index b7c6b3835a..1296f26c47 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.cs
@@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.IO;
using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@@ -169,5 +171,72 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal("y", ex.ParamName);
}
}
+
+ public class Dispose
+ {
+ private readonly Configuration configuration = Configuration.CreateDefaultInstance();
+
+ public void MultipleDisposeCalls()
+ {
+ var image = new Image(this.configuration, 10, 10);
+ image.Dispose();
+ image.Dispose();
+ }
+
+ [Fact]
+ public void NonPrivateProperties_ObjectDisposedException()
+ {
+ var image = new Image(this.configuration, 10, 10);
+ var genericImage = (Image)image;
+
+ image.Dispose();
+
+ // Image
+ Assert.Throws(() => { var prop = image.Frames; });
+
+ // Image
+ Assert.Throws(() => { var prop = genericImage.Frames; });
+ }
+
+ [Fact]
+ public void Save_ObjectDisposedException()
+ {
+ using var stream = new MemoryStream();
+ var image = new Image(this.configuration, 10, 10);
+ var encoder = new JpegEncoder();
+
+ image.Dispose();
+
+ // Image