Skip to content

Commit

Permalink
Added GetReadOnlyArea to IPixelCollection (#1456)
Browse files Browse the repository at this point in the history
  • Loading branch information
dlemstra committed Oct 15, 2023
1 parent edba1d0 commit 113ae0b
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ namespace ImageMagick;
/// <content />
public partial interface IPixelCollection<TQuantumType>
{
/// <summary>
/// Returns the pixels at the specified area.
/// </summary>
/// <param name="x">The X coordinate of the area.</param>
/// <param name="y">The Y coordinate of the area.</param>
/// <param name="width">The width of the area.</param>
/// <param name="height">The height of the area.</param>
/// <returns>A <typeparamref name="TQuantumType"/> array.</returns>
ReadOnlySpan<TQuantumType> GetReadOnlyArea(int x, int y, int width, int height);

/// <summary>
/// Returns the pixels of the specified area.
/// </summary>
/// <param name="geometry">The geometry of the area.</param>
/// <returns>A <typeparamref name="TQuantumType"/> array.</returns>
ReadOnlySpan<TQuantumType> GetReadOnlyArea(IMagickGeometry geometry);

/// <summary>
/// Changes the values of the specified pixels.
/// </summary>
Expand Down
31 changes: 31 additions & 0 deletions src/Magick.NET/Native/Pixels/PixelCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public static class X64
[DllImport(NativeLibrary.X64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.X64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetReadOnlyArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.X64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern void PixelCollection_SetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, QuantumType* values, UIntPtr length, out IntPtr exception);
[DllImport(NativeLibrary.X64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_ToByteArray(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, IntPtr mapping, out IntPtr exception);
Expand All @@ -51,6 +53,8 @@ public static class ARM64
[DllImport(NativeLibrary.ARM64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.ARM64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetReadOnlyArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.ARM64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern void PixelCollection_SetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, QuantumType* values, UIntPtr length, out IntPtr exception);
[DllImport(NativeLibrary.ARM64Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_ToByteArray(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, IntPtr mapping, out IntPtr exception);
Expand All @@ -68,6 +72,8 @@ public static class X86
[DllImport(NativeLibrary.X86Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.X86Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_GetReadOnlyArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, out IntPtr exception);
[DllImport(NativeLibrary.X86Name, CallingConvention = CallingConvention.Cdecl)]
public static extern void PixelCollection_SetArea(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, QuantumType* values, UIntPtr length, out IntPtr exception);
[DllImport(NativeLibrary.X86Name, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr PixelCollection_ToByteArray(IntPtr Instance, IntPtr x, IntPtr y, UIntPtr width, UIntPtr height, IntPtr mapping, out IntPtr exception);
Expand Down Expand Up @@ -157,6 +163,31 @@ public IntPtr GetArea(int x, int y, int width, int height)
CheckException(exception);
return result;
}
public IntPtr GetReadOnlyArea(int x, int y, int width, int height)
{
IntPtr exception = IntPtr.Zero;
IntPtr result;
#if PLATFORM_AnyCPU
if (Runtime.IsArm64)
#endif
#if PLATFORM_arm64 || PLATFORM_AnyCPU
result = NativeMethods.ARM64.PixelCollection_GetReadOnlyArea(Instance, (IntPtr)x, (IntPtr)y, (UIntPtr)width, (UIntPtr)height, out exception);
#endif
#if PLATFORM_AnyCPU
else if (Runtime.Is64Bit)
#endif
#if PLATFORM_x64 || PLATFORM_AnyCPU
result = NativeMethods.X64.PixelCollection_GetReadOnlyArea(Instance, (IntPtr)x, (IntPtr)y, (UIntPtr)width, (UIntPtr)height, out exception);
#endif
#if PLATFORM_AnyCPU
else
#endif
#if PLATFORM_x86 || PLATFORM_AnyCPU
result = NativeMethods.X86.PixelCollection_GetReadOnlyArea(Instance, (IntPtr)x, (IntPtr)y, (UIntPtr)width, (UIntPtr)height, out exception);
#endif
CheckException(exception);
return result;
}
public void SetArea(int x, int y, int width, int height, QuantumType[] values, int length)
{
fixed (QuantumType* valuesFixed = values)
Expand Down
23 changes: 23 additions & 0 deletions src/Magick.NET/Native/Pixels/PixelCollection.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@
}
]
},
{
"name": "GetReadOnlyArea",
"type": "Instance",
"throws": "true",
"arguments": [
{
"name": "x",
"type": "ssize_t"
},
{
"name": "y",
"type": "ssize_t"
},
{
"name": "width",
"type": "size_t"
},
{
"name": "height",
"type": "size_t"
}
],
}
{
"name": "SetArea",
"throws": "true",
Expand Down
24 changes: 24 additions & 0 deletions src/Magick.NET/Netstandard21/Pixels/PixelCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ namespace ImageMagick;

internal abstract partial class PixelCollection
{
public virtual unsafe ReadOnlySpan<QuantumType> GetReadOnlyArea(int x, int y, int width, int height)
{
var area = GetAreaPointer(x, y, width, height);
if (area == IntPtr.Zero)
{
return default;
}

var length = width * height * Image.ChannelCount;

#if Q8
return new ReadOnlySpan<QuantumType>((byte*)area, length);
#elif Q16
return new ReadOnlySpan<QuantumType>((ushort*)area, length);
#elif Q16HDRI
return new ReadOnlySpan<QuantumType>((float*)area, length);
#else
#error Not implemented!
#endif
}

public virtual ReadOnlySpan<QuantumType> GetReadOnlyArea(IMagickGeometry geometry)
=> GetReadOnlyArea(geometry.X, geometry.Y, geometry.Width, geometry.Height);

public virtual void SetArea(int x, int y, int width, int height, ReadOnlySpan<QuantumType> values)
=> SetAreaUnchecked(x, y, width, height, values);

Expand Down
14 changes: 14 additions & 0 deletions src/Magick.NET/Netstandard21/Pixels/SafePixelCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ namespace ImageMagick;

internal sealed partial class SafePixelCollection
{
public override ReadOnlySpan<byte> GetReadOnlyArea(int x, int y, int width, int height)

Check failure on line 22 in src/Magick.NET/Netstandard21/Pixels/SafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'SafePixelCollection.GetReadOnlyArea(int, int, int, int)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(int, int, int, int)'

Check failure on line 22 in src/Magick.NET/Netstandard21/Pixels/SafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'SafePixelCollection.GetReadOnlyArea(int, int, int, int)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(int, int, int, int)'
{
CheckArea(x, y, width, height);

return base.GetReadOnlyArea(x, y, width, height);
}

public override ReadOnlySpan<byte> GetReadOnlyArea(IMagickGeometry geometry)

Check failure on line 29 in src/Magick.NET/Netstandard21/Pixels/SafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'SafePixelCollection.GetReadOnlyArea(IMagickGeometry)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(IMagickGeometry)'

Check failure on line 29 in src/Magick.NET/Netstandard21/Pixels/SafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'SafePixelCollection.GetReadOnlyArea(IMagickGeometry)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(IMagickGeometry)'
{
Throw.IfNull(nameof(geometry), geometry);

return base.GetReadOnlyArea(geometry);
}

public override void SetArea(int x, int y, int width, int height, ReadOnlySpan<QuantumType> values)
{
CheckValues(x, y, width, height, values);
Expand Down
8 changes: 8 additions & 0 deletions src/Magick.NET/Netstandard21/Pixels/UnsafePixelCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ namespace ImageMagick;

internal sealed partial class UnsafePixelCollection
{
public override ReadOnlySpan<byte> GetReadOnlyArea(IMagickGeometry geometry)

Check failure on line 22 in src/Magick.NET/Netstandard21/Pixels/UnsafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'UnsafePixelCollection.GetReadOnlyArea(IMagickGeometry)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(IMagickGeometry)'

Check failure on line 22 in src/Magick.NET/Netstandard21/Pixels/UnsafePixelCollection.cs

View workflow job for this annotation

GitHub Actions / MacOS (Q8/Q16/Q16-HDRI)

'UnsafePixelCollection.GetReadOnlyArea(IMagickGeometry)': return type must be 'ReadOnlySpan<ushort>' to match overridden member 'PixelCollection.GetReadOnlyArea(IMagickGeometry)'
{
if (geometry is null)
return default;

return base.GetReadOnlyArea(geometry);
}

public override void SetArea(IMagickGeometry geometry, ReadOnlySpan<QuantumType> values)
{
if (geometry is not null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Dirk Lemstra https://github.com/dlemstra/Magick.NET.
// Licensed under the Apache License, Version 2.0.

#if NETCOREAPP

using System;
using ImageMagick;
using Xunit;

namespace Magick.NET.Tests
{
public partial class SafePixelCollectionTests
{
public partial class TheGetReadonlyAreaMethod
{
[Fact]
public void ShouldThrowExceptionWhenAreaIsInvalid()
{
using var image = new MagickImage(MagickColors.Red, 1, 1);
using var pixels = image.GetPixels();

Assert.Throws<ArgumentOutOfRangeException>(() => pixels.GetReadOnlyArea(1, 2, 3, 4));
}

[Fact]
public void ShouldNotThrowExceptionWhenGeometryIsNull()
{
using var image = new MagickImage(Files.ImageMagickJPG);
using var pixels = image.GetPixels();

Assert.Throws<ArgumentNullException>("geometry", () => pixels.GetReadOnlyArea(null));
}

[Fact]
public void ShouldReturnAllPixelsOfTheImage()
{
using var input = new MagickImage(Files.ImageMagickJPG);
using var pixels = input.GetPixels();
var area = pixels.GetReadOnlyArea(0, 0, input.Width, input.Height);

Assert.Equal(43542, area.Length);

var settings = new PixelReadSettings(input.Width, input.Height, StorageType.Quantum, PixelMapping.RGB);
using var output = new MagickImage();
output.ReadPixels(area, settings);

var difference = output.Compare(input, ErrorMetric.RootMeanSquared);
Assert.Equal(0.0, difference);
}
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Dirk Lemstra https://github.com/dlemstra/Magick.NET.
// Licensed under the Apache License, Version 2.0.

#if NETCOREAPP

using ImageMagick;
using Xunit;

namespace Magick.NET.Tests
{
public partial class UnsafePixelCollectionTests
{
public partial class TheGetReadonlyAreaMethod
{
[Fact]
public void ShouldNotThrowExceptionWhenAreaIsInvalid()
{
using var image = new MagickImage(MagickColors.Red, 1, 1);
using var pixels = image.GetPixelsUnsafe();
var area = pixels.GetReadOnlyArea(1, 2, 3, 4);

Assert.Equal(36, area.Length);
}

[Fact]
public void ShouldNotThrowExceptionWhenGeometryIsNull()
{
using var image = new MagickImage(Files.ImageMagickJPG);
using var pixels = image.GetPixelsUnsafe();
var area = pixels.GetReadOnlyArea(null);

Assert.Equal(0, area.Length);
}

[Fact]
public void ShouldReturnAllPixelsOfTheImage()
{
using var input = new MagickImage(Files.ImageMagickJPG);
using var pixels = input.GetPixelsUnsafe();
var area = pixels.GetReadOnlyArea(0, 0, input.Width, input.Height);

Assert.Equal(43542, area.Length);

var settings = new PixelReadSettings(input.Width, input.Height, StorageType.Quantum, PixelMapping.RGB);
using var output = new MagickImage();
output.ReadPixels(area, settings);

var difference = output.Compare(input, ErrorMetric.RootMeanSquared);
Assert.Equal(0.0, difference);
}
}
}
}

#endif

0 comments on commit 113ae0b

Please sign in to comment.