diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 412b1d807..0e093a834 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -64,7 +64,7 @@ jobs:
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
- name: Update Codecov
- uses: codecov/codecov-action@v1.0.7
+ uses: codecov/codecov-action@v1
if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
with:
flags: unittests
diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index c5abbda61..54a773be0 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -23,7 +23,9 @@ namespace SixLabors.ImageSharp.Advanced
///
/// The source image.
/// The target file path to save the image to.
- /// The matching encoder.
+ /// The file path is null.
+ /// No encoder available for provided path.
+ /// The matching .
public static IImageEncoder DetectEncoder(this Image source, string filePath)
{
Guard.NotNull(filePath, nameof(filePath));
diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs
index 2d3c29ed4..d89c44dc5 100644
--- a/src/ImageSharp/Image.WrapMemory.cs
+++ b/src/ImageSharp/Image.WrapMemory.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp
{
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
///
/// The pixel type
/// The
@@ -38,6 +38,7 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
+ Guard.IsTrue(pixelMemory.Length == width * height, nameof(pixelMemory), "The length of the input memory doesn't match the specified image size");
var memorySource = MemoryGroup.Wrap(pixelMemory);
return new Image(configuration, memorySource, width, height, metadata);
@@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
///
/// The pixel type
/// The
@@ -64,7 +65,7 @@ namespace SixLabors.ImageSharp
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
///
/// The pixel type.
@@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
/// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
@@ -105,6 +106,7 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
+ Guard.IsTrue(pixelMemoryOwner.Memory.Length == width * height, nameof(pixelMemoryOwner), "The length of the input memory doesn't match the specified image size");
var memorySource = MemoryGroup.Wrap(pixelMemoryOwner);
return new Image(configuration, memorySource, width, height, metadata);
@@ -112,7 +114,7 @@ namespace SixLabors.ImageSharp
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
/// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
@@ -134,7 +136,7 @@ namespace SixLabors.ImageSharp
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
- /// allowing to view/manipulate it as an ImageSharp instance.
+ /// allowing to view/manipulate it as an instance.
/// The ownership of the is being transferred to the new instance,
/// meaning that the caller is not allowed to dispose .
/// It will be disposed together with the result image.
@@ -150,5 +152,73 @@ namespace SixLabors.ImageSharp
int height)
where TPixel : unmanaged, IPixel
=> WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
+
+ ///
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
+ /// allowing to view/manipulate it as an instance.
+ ///
+ /// The pixel type
+ /// The
+ /// The byte memory representing the pixel data.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// The .
+ /// The configuration is null.
+ /// The metadata is null.
+ /// An instance
+ public static Image WrapMemory(
+ Configuration configuration,
+ Memory byteMemory,
+ int width,
+ int height,
+ ImageMetadata metadata)
+ where TPixel : unmanaged, IPixel
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(metadata, nameof(metadata));
+
+ var memoryManager = new ByteMemoryManager(byteMemory);
+
+ Guard.IsTrue(memoryManager.Memory.Length == width * height, nameof(byteMemory), "The length of the input memory doesn't match the specified image size");
+
+ var memorySource = MemoryGroup.Wrap(memoryManager.Memory);
+ return new Image(configuration, memorySource, width, height, metadata);
+ }
+
+ ///
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
+ /// allowing to view/manipulate it as an instance.
+ ///
+ /// The pixel type
+ /// The
+ /// The byte memory representing the pixel data.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// The configuration is null.
+ /// An instance.
+ public static Image WrapMemory(
+ Configuration configuration,
+ Memory byteMemory,
+ int width,
+ int height)
+ where TPixel : unmanaged, IPixel
+ => WrapMemory(configuration, byteMemory, width, height, new ImageMetadata());
+
+ ///
+ /// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
+ /// allowing to view/manipulate it as an instance.
+ /// The memory is being observed, the caller remains responsible for managing it's lifecycle.
+ ///
+ /// The pixel type.
+ /// The byte memory representing the pixel data.
+ /// The width of the memory image.
+ /// The height of the memory image.
+ /// An instance.
+ public static Image WrapMemory(
+ Memory byteMemory,
+ int width,
+ int height)
+ where TPixel : unmanaged, IPixel
+ => WrapMemory(Configuration.Default, byteMemory, width, height);
}
}
diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs
index d40c5c271..75cd32106 100644
--- a/src/ImageSharp/ImageExtensions.cs
+++ b/src/ImageSharp/ImageExtensions.cs
@@ -18,27 +18,29 @@ namespace SixLabors.ImageSharp
public static partial class ImageExtensions
{
///
- /// Writes the image to the given stream using the currently loaded image format.
+ /// Writes the image to the given file path using an encoder detected from the path.
///
/// The source image.
/// The file path to save the image to.
/// The path is null.
+ /// No encoder available for provided path.
public static void Save(this Image source, string path)
=> source.Save(path, source.DetectEncoder(path));
///
- /// Writes the image to the given stream using the currently loaded image format.
+ /// Writes the image to the given file path using an encoder detected from the path.
///
/// The source image.
/// The file path to save the image to.
/// The token to monitor for cancellation requests.
/// The path is null.
+ /// No encoder available for provided path.
/// A representing the asynchronous operation.
public static Task SaveAsync(this Image source, string path, CancellationToken cancellationToken = default)
=> source.SaveAsync(path, source.DetectEncoder(path), cancellationToken);
///
- /// Writes the image to the given stream using the currently loaded image format.
+ /// Writes the image to the given file path using the given image encoder.
///
/// The source image.
/// The file path to save the image to.
@@ -56,7 +58,7 @@ namespace SixLabors.ImageSharp
}
///
- /// Writes the image to the given stream using the currently loaded image format.
+ /// Writes the image to the given file path using the given image encoder.
///
/// The source image.
/// The file path to save the image to.
@@ -73,12 +75,15 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
- using Stream fs = source.GetConfiguration().FileSystem.Create(path);
- await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
+
+ using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
+ {
+ await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
+ }
}
///
- /// Writes the image to the given stream using the currently loaded image format.
+ /// Writes the image to the given stream using the given image format.
///
/// The source image.
/// The stream to save the image to.
@@ -115,6 +120,50 @@ namespace SixLabors.ImageSharp
source.Save(stream, encoder);
}
+ ///
+ /// Writes the image to the given stream using the given image format.
+ ///
+ /// The source image.
+ /// The stream to save the image to.
+ /// The format to save the image in.
+ /// The token to monitor for cancellation requests.
+ /// The stream is null.
+ /// The format is null.
+ /// The stream is not writable.
+ /// No encoder available for provided format.
+ /// A representing the asynchronous operation.
+ public static Task SaveAsync(
+ this Image source,
+ Stream stream,
+ IImageFormat format,
+ CancellationToken cancellationToken = default)
+ {
+ Guard.NotNull(stream, nameof(stream));
+ Guard.NotNull(format, nameof(format));
+
+ if (!stream.CanWrite)
+ {
+ throw new NotSupportedException("Cannot write to the stream.");
+ }
+
+ IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
+
+ if (encoder is null)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
+
+ foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
+ {
+ sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
+ }
+
+ throw new NotSupportedException(sb.ToString());
+ }
+
+ return source.SaveAsync(stream, encoder, cancellationToken);
+ }
+
///
/// Returns a Base64 encoded string from the given image.
/// The result is prepended with a Data URI
diff --git a/src/ImageSharp/Memory/ByteMemoryManager{T}.cs b/src/ImageSharp/Memory/ByteMemoryManager{T}.cs
new file mode 100644
index 000000000..223709df6
--- /dev/null
+++ b/src/ImageSharp/Memory/ByteMemoryManager{T}.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+using System;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Memory
+{
+ ///
+ /// A custom that can wrap of instances
+ /// and cast them to be for any arbitrary unmanaged value type.
+ ///
+ /// The value type to use when casting the wrapped instance.
+ internal sealed class ByteMemoryManager : MemoryManager
+ where T : unmanaged
+ {
+ ///
+ /// The wrapped of instance.
+ ///
+ private readonly Memory memory;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The of instance to wrap.
+ public ByteMemoryManager(Memory memory)
+ {
+ this.memory = memory;
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ }
+
+ ///
+ public override Span GetSpan()
+ {
+ return MemoryMarshal.Cast(this.memory.Span);
+ }
+
+ ///
+ public override MemoryHandle Pin(int elementIndex = 0)
+ {
+ // We need to adjust the offset into the wrapped byte segment,
+ // as the input index refers to the target-cast memory of T.
+ // We just have to shift this index by the byte size of T.
+ return this.memory.Slice(elementIndex * Unsafe.SizeOf()).Pin();
+ }
+
+ ///
+ public override void Unpin()
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
index aa475a80f..c2551ccf2 100644
--- a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
+++ b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs
@@ -13,13 +13,27 @@ namespace SixLabors.ImageSharp.Memory
///
internal static class MemoryOwnerExtensions
{
+ ///
+ /// Gets a from an instance.
+ ///
+ /// The buffer
+ /// The
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span GetSpan(this IMemoryOwner buffer)
- => buffer.Memory.Span;
+ {
+ return buffer.Memory.Span;
+ }
+ ///
+ /// Gets the length of an internal buffer.
+ ///
+ /// The buffer
+ /// The length of the buffer
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Length(this IMemoryOwner buffer)
- => buffer.GetSpan().Length;
+ {
+ return buffer.Memory.Length;
+ }
///
/// Gets a to an offsetted position inside the buffer.
@@ -56,8 +70,16 @@ namespace SixLabors.ImageSharp.Memory
buffer.GetSpan().Clear();
}
+ ///
+ /// Gets a reference to the first item in the internal buffer for an instance.
+ ///
+ /// The buffer
+ /// A reference to the first item within the memory wrapped by
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T GetReference(this IMemoryOwner buffer)
- where T : struct =>
- ref MemoryMarshal.GetReference(buffer.GetSpan());
+ where T : struct
+ {
+ return ref MemoryMarshal.GetReference(buffer.GetSpan());
+ }
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
index 27eca523c..8a4c703e0 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
@@ -29,8 +29,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the class.
///
public BokehBlurProcessor()
- : this(DefaultRadius, DefaultComponents, DefaultGamma)
{
+ this.Radius = DefaultRadius;
+ this.Components = DefaultComponents;
+ this.Gamma = DefaultGamma;
}
///
@@ -47,6 +49,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public BokehBlurProcessor(int radius, int components, float gamma)
{
+ Guard.MustBeGreaterThan(radius, 0, nameof(radius));
+ Guard.MustBeBetweenOrEqualTo(components, 1, 6, nameof(components));
Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma));
this.Radius = radius;
diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
index b11411e32..448eb3833 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs
@@ -262,7 +262,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
this.source = source;
this.bounds = bounds;
this.scale = processor.DitherScale;
- this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Span.Length);
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Length);
}
[MethodImpl(InliningOptions.ShortMethod)]
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
index 40c3b65b5..4e6b002d0 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs
@@ -72,6 +72,37 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ [Theory]
+ [InlineData("test.png", "image/png")]
+ [InlineData("test.tga", "image/tga")]
+ [InlineData("test.bmp", "image/bmp")]
+ [InlineData("test.jpg", "image/jpeg")]
+ [InlineData("test.gif", "image/gif")]
+ public async Task SaveStreamWithMime(string filename, string mimeType)
+ {
+ using (var image = new Image(5, 5))
+ {
+ string ext = Path.GetExtension(filename);
+ IImageFormat format = image.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
+ Assert.Equal(mimeType, format.DefaultMimeType);
+
+ using (var stream = new MemoryStream())
+ {
+ var asyncStream = new AsyncStreamWrapper(stream, () => false);
+ await image.SaveAsync(asyncStream, format);
+
+ stream.Position = 0;
+
+ (Image Image, IImageFormat Format) imf = await Image.LoadWithFormatAsync(stream);
+
+ Assert.Equal(format, imf.Format);
+ Assert.Equal(mimeType, imf.Format.DefaultMimeType);
+
+ imf.Image.Dispose();
+ }
+ }
+ }
+
[Fact]
public async Task ThrowsWhenDisposed()
{
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
index 2b30d9459..7dc7dbb30 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
@@ -6,10 +6,10 @@ using System.Buffers;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -80,10 +80,52 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ public sealed class CastMemoryManager : MemoryManager
+ where TFrom : unmanaged
+ where TTo : unmanaged
+ {
+ private readonly Memory memory;
+
+ public CastMemoryManager(Memory memory)
+ {
+ this.memory = memory;
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ }
+
+ ///
+ public override Span GetSpan()
+ {
+ return MemoryMarshal.Cast(this.memory.Span);
+ }
+
+ ///
+ public override MemoryHandle Pin(int elementIndex = 0)
+ {
+ int byteOffset = elementIndex * Unsafe.SizeOf();
+ int shiftedOffset = Math.DivRem(byteOffset, Unsafe.SizeOf(), out int remainder);
+
+ if (remainder != 0)
+ {
+ ThrowHelper.ThrowArgumentException("The input index doesn't result in an aligned item access", nameof(elementIndex));
+ }
+
+ return this.memory.Slice(shiftedOffset).Pin();
+ }
+
+ ///
+ public override void Unpin()
+ {
+ }
+ }
+
[Fact]
public void WrapMemory_CreatedImageIsCorrect()
{
- Configuration cfg = Configuration.Default.Clone();
+ var cfg = Configuration.CreateDefaultInstance();
var metaData = new ImageMetadata();
var array = new Rgba32[25];
@@ -173,6 +215,124 @@ namespace SixLabors.ImageSharp.Tests
}
}
+ [Fact]
+ public void WrapMemory_FromBytes_CreatedImageIsCorrect()
+ {
+ var cfg = Configuration.CreateDefaultInstance();
+ var metaData = new ImageMetadata();
+
+ var array = new byte[25 * Unsafe.SizeOf()];
+ var memory = new Memory(array);
+
+ using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData))
+ {
+ Assert.True(image.TryGetSinglePixelSpan(out Span imageSpan));
+ ref Rgba32 pixel0 = ref imageSpan[0];
+ Assert.True(Unsafe.AreSame(ref Unsafe.As(ref array[0]), ref pixel0));
+
+ Assert.Equal(cfg, image.GetConfiguration());
+ Assert.Equal(metaData, image.Metadata);
+ }
+ }
+
+ [Fact]
+ public void WrapSystemDrawingBitmap_FromBytes_WhenObserved()
+ {
+ if (ShouldSkipBitmapTest)
+ {
+ return;
+ }
+
+ using (var bmp = new Bitmap(51, 23))
+ {
+ using (var memoryManager = new BitmapMemoryManager(bmp))
+ {
+ Memory pixelMemory = memoryManager.Memory;
+ Memory byteMemory = new CastMemoryManager(pixelMemory).Memory;
+ Bgra32 bg = Color.Red;
+ Bgra32 fg = Color.Green;
+
+ using (var image = Image.WrapMemory(byteMemory, bmp.Width, bmp.Height))
+ {
+ Span pixelSpan = pixelMemory.Span;
+ Span imageSpan = image.GetRootFramePixelBuffer().GetSingleMemory().Span;
+
+ // We can't compare the two Memory instances directly as they wrap different memory managers.
+ // To check that the underlying data matches, we can just manually check their lenth, and the
+ // fact that a reference to the first pixel in both spans is actually the same memory location.
+ Assert.Equal(pixelSpan.Length, imageSpan.Length);
+ Assert.True(Unsafe.AreSame(ref pixelSpan.GetPinnableReference(), ref imageSpan.GetPinnableReference()));
+
+ Assert.True(image.TryGetSinglePixelSpan(out imageSpan));
+ imageSpan.Fill(bg);
+ for (var i = 10; i < 20; i++)
+ {
+ image.GetPixelRowSpan(i).Slice(10, 10).Fill(fg);
+ }
+ }
+
+ Assert.False(memoryManager.IsDisposed);
+ }
+
+ string fn = System.IO.Path.Combine(
+ TestEnvironment.ActualOutputDirectoryFullPath,
+ $"{nameof(this.WrapSystemDrawingBitmap_WhenObserved)}.bmp");
+
+ bmp.Save(fn, ImageFormat.Bmp);
+ }
+ }
+
+ [Theory]
+ [InlineData(0, 5, 5)]
+ [InlineData(20, 5, 5)]
+ [InlineData(26, 5, 5)]
+ [InlineData(2, 1, 1)]
+ [InlineData(1023, 32, 32)]
+ public void WrapMemory_MemoryOfT_InvalidSize(int size, int height, int width)
+ {
+ var array = new Rgba32[size];
+ var memory = new Memory(array);
+
+ Assert.Throws(() => Image.WrapMemory(memory, height, width));
+ }
+
+ private class TestMemoryOwner : IMemoryOwner
+ {
+ public Memory Memory { get; set; }
+
+ public void Dispose()
+ {
+ }
+ }
+
+ [Theory]
+ [InlineData(0, 5, 5)]
+ [InlineData(20, 5, 5)]
+ [InlineData(26, 5, 5)]
+ [InlineData(2, 1, 1)]
+ [InlineData(1023, 32, 32)]
+ public void WrapMemory_IMemoryOwnerOfT_InvalidSize(int size, int height, int width)
+ {
+ var array = new Rgba32[size];
+ var memory = new TestMemoryOwner { Memory = array };
+
+ Assert.Throws(() => Image.WrapMemory(memory, height, width));
+ }
+
+ [Theory]
+ [InlineData(0, 5, 5)]
+ [InlineData(20, 5, 5)]
+ [InlineData(26, 5, 5)]
+ [InlineData(2, 1, 1)]
+ [InlineData(1023, 32, 32)]
+ public void WrapMemory_MemoryOfByte_InvalidSize(int size, int height, int width)
+ {
+ var array = new byte[size * Unsafe.SizeOf()];
+ var memory = new Memory(array);
+
+ Assert.Throws(() => Image.WrapMemory(memory, height, width));
+ }
+
private static bool ShouldSkipBitmapTest =>
!TestEnvironment.Is64BitProcess || (TestHelpers.ImageSharpBuiltAgainst != "netcoreapp3.1" && TestHelpers.ImageSharpBuiltAgainst != "netcoreapp2.1");
}
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs
index 490a6ea49..50b8782e4 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs
@@ -35,6 +35,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
0.02565295+0.01611732j 0.0153483+0.01605112j 0.00698622+0.01370844j
0.00135338+0.00998296j -0.00152245+0.00604545j -0.00227282+0.002851j ]]";
+ [Theory]
+ [InlineData(-10, 2, 3f)]
+ [InlineData(-1, 2, 3f)]
+ [InlineData(0, 2, 3f)]
+ [InlineData(20, -1, 3f)]
+ [InlineData(20, -0, 3f)]
+ [InlineData(20, 4, -10f)]
+ [InlineData(20, 4, 0f)]
+ public void VerifyBokehBlurProcessorArguments_Fail(int radius, int components, float gamma)
+ {
+ Assert.Throws(() => new BokehBlurProcessor(radius, components, gamma));
+ }
+
[Fact]
public void VerifyComplexComponents()
{