private static JpegColorConverter.ComponentValues CreateRandomValues( int componentCount, int inputBufferLength, int seed, float minVal = 0f, float maxVal = 255f) { var rnd = new Random(seed); var buffers = new Buffer2D <float> [componentCount]; for (int i = 0; i < componentCount; i++) { var values = new float[inputBufferLength]; for (int j = 0; j < inputBufferLength; j++) { values[j] = ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } // no need to dispose when buffer is not array owner var memory = new Memory <float>(values); var source = MemoryGroup <float> .Wrap(memory); buffers[i] = new Buffer2D <float>(source, values.Length, 1); } return(new JpegColorConverter.ComponentValues(buffers, 0)); }
public void Wrap() { int[] data0 = { 1, 2, 3, 4 }; int[] data1 = { 5, 6, 7, 8 }; int[] data2 = { 9, 10 }; using var mgr0 = new TestMemoryManager <int>(data0); using var mgr1 = new TestMemoryManager <int>(data1); using var group = MemoryGroup <int> .Wrap(mgr0.Memory, mgr1.Memory, data2); Assert.Equal(3, group.Count); Assert.Equal(4, group.BufferLength); Assert.Equal(10, group.TotalLength); Assert.True(group[0].Span.SequenceEqual(data0)); Assert.True(group[1].Span.SequenceEqual(data1)); Assert.True(group[2].Span.SequenceEqual(data2)); int cnt = 0; int[][] allData = { data0, data1, data2 }; foreach (Memory <int> memory in group) { Assert.True(memory.Span.SequenceEqual(allData[cnt])); cnt++; } }
/// <summary> /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance, /// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>. /// It will be disposed together with the result image. /// </summary> /// <typeparam name="TPixel">The pixel type</typeparam> /// <param name="config">The <see cref="ImageSharp.Configuration"/></param> /// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param> /// <param name="width">The width of the memory image.</param> /// <param name="height">The height of the memory image.</param> /// <param name="metadata">The <see cref="ImageMetadata"/></param> /// <returns>An <see cref="Image{TPixel}"/> instance</returns> public static Image <TPixel> WrapMemory <TPixel>( Configuration config, IMemoryOwner <TPixel> pixelMemoryOwner, int width, int height, ImageMetadata metadata) where TPixel : struct, IPixel <TPixel> { var memorySource = MemoryGroup <TPixel> .Wrap(pixelMemoryOwner); return(new Image <TPixel>(config, memorySource, width, height, metadata)); }
/// <summary> /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance, /// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>. /// It will be disposed together with the result image. /// </summary> /// <typeparam name="TPixel">The pixel type</typeparam> /// <param name="configuration">The <see cref="Configuration"/></param> /// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param> /// <param name="width">The width of the memory image.</param> /// <param name="height">The height of the memory image.</param> /// <param name="metadata">The <see cref="ImageMetadata"/></param> /// <exception cref="ArgumentNullException">The configuration is null.</exception> /// <exception cref="ArgumentNullException">The metadata is null.</exception> /// <returns>An <see cref="Image{TPixel}"/> instance</returns> public static Image <TPixel> WrapMemory <TPixel>( Configuration configuration, IMemoryOwner <TPixel> pixelMemoryOwner, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel <TPixel> { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); var memorySource = MemoryGroup <TPixel> .Wrap(pixelMemoryOwner); return(new Image <TPixel>(configuration, memorySource, width, height, metadata)); }
/// <summary> /// <para> /// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as /// an <see cref="Image{TPixel}"/> instance. /// </para> /// <para> /// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/> /// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer /// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was /// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed. /// </para> /// <para> /// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance /// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still /// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other /// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately. /// </para> /// </summary> /// <typeparam name="TPixel">The pixel type</typeparam> /// <param name="configuration">The <see cref="Configuration"/></param> /// <param name="pixelMemory">The pixel memory.</param> /// <param name="width">The width of the memory image.</param> /// <param name="height">The height of the memory image.</param> /// <param name="metadata">The <see cref="ImageMetadata"/>.</param> /// <exception cref="ArgumentNullException">The configuration is null.</exception> /// <exception cref="ArgumentNullException">The metadata is null.</exception> /// <returns>An <see cref="Image{TPixel}"/> instance</returns> public static Image <TPixel> WrapMemory <TPixel>( Configuration configuration, Memory <TPixel> pixelMemory, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel <TPixel> { 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 <TPixel> .Wrap(pixelMemory); return(new Image <TPixel>(configuration, memorySource, width, height, metadata)); }
/// <summary> /// <para> /// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as /// an <see cref="Image{TPixel}"/> instance. /// </para> /// <para> /// Please note: this method relies on callers to carefully manage the target memory area being referenced by the /// pointer and that the lifetime of such a memory area is at least equal to that of the returned /// <see cref="Image{TPixel}"/> instance. For example, if the input pointer references an unmanaged memory area, /// callers must ensure that the memory area is not freed as long as the returned <see cref="Image{TPixel}"/> is /// in use and not disposed. The same applies if the input memory area points to a pinned managed object, as callers /// must ensure that objects will remain pinned as long as the <see cref="Image{TPixel}"/> instance is in use. /// Failing to do so constitutes undefined behavior and will likely lead to memory corruption and runtime crashes. /// </para> /// <para> /// Note also that if you have a <see cref="Memory{T}"/> or an array (which can be cast to <see cref="Memory{T}"/>) of /// either <see cref="byte"/> or <typeparamref name="TPixel"/> values, it is highly recommended to use one of the other /// available overloads of this method instead (such as <see cref="WrapMemory{TPixel}(Configuration, Memory{byte}, int, int)"/> /// or <see cref="WrapMemory{TPixel}(Configuration, Memory{TPixel}, int, int)"/>, to make the resulting code less error /// prone and avoid having to pin the underlying memory buffer in use. This method is primarily meant to be used when /// doing interop or working with buffers that are located in unmanaged memory. /// </para> /// </summary> /// <typeparam name="TPixel">The pixel type</typeparam> /// <param name="configuration">The <see cref="Configuration"/></param> /// <param name="pointer">The pointer to the target memory buffer to wrap.</param> /// <param name="width">The width of the memory image.</param> /// <param name="height">The height of the memory image.</param> /// <param name="metadata">The <see cref="ImageMetadata"/>.</param> /// <exception cref="ArgumentNullException">The configuration is null.</exception> /// <exception cref="ArgumentNullException">The metadata is null.</exception> /// <returns>An <see cref="Image{TPixel}"/> instance</returns> public static unsafe Image <TPixel> WrapMemory <TPixel>( Configuration configuration, void *pointer, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel <TPixel> { Guard.IsFalse(pointer == null, nameof(pointer), "Pointer must be not null"); Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); var memoryManager = new UnmanagedMemoryManager <TPixel>(pointer, width * height); var memorySource = MemoryGroup <TPixel> .Wrap(memoryManager.Memory); return(new Image <TPixel>(configuration, memorySource, width, height, metadata)); }
public void Wrap() { int[] data0 = { 1, 2, 3, 4 }; int[] data1 = { 5, 6, 7, 8 }; int[] data2 = { 9, 10 }; using var mgr0 = new TestMemoryManager <int>(data0); using var mgr1 = new TestMemoryManager <int>(data1); using var group = MemoryGroup <int> .Wrap(mgr0.Memory, mgr1.Memory, data2); Assert.Equal(3, group.Count); Assert.Equal(4, group.BufferLength); Assert.Equal(10, group.TotalLength); Assert.True(group[0].Span.SequenceEqual(data0)); Assert.True(group[1].Span.SequenceEqual(data1)); Assert.True(group[2].Span.SequenceEqual(data2)); }
public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) { var data = new Rgba32[21]; var color = new Rgba32(1, 2, 3, 4); using var destOwner = new TestMemoryManager <Rgba32>(data); using var dest = new Buffer2D <Rgba32>(MemoryGroup <Rgba32> .Wrap(destOwner.Memory), 21, 1); using Buffer2D <Rgba32> source = this.MemoryAllocator.Allocate2D <Rgba32>(22, 1); source.FastMemoryGroup[0].Span[10] = color; // Act: Assert.ThrowsAny <InvalidOperationException>(() => Buffer2D <Rgba32> .SwapOrCopyContent(dest, source)); Assert.Equal(color, source.MemoryGroup[0].Span[10]); Assert.NotEqual(color, dest.MemoryGroup[0].Span[10]); }
/// <summary> /// Wraps an existing contiguous memory area of at least 'width' x 'height' pixels, /// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance. /// The ownership of the <paramref name="byteMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance, /// meaning that the caller is not allowed to dispose <paramref name="byteMemoryOwner"/>. /// It will be disposed together with the result image. /// </summary> /// <typeparam name="TPixel">The pixel type</typeparam> /// <param name="configuration">The <see cref="Configuration"/></param> /// <param name="byteMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image</param> /// <param name="width">The width of the memory image.</param> /// <param name="height">The height of the memory image.</param> /// <param name="metadata">The <see cref="ImageMetadata"/></param> /// <exception cref="ArgumentNullException">The configuration is null.</exception> /// <exception cref="ArgumentNullException">The metadata is null.</exception> /// <returns>An <see cref="Image{TPixel}"/> instance</returns> public static Image <TPixel> WrapMemory <TPixel>( Configuration configuration, IMemoryOwner <byte> byteMemoryOwner, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel <TPixel> { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(metadata, nameof(metadata)); var pixelMemoryOwner = new ByteMemoryOwner <TPixel>(byteMemoryOwner); Guard.IsTrue(pixelMemoryOwner.Memory.Length >= (long)width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size"); var memorySource = MemoryGroup <TPixel> .Wrap(pixelMemoryOwner); return(new Image <TPixel>(configuration, memorySource, width, height, metadata)); }
public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated) { var data = new Rgba32[21]; var color = new Rgba32(1, 2, 3, 4); using var destOwner = new TestMemoryManager <Rgba32>(data); using var dest = new Buffer2D <Rgba32>(MemoryGroup <Rgba32> .Wrap(destOwner.Memory), 21, 1); using Buffer2D <Rgba32> source = this.MemoryAllocator.Allocate2D <Rgba32>(21, 1); source.FastMemoryGroup[0].Span[10] = color; // Act: bool swap = Buffer2D <Rgba32> .SwapOrCopyContent(dest, source); // Assert: Assert.False(swap); Assert.Equal(color, dest.MemoryGroup[0].Span[10]); Assert.NotEqual(source.FastMemoryGroup[0], dest.FastMemoryGroup[0]); }
public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer() { using var destData = MemoryGroup <int> .Wrap(new int[100]); using var dest = new Buffer2D <int>(destData, 10, 10); using (Buffer2D <int> source = this.MemoryAllocator.Allocate2D <int>(10, 10, AllocationOptions.Clean)) { source[0, 0] = 1; dest[0, 0] = 2; Buffer2D <int> .SwapOrCopyContent(dest, source); } int actual1 = dest.DangerousGetRowSpan(0)[0]; int actual2 = dest.DangerousGetRowSpan(0)[0]; int actual3 = dest.GetSafeRowMemory(0).Span[0]; int actual5 = dest[0, 0]; Assert.Equal(1, actual1); Assert.Equal(1, actual2); Assert.Equal(1, actual3); Assert.Equal(1, actual5); }