Skip to content

Commit a603b96

Browse files
Merge pull request #1419 from SixLabors/sp/image-wrap-ptr
New Image.WrapMemory<TPixel>(void*) overloads
2 parents b4e7d80 + 62dc5d3 commit a603b96

File tree

4 files changed

+361
-14
lines changed

4 files changed

+361
-14
lines changed

src/ImageSharp/Image.WrapMemory.cs

Lines changed: 219 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,22 @@ namespace SixLabors.ImageSharp
1616
public abstract partial class Image
1717
{
1818
/// <summary>
19-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
20-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
19+
/// <para>
20+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
21+
/// an <see cref="Image{TPixel}"/> instance.
22+
/// </para>
23+
/// <para>
24+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
25+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
26+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
27+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
28+
/// </para>
29+
/// <para>
30+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
31+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
32+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
33+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
34+
/// </para>
2135
/// </summary>
2236
/// <typeparam name="TPixel">The pixel type</typeparam>
2337
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -45,8 +59,22 @@ public static Image<TPixel> WrapMemory<TPixel>(
4559
}
4660

4761
/// <summary>
48-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
49-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
62+
/// <para>
63+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
64+
/// an <see cref="Image{TPixel}"/> instance.
65+
/// </para>
66+
/// <para>
67+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
68+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
69+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
70+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
71+
/// </para>
72+
/// <para>
73+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
74+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
75+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
76+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
77+
/// </para>
5078
/// </summary>
5179
/// <typeparam name="TPixel">The pixel type</typeparam>
5280
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -64,9 +92,22 @@ public static Image<TPixel> WrapMemory<TPixel>(
6492
=> WrapMemory(configuration, pixelMemory, width, height, new ImageMetadata());
6593

6694
/// <summary>
67-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
68-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
69-
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
95+
/// <para>
96+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
97+
/// an <see cref="Image{TPixel}"/> instance.
98+
/// </para>
99+
/// <para>
100+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
101+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
102+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
103+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
104+
/// </para>
105+
/// <para>
106+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
107+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
108+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
109+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
110+
/// </para>
70111
/// </summary>
71112
/// <typeparam name="TPixel">The pixel type.</typeparam>
72113
/// <param name="pixelMemory">The pixel memory.</param>
@@ -154,8 +195,22 @@ public static Image<TPixel> WrapMemory<TPixel>(
154195
=> WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
155196

156197
/// <summary>
157-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
158-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
198+
/// <para>
199+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
200+
/// an <see cref="Image{TPixel}"/> instance.
201+
/// </para>
202+
/// <para>
203+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
204+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
205+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
206+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
207+
/// </para>
208+
/// <para>
209+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
210+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
211+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
212+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
213+
/// </para>
159214
/// </summary>
160215
/// <typeparam name="TPixel">The pixel type</typeparam>
161216
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -186,8 +241,22 @@ public static Image<TPixel> WrapMemory<TPixel>(
186241
}
187242

188243
/// <summary>
189-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
190-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
244+
/// <para>
245+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
246+
/// an <see cref="Image{TPixel}"/> instance.
247+
/// </para>
248+
/// <para>
249+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
250+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
251+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
252+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
253+
/// </para>
254+
/// <para>
255+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
256+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
257+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
258+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
259+
/// </para>
191260
/// </summary>
192261
/// <typeparam name="TPixel">The pixel type</typeparam>
193262
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -205,9 +274,22 @@ public static Image<TPixel> WrapMemory<TPixel>(
205274
=> WrapMemory<TPixel>(configuration, byteMemory, width, height, new ImageMetadata());
206275

207276
/// <summary>
208-
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
209-
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
210-
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
277+
/// <para>
278+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
279+
/// an <see cref="Image{TPixel}"/> instance.
280+
/// </para>
281+
/// <para>
282+
/// Please note: using this method does not transfer the ownership of the underlying buffer of the input <see cref="Memory{T}"/>
283+
/// to the new <see cref="Image{TPixel}"/> instance. This means that consumers of this method must ensure that the input buffer
284+
/// is either self-contained, (for example, a <see cref="Memory{T}"/> instance wrapping a new array that was
285+
/// created), or that the owning object is not disposed until the returned <see cref="Image{TPixel}"/> is disposed.
286+
/// </para>
287+
/// <para>
288+
/// If the input <see cref="Memory{T}"/> instance is one retrieved from an <see cref="IMemoryOwner{T}"/> instance
289+
/// rented from a memory pool (such as <see cref="MemoryPool{T}"/>), and that owning instance is disposed while the image is still
290+
/// in use, this will lead to undefined behavior and possibly runtime crashes (as the same buffer might then be modified by other
291+
/// consumers while the returned image is still working on it). Make sure to control the lifetime of the input buffers appropriately.
292+
/// </para>
211293
/// </summary>
212294
/// <typeparam name="TPixel">The pixel type.</typeparam>
213295
/// <param name="byteMemory">The byte memory representing the pixel data.</param>
@@ -220,5 +302,128 @@ public static Image<TPixel> WrapMemory<TPixel>(
220302
int height)
221303
where TPixel : unmanaged, IPixel<TPixel>
222304
=> WrapMemory<TPixel>(Configuration.Default, byteMemory, width, height);
305+
306+
/// <summary>
307+
/// <para>
308+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
309+
/// an <see cref="Image{TPixel}"/> instance.
310+
/// </para>
311+
/// <para>
312+
/// Please note: this method relies on callers to carefully manage the target memory area being referenced by the
313+
/// pointer and that the lifetime of such a memory area is at least equal to that of the returned
314+
/// <see cref="Image{TPixel}"/> instance. For example, if the input pointer references an unmanaged memory area,
315+
/// callers must ensure that the memory area is not freed as long as the returned <see cref="Image{TPixel}"/> is
316+
/// in use and not disposed. The same applies if the input memory area points to a pinned managed object, as callers
317+
/// must ensure that objects will remain pinned as long as the <see cref="Image{TPixel}"/> instance is in use.
318+
/// Failing to do so constitutes undefined behavior and will likely lead to memory corruption and runtime crashes.
319+
/// </para>
320+
/// <para>
321+
/// Note also that if you have a <see cref="Memory{T}"/> or an array (which can be cast to <see cref="Memory{T}"/>) of
322+
/// either <see cref="byte"/> or <typeparamref name="TPixel"/> values, it is highly recommended to use one of the other
323+
/// available overloads of this method instead (such as <see cref="WrapMemory{TPixel}(Configuration, Memory{byte}, int, int)"/>
324+
/// or <see cref="WrapMemory{TPixel}(Configuration, Memory{TPixel}, int, int)"/>, to make the resulting code less error
325+
/// prone and avoid having to pin the underlying memory buffer in use. This method is primarily meant to be used when
326+
/// doing interop or working with buffers that are located in unmanaged memory.
327+
/// </para>
328+
/// </summary>
329+
/// <typeparam name="TPixel">The pixel type</typeparam>
330+
/// <param name="configuration">The <see cref="Configuration"/></param>
331+
/// <param name="pointer">The pointer to the target memory buffer to wrap.</param>
332+
/// <param name="width">The width of the memory image.</param>
333+
/// <param name="height">The height of the memory image.</param>
334+
/// <param name="metadata">The <see cref="ImageMetadata"/>.</param>
335+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
336+
/// <exception cref="ArgumentNullException">The metadata is null.</exception>
337+
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
338+
public static unsafe Image<TPixel> WrapMemory<TPixel>(
339+
Configuration configuration,
340+
void* pointer,
341+
int width,
342+
int height,
343+
ImageMetadata metadata)
344+
where TPixel : unmanaged, IPixel<TPixel>
345+
{
346+
Guard.IsFalse(pointer == null, nameof(pointer), "Pointer must be not null");
347+
Guard.NotNull(configuration, nameof(configuration));
348+
Guard.NotNull(metadata, nameof(metadata));
349+
350+
var memoryManager = new UnmanagedMemoryManager<TPixel>(pointer, width * height);
351+
352+
var memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
353+
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
354+
}
355+
356+
/// <summary>
357+
/// <para>
358+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
359+
/// an <see cref="Image{TPixel}"/> instance.
360+
/// </para>
361+
/// <para>
362+
/// Please note: this method relies on callers to carefully manage the target memory area being referenced by the
363+
/// pointer and that the lifetime of such a memory area is at least equal to that of the returned
364+
/// <see cref="Image{TPixel}"/> instance. For example, if the input pointer references an unmanaged memory area,
365+
/// callers must ensure that the memory area is not freed as long as the returned <see cref="Image{TPixel}"/> is
366+
/// in use and not disposed. The same applies if the input memory area points to a pinned managed object, as callers
367+
/// must ensure that objects will remain pinned as long as the <see cref="Image{TPixel}"/> instance is in use.
368+
/// Failing to do so constitutes undefined behavior and will likely lead to memory corruption and runtime crashes.
369+
/// </para>
370+
/// <para>
371+
/// Note also that if you have a <see cref="Memory{T}"/> or an array (which can be cast to <see cref="Memory{T}"/>) of
372+
/// either <see cref="byte"/> or <typeparamref name="TPixel"/> values, it is highly recommended to use one of the other
373+
/// available overloads of this method instead (such as <see cref="WrapMemory{TPixel}(Configuration, Memory{byte}, int, int)"/>
374+
/// or <see cref="WrapMemory{TPixel}(Configuration, Memory{TPixel}, int, int)"/>, to make the resulting code less error
375+
/// prone and avoid having to pin the underlying memory buffer in use. This method is primarily meant to be used when
376+
/// doing interop or working with buffers that are located in unmanaged memory.
377+
/// </para>
378+
/// </summary>
379+
/// <typeparam name="TPixel">The pixel type</typeparam>
380+
/// <param name="configuration">The <see cref="Configuration"/></param>
381+
/// <param name="pointer">The pointer to the target memory buffer to wrap.</param>
382+
/// <param name="width">The width of the memory image.</param>
383+
/// <param name="height">The height of the memory image.</param>
384+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
385+
/// <returns>An <see cref="Image{TPixel}"/> instance.</returns>
386+
public static unsafe Image<TPixel> WrapMemory<TPixel>(
387+
Configuration configuration,
388+
void* pointer,
389+
int width,
390+
int height)
391+
where TPixel : unmanaged, IPixel<TPixel>
392+
=> WrapMemory<TPixel>(configuration, pointer, width, height, new ImageMetadata());
393+
394+
/// <summary>
395+
/// <para>
396+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels allowing viewing/manipulation as
397+
/// an <see cref="Image{TPixel}"/> instance.
398+
/// </para>
399+
/// <para>
400+
/// Please note: this method relies on callers to carefully manage the target memory area being referenced by the
401+
/// pointer and that the lifetime of such a memory area is at least equal to that of the returned
402+
/// <see cref="Image{TPixel}"/> instance. For example, if the input pointer references an unmanaged memory area,
403+
/// callers must ensure that the memory area is not freed as long as the returned <see cref="Image{TPixel}"/> is
404+
/// in use and not disposed. The same applies if the input memory area points to a pinned managed object, as callers
405+
/// must ensure that objects will remain pinned as long as the <see cref="Image{TPixel}"/> instance is in use.
406+
/// Failing to do so constitutes undefined behavior and will likely lead to memory corruption and runtime crashes.
407+
/// </para>
408+
/// <para>
409+
/// Note also that if you have a <see cref="Memory{T}"/> or an array (which can be cast to <see cref="Memory{T}"/>) of
410+
/// either <see cref="byte"/> or <typeparamref name="TPixel"/> values, it is highly recommended to use one of the other
411+
/// available overloads of this method instead (such as <see cref="WrapMemory{TPixel}(Configuration, Memory{byte}, int, int)"/>
412+
/// or <see cref="WrapMemory{TPixel}(Configuration, Memory{TPixel}, int, int)"/>, to make the resulting code less error
413+
/// prone and avoid having to pin the underlying memory buffer in use. This method is primarily meant to be used when
414+
/// doing interop or working with buffers that are located in unmanaged memory.
415+
/// </para>
416+
/// </summary>
417+
/// <typeparam name="TPixel">The pixel type.</typeparam>
418+
/// <param name="pointer">The pointer to the target memory buffer to wrap.</param>
419+
/// <param name="width">The width of the memory image.</param>
420+
/// <param name="height">The height of the memory image.</param>
421+
/// <returns>An <see cref="Image{TPixel}"/> instance.</returns>
422+
public static unsafe Image<TPixel> WrapMemory<TPixel>(
423+
void* pointer,
424+
int width,
425+
int height)
426+
where TPixel : unmanaged, IPixel<TPixel>
427+
=> WrapMemory<TPixel>(Configuration.Default, pointer, width, height);
223428
}
224429
}

0 commit comments

Comments
 (0)