Skip to content

Commit 58bf73e

Browse files
committed
Added ImportIndexedPixels to MagickImage (#2040)
1 parent a6a4428 commit 58bf73e

7 files changed

Lines changed: 326 additions & 1 deletion

File tree

src/Magick.NET.Core/IMagickImage{TQuantumType}.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,26 @@ public partial interface IMagickImage<TQuantumType> : IMagickImageCreateOperatio
297297
/// <exception cref="MagickException">Thrown when an error is raised by ImageMagick.</exception>
298298
IReadOnlyDictionary<IMagickColor<TQuantumType>, uint> Histogram();
299299

300+
/// <summary>
301+
/// Imports the specified indexed pixels into the current image.
302+
/// </summary>
303+
/// <param name="width">The width of the image.</param>
304+
/// <param name="height">The height of the image.</param>
305+
/// <param name="colors">The colors.</param>
306+
/// <param name="data">The index data.</param>
307+
/// <exception cref="MagickException">Thrown when an error is raised by ImageMagick.</exception>
308+
void ImportIndexedPixels(uint width, uint height, IReadOnlyList<IMagickColor<TQuantumType>> colors, byte[] data);
309+
310+
/// <summary>
311+
/// Imports the specified indexed pixels into the current image.
312+
/// </summary>
313+
/// <param name="width">The width of the image.</param>
314+
/// <param name="height">The height of the image.</param>
315+
/// <param name="colors">The colors.</param>
316+
/// <param name="data">The index data.</param>
317+
/// <exception cref="MagickException">Thrown when an error is raised by ImageMagick.</exception>
318+
void ImportIndexedPixels(uint width, uint height, IReadOnlyList<IMagickColor<TQuantumType>> colors, ushort[] data);
319+
300320
#if !Q8
301321
/// <summary>
302322
/// Import pixels from the specified quantum array into the current image.

src/Magick.NET/Colors/MagickColorCollection.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ namespace ImageMagick;
1818

1919
internal partial class MagickColorCollection
2020
{
21+
public MagickColorCollection(IReadOnlyList<IMagickColor<QuantumType>> colors)
22+
{
23+
Count = (uint)colors.Count;
24+
_nativeInstance = NativeMagickColorCollection.Create(Count);
25+
for (var i = 0; i < colors.Count; i++)
26+
{
27+
var color = colors[i];
28+
_nativeInstance.Set((nuint)i, color);
29+
}
30+
}
31+
32+
public uint Count { get; }
33+
2134
public static IReadOnlyDictionary<IMagickColor<QuantumType>, uint> ToDictionary(IntPtr list, uint length)
2235
{
2336
var colors = new Dictionary<IMagickColor<QuantumType>, uint>((int)length);
@@ -40,4 +53,10 @@ public static IReadOnlyDictionary<IMagickColor<QuantumType>, uint> ToDictionary(
4053

4154
return colors;
4255
}
56+
57+
public void Dispose()
58+
=> _nativeInstance.Dispose();
59+
60+
internal static IntPtr GetInstance(MagickColorCollection pointInfoCollection)
61+
=> pointInfoCollection._nativeInstance.Instance;
4362
}

src/Magick.NET/MagickImage.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3404,6 +3404,48 @@ public void Implode(double amount, PixelInterpolateMethod method)
34043404
mutator.Implode(amount, method);
34053405
}
34063406

3407+
/// <summary>
3408+
/// Imports the specified indexed pixels into the current image.
3409+
/// </summary>
3410+
/// <param name="width">The width of the image.</param>
3411+
/// <param name="height">The height of the image.</param>
3412+
/// <param name="colors">The colors.</param>
3413+
/// <param name="data">The index data.</param>
3414+
/// <exception cref="MagickException">Thrown when an error is raised by ImageMagick.</exception>
3415+
public void ImportIndexedPixels(uint width, uint height, IReadOnlyList<IMagickColor<QuantumType>> colors, byte[] data)
3416+
{
3417+
Throw.IfNullOrEmpty(colors);
3418+
Throw.IfNullOrEmpty(data);
3419+
3420+
var length = data.Length;
3421+
var expectedLength = width * height;
3422+
Throw.IfTrue(length < expectedLength, nameof(data), "The data length is {0} but should be at least {1}.", data.Length, expectedLength);
3423+
3424+
using var collection = new MagickColorCollection(colors);
3425+
_nativeInstance.ImportIndexedPixels(width, height, collection, data);
3426+
}
3427+
3428+
/// <summary>
3429+
/// Imports the specified indexed pixels into the current image.
3430+
/// </summary>
3431+
/// <param name="width">The width of the image.</param>
3432+
/// <param name="height">The height of the image.</param>
3433+
/// <param name="colors">The colors.</param>
3434+
/// <param name="data">The index data.</param>
3435+
/// <exception cref="MagickException">Thrown when an error is raised by ImageMagick.</exception>
3436+
public void ImportIndexedPixels(uint width, uint height, IReadOnlyList<IMagickColor<QuantumType>> colors, ushort[] data)
3437+
{
3438+
Throw.IfNullOrEmpty(colors);
3439+
Throw.IfNullOrEmpty(data);
3440+
3441+
var length = data.Length;
3442+
var expectedLength = width * height;
3443+
Throw.IfTrue(length < expectedLength, nameof(data), "The data length is {0} but should be at least {1}.", data.Length, expectedLength);
3444+
3445+
using var collection = new MagickColorCollection(colors);
3446+
_nativeInstance.ImportIndexedPixels(width, height, collection, data);
3447+
}
3448+
34073449
/// <summary>
34083450
/// Import pixels from the specified byte array into the current image.
34093451
/// </summary>
@@ -8076,6 +8118,22 @@ private void SetSettings(MagickSettings settings)
80768118

80778119
private unsafe sealed partial class NativeMagickImage : NativeInstance
80788120
{
8121+
public void ImportIndexedPixels(nuint width, nuint height, MagickColorCollection colors, byte[] data)
8122+
{
8123+
fixed (byte* dataFixed = data)
8124+
{
8125+
ImportIndexedPixels(width, height, colors, colors.Count, StorageType.Char, dataFixed);
8126+
}
8127+
}
8128+
8129+
public void ImportIndexedPixels(nuint width, nuint height, MagickColorCollection colors, ushort[] data)
8130+
{
8131+
fixed (ushort* dataFixed = data)
8132+
{
8133+
ImportIndexedPixels(width, height, colors, colors.Count, StorageType.Char, dataFixed);
8134+
}
8135+
}
8136+
80798137
public void ImportPixels(nint x, nint y, nuint width, nuint height, string map, StorageType storageType, byte[] data, nuint offsetInBytes)
80808138
{
80818139
fixed (byte* dataFixed = data)

src/Magick.NET/Native/Colors/MagickColorCollection.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,29 @@
44
using System;
55
using ImageMagick.SourceGenerator;
66

7+
#if Q8
8+
using QuantumType = System.Byte;
9+
#elif Q16
10+
using QuantumType = System.UInt16;
11+
#elif Q16HDRI
12+
using QuantumType = System.Single;
13+
#else
14+
#error Not implemented!
15+
#endif
16+
717
namespace ImageMagick;
818

9-
internal partial class MagickColorCollection
19+
internal partial class MagickColorCollection : IDisposable
1020
{
21+
private readonly NativeMagickColorCollection _nativeInstance;
22+
1123
[NativeInterop]
1224
private partial class NativeMagickColorCollection : NativeInstance
1325
{
26+
public static partial NativeMagickColorCollection Create(nuint length);
27+
1428
public partial IntPtr Get(nuint index);
29+
30+
public partial void Set(nuint index, IMagickColor<QuantumType> value);
1531
}
1632
}

src/Magick.NET/Native/MagickImage.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,9 @@ private unsafe sealed partial class NativeMagickImage : NativeInstance, INativeM
478478
[Throws]
479479
public partial IntPtr Implode(double amount, PixelInterpolateMethod method);
480480

481+
[Throws]
482+
public partial void ImportIndexedPixels(nuint width, nuint height, MagickColorCollection colors, nuint colorCount, StorageType storageType, void* data);
483+
481484
[Throws]
482485
public partial void ImportPixels(nint x, nint y, nuint width, nuint height, string map, StorageType storageType, void* data, nuint offsetInBytes);
483486

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright Dirk Lemstra https://github.com/dlemstra/Magick.NET.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using ImageMagick;
6+
using Xunit;
7+
8+
namespace Magick.NET.Tests;
9+
10+
public partial class MagickImageTests
11+
{
12+
public partial class TheImportIndexedPixelsMethod
13+
{
14+
public class WithByteArray
15+
{
16+
[Fact]
17+
public void ShouldThrowExceptionWhenColorsIsNull()
18+
{
19+
var data = new byte[] { 0 };
20+
using var image = new MagickImage();
21+
22+
Assert.Throws<ArgumentNullException>("colors", () => image.ImportIndexedPixels(1, 1, null!, data));
23+
}
24+
25+
[Fact]
26+
public void ShouldThrowExceptionWhenColorsIEmpty()
27+
{
28+
var colors = Array.Empty<MagickColor>();
29+
var data = new byte[] { 0 };
30+
using var image = new MagickImage();
31+
32+
var exception = Assert.Throws<ArgumentException>("colors", () => image.ImportIndexedPixels(1, 1, colors, data));
33+
ExceptionAssert.Contains("Value cannot be empty.", exception);
34+
}
35+
36+
[Fact]
37+
public void ShouldThrowExceptionWhenArrayIsNull()
38+
{
39+
var colors = new MagickColor[] { MagickColors.Red };
40+
using var image = new MagickImage();
41+
42+
Assert.Throws<ArgumentNullException>("data", () => image.ImportIndexedPixels(1, 1, colors, (byte[])null!));
43+
}
44+
45+
[Fact]
46+
public void ShouldThrowExceptionWhenArrayIsEmpty()
47+
{
48+
var colors = new MagickColor[] { MagickColors.Red };
49+
var data = Array.Empty<byte>();
50+
using var image = new MagickImage();
51+
52+
var exception = Assert.Throws<ArgumentException>("data", () => image.ImportIndexedPixels(1, 1, colors, data));
53+
ExceptionAssert.Contains("Value cannot be empty.", exception);
54+
}
55+
56+
[Fact]
57+
public void ShouldThrowExceptionWhenArrayIsToSmall()
58+
{
59+
var colors = new MagickColor[] { MagickColors.Red };
60+
var data = new byte[] { 0 };
61+
using var image = new MagickImage();
62+
63+
var exception = Assert.Throws<ArgumentException>("data", () => image.ImportIndexedPixels(1, 2, colors, data));
64+
ExceptionAssert.Contains("The data length is 1 but should be at least 2.", exception);
65+
}
66+
67+
[Fact]
68+
public void ShouldImportPixelsFromByteArray()
69+
{
70+
var data = new byte[]
71+
{
72+
2, 0,
73+
3, 1,
74+
};
75+
76+
var colors = new MagickColor[]
77+
{
78+
MagickColors.PaleGreen,
79+
MagickColors.RebeccaPurple,
80+
MagickColors.PaleVioletRed,
81+
MagickColors.Orchid,
82+
};
83+
84+
using var image = new MagickImage();
85+
image.ImportIndexedPixels(2, 2, colors, data);
86+
87+
Assert.Equal(ColorType.Palette, image.ColorType);
88+
Assert.Equal(ColorSpace.Gray, image.ColorSpace);
89+
90+
Assert.Equal(2U, image.Width);
91+
Assert.Equal(2U, image.Height);
92+
93+
using var pixels = image.GetPixelsUnsafe();
94+
var pixel = pixels.GetPixel(0, 0);
95+
Assert.Equal(2U, pixel.Channels);
96+
pixel.Equals(MagickColors.PaleVioletRed);
97+
98+
pixel = pixels.GetPixel(0, 1);
99+
Assert.Equal(2U, pixel.Channels);
100+
pixel.Equals(MagickColors.PaleGreen);
101+
102+
pixel = pixels.GetPixel(1, 0);
103+
Assert.Equal(2U, pixel.Channels);
104+
pixel.Equals(MagickColors.Orchid);
105+
106+
pixel = pixels.GetPixel(1, 1);
107+
Assert.Equal(2U, pixel.Channels);
108+
pixel.Equals(MagickColors.RebeccaPurple);
109+
}
110+
}
111+
112+
public class WithUshortArray
113+
{
114+
[Fact]
115+
public void ShouldThrowExceptionWhenColorsIsNull()
116+
{
117+
var data = new ushort[] { 0 };
118+
using var image = new MagickImage();
119+
120+
Assert.Throws<ArgumentNullException>("colors", () => image.ImportIndexedPixels(1, 1, null!, data));
121+
}
122+
123+
[Fact]
124+
public void ShouldThrowExceptionWhenColorsIEmpty()
125+
{
126+
var colors = Array.Empty<MagickColor>();
127+
var data = new ushort[] { 0 };
128+
using var image = new MagickImage();
129+
130+
var exception = Assert.Throws<ArgumentException>("colors", () => image.ImportIndexedPixels(1, 1, colors, data));
131+
ExceptionAssert.Contains("Value cannot be empty.", exception);
132+
}
133+
134+
[Fact]
135+
public void ShouldThrowExceptionWhenArrayIsNull()
136+
{
137+
var colors = new MagickColor[] { MagickColors.Red };
138+
using var image = new MagickImage();
139+
140+
Assert.Throws<ArgumentNullException>("data", () => image.ImportIndexedPixels(1, 1, colors, (ushort[])null!));
141+
}
142+
143+
[Fact]
144+
public void ShouldThrowExceptionWhenArrayIsEmpty()
145+
{
146+
var colors = new MagickColor[] { MagickColors.Red };
147+
var data = Array.Empty<ushort>();
148+
using var image = new MagickImage();
149+
150+
var exception = Assert.Throws<ArgumentException>("data", () => image.ImportIndexedPixels(1, 1, colors, data));
151+
ExceptionAssert.Contains("Value cannot be empty.", exception);
152+
}
153+
154+
[Fact]
155+
public void ShouldThrowExceptionWhenArrayIsToSmall()
156+
{
157+
var colors = new MagickColor[] { MagickColors.Red };
158+
var data = new ushort[] { 0 };
159+
using var image = new MagickImage();
160+
161+
var exception = Assert.Throws<ArgumentException>("data", () => image.ImportIndexedPixels(1, 2, colors, data));
162+
ExceptionAssert.Contains("The data length is 1 but should be at least 2.", exception);
163+
}
164+
165+
[Fact]
166+
public void ShouldImportPixelsFromByteArray()
167+
{
168+
var data = new ushort[]
169+
{
170+
1257, 57,
171+
11257, 257,
172+
};
173+
174+
var colors = new MagickColor[65535];
175+
colors[57] = MagickColors.PaleGreen;
176+
colors[257] = MagickColors.RebeccaPurple;
177+
colors[1257] = MagickColors.PaleVioletRed;
178+
colors[11257] = MagickColors.Orchid;
179+
180+
using var image = new MagickImage();
181+
image.ImportIndexedPixels(2, 2, colors, data);
182+
183+
Assert.Equal(ColorType.Palette, image.ColorType);
184+
Assert.Equal(ColorSpace.Gray, image.ColorSpace);
185+
186+
Assert.Equal(2U, image.Width);
187+
Assert.Equal(2U, image.Height);
188+
189+
using var pixels = image.GetPixelsUnsafe();
190+
var pixel = pixels.GetPixel(0, 0);
191+
Assert.Equal(2U, pixel.Channels);
192+
pixel.Equals(MagickColors.PaleVioletRed);
193+
194+
pixel = pixels.GetPixel(0, 1);
195+
Assert.Equal(2U, pixel.Channels);
196+
pixel.Equals(MagickColors.PaleGreen);
197+
198+
pixel = pixels.GetPixel(1, 0);
199+
Assert.Equal(2U, pixel.Channels);
200+
pixel.Equals(MagickColors.Orchid);
201+
202+
pixel = pixels.GetPixel(1, 1);
203+
Assert.Equal(2U, pixel.Channels);
204+
pixel.Equals(MagickColors.RebeccaPurple);
205+
}
206+
}
207+
}
208+
}

tools/Magick.NET.SourceGenerator/Helpers/TypeInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public bool HasGetInstance
5252
=> ClassName switch
5353
{
5454
"PointInfoCollection" => true,
55+
"MagickColorCollection" => true,
5556
"MagickImage" => true,
5657
_ => false,
5758
};

0 commit comments

Comments
 (0)