Correctly implemented image.ConverTo and added tests

This commit is contained in:
Darth Affe 2024-07-14 21:35:32 +02:00
parent 19c9143fa1
commit dd5f742b48
4 changed files with 75 additions and 7 deletions

View File

@ -158,5 +158,51 @@ public class ImageTest
Assert.AreEqual(image[x, y], refImage[x, y]); Assert.AreEqual(image[x, y], refImage[x, y]);
} }
[TestMethod]
public void ConvertToInPlace()
{
IImage image = TestDataHelper.CreateTestImage<ColorARGB>(TEST_WIDTH, TEST_HEIGHT);
IColor[] referenceData = image.ToArray();
image.ConvertTo<ColorBGRA>();
IColor[] testData = image.ToArray();
Assert.AreEqual(referenceData.Length, testData.Length);
for (int i = 0; i < referenceData.Length; i++)
{
IColor reference = referenceData[i];
IColor test = testData[i];
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
}
}
[TestMethod]
public void ConvertToCopy()
{
IImage image = TestDataHelper.CreateTestImage<ColorARGB>(TEST_WIDTH, TEST_HEIGHT);
IColor[] referenceData = image.ToArray();
image.ConvertTo<ColorBGR>();
IColor[] testData = image.ToArray();
Assert.AreEqual(referenceData.Length, testData.Length);
for (int i = 0; i < referenceData.Length; i++)
{
IColor reference = referenceData[i];
IColor test = testData[i];
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
}
}
#endregion #endregion
} }

View File

@ -51,7 +51,6 @@ public sealed class Image<T> : IImage<T>
} }
} }
IImage IImage.this[int x, int y, int width, int height] IImage IImage.this[int x, int y, int width, int height]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -123,11 +122,23 @@ public sealed class Image<T> : IImage<T>
return new Image<T>(data, 0, 0, width, height, stride); return new Image<T>(data, 0, 0, width, height, stride);
} }
public void ConvertTo<TColor>() public IImage<TColor> ConvertTo<TColor>()
where TColor : struct, IColor where TColor : struct, IColor
{ {
for (int i = 0; i < Height; i++) int targetBpp = TColor.ColorFormat.BytesPerPixel;
MemoryMarshal.Cast<byte, T>(_buffer.AsSpan().Slice(((i + _y) * _stride) + _x, Width)).ConvertInPlace<T, TColor>(); if (targetBpp == ColorFormat.BytesPerPixel)
{
byte[] data = ToRawArray();
MemoryMarshal.Cast<byte, T>(data.AsSpan()).ConvertInPlace<T, TColor>();
return new Image<TColor>(data, 0, 0, Width, Height, Width * targetBpp);
}
else
{
byte[] data = ToRawArray();
byte[] target = new byte[Width * Height * targetBpp];
MemoryMarshal.Cast<byte, T>(data.AsSpan()).Convert(MemoryMarshal.Cast<byte, TColor>(target));
return new Image<TColor>(target, 0, 0, Width, Height, Width * targetBpp);
}
} }
public void CopyTo(Span<T> destination) => CopyTo(MemoryMarshal.AsBytes(destination)); public void CopyTo(Span<T> destination) => CopyTo(MemoryMarshal.AsBytes(destination));

View File

@ -60,8 +60,8 @@ public interface IImage : IEnumerable<IColor>
/// <returns>The <inheritdoc cref="RefImage{TColor}"/>.</returns> /// <returns>The <inheritdoc cref="RefImage{TColor}"/>.</returns>
RefImage<TColor> AsRefImage<TColor>() where TColor : struct, IColor; RefImage<TColor> AsRefImage<TColor>() where TColor : struct, IColor;
void ConvertTo<T>() where T : struct, IColor; IImage<TColor> ConvertTo<TColor>() where TColor : struct, IColor;
/// <summary> /// <summary>
/// Copies the contents of this <see cref="IImage"/> into a destination <see cref="Span{T}"/> instance. /// Copies the contents of this <see cref="IImage"/> into a destination <see cref="Span{T}"/> instance.
/// </summary> /// </summary>

View File

@ -33,7 +33,7 @@ public static unsafe partial class PixelHelper
if (colors == null) throw new ArgumentNullException(nameof(colors)); if (colors == null) throw new ArgumentNullException(nameof(colors));
TTarget[] buffer = new TTarget[colors.Length]; TTarget[] buffer = new TTarget[colors.Length];
Convert<TSource, TTarget>(colors, buffer.AsSpan()); Convert(colors, buffer.AsSpan());
return buffer; return buffer;
} }
@ -48,6 +48,17 @@ public static unsafe partial class PixelHelper
return buffer; return buffer;
} }
public static void Convert<TSource, TTarget>(this Span<TSource> source, Span<TTarget> target)
where TSource : struct, IColor
where TTarget : struct, IColor
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (target == null) throw new ArgumentNullException(nameof(target));
if (target.Length < source.Length) throw new ArgumentException($"Target-buffer is not big enough. {target.Length} < {source.Length}", nameof(target));
Convert(MemoryMarshal.AsBytes(source), MemoryMarshal.AsBytes(target), TSource.ColorFormat, TTarget.ColorFormat);
}
public static void Convert<TSource, TTarget>(this ReadOnlySpan<TSource> source, Span<TTarget> target) public static void Convert<TSource, TTarget>(this ReadOnlySpan<TSource> source, Span<TTarget> target)
where TSource : struct, IColor where TSource : struct, IColor
where TTarget : struct, IColor where TTarget : struct, IColor