Browse Source

Merge remote-tracking branch 'upstream/master' into feature/pngExif

# Conflicts:
#	src/ImageSharp/Formats/Png/PngDecoderCore.cs
af/merge-core
popow 8 years ago
parent
commit
2e7f91bdd3
  1. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  2. 14
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  3. 22
      src/ImageSharp/Formats/Png/PngBitDepth.cs
  4. 8
      src/ImageSharp/Formats/Png/PngConfigurationModule.cs
  5. 731
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  6. 14
      src/ImageSharp/Formats/Png/PngEncoder.cs
  7. 196
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  8. 21
      src/ImageSharp/PixelFormats/Alpha8.cs
  9. 35
      src/ImageSharp/PixelFormats/Argb32.cs
  10. 41
      src/ImageSharp/PixelFormats/Bgr24.cs
  11. 22
      src/ImageSharp/PixelFormats/Bgr565.cs
  12. 54
      src/ImageSharp/PixelFormats/Bgra32.cs
  13. 16
      src/ImageSharp/PixelFormats/Bgra4444.cs
  14. 24
      src/ImageSharp/PixelFormats/Bgra5551.cs
  15. 18
      src/ImageSharp/PixelFormats/Byte4.cs
  16. 142
      src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs
  17. 90
      src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt
  18. 16
      src/ImageSharp/PixelFormats/HalfSingle.cs
  19. 16
      src/ImageSharp/PixelFormats/HalfVector2.cs
  20. 16
      src/ImageSharp/PixelFormats/HalfVector4.cs
  21. 24
      src/ImageSharp/PixelFormats/IPixel.cs
  22. 16
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  23. 16
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  24. 16
      src/ImageSharp/PixelFormats/NormalizedShort2.cs
  25. 16
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  26. 16
      src/ImageSharp/PixelFormats/Rg32.cs
  27. 48
      src/ImageSharp/PixelFormats/Rgb24.cs
  28. 256
      src/ImageSharp/PixelFormats/Rgb48.cs
  29. 16
      src/ImageSharp/PixelFormats/Rgba1010102.cs
  30. 22
      src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
  31. 46
      src/ImageSharp/PixelFormats/Rgba32.cs
  32. 187
      src/ImageSharp/PixelFormats/Rgba64.cs
  33. 16
      src/ImageSharp/PixelFormats/RgbaVector.cs
  34. 16
      src/ImageSharp/PixelFormats/Short2.cs
  35. 19
      src/ImageSharp/PixelFormats/Short4.cs
  36. 1
      tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
  37. 2
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs
  38. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  39. 173
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  40. 4
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  41. 3
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  42. 49
      tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs
  43. 32
      tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs
  44. 36
      tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs
  45. 34
      tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs
  46. 34
      tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs
  47. 32
      tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs
  48. 32
      tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs
  49. 32
      tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs
  50. 32
      tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs
  51. 32
      tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs
  52. 32
      tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs
  53. 32
      tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs
  54. 32
      tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs
  55. 32
      tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs
  56. 32
      tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs
  57. 137
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs
  58. 32
      tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs
  59. 34
      tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs
  60. 199
      tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs
  61. 32
      tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs
  62. 32
      tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs
  63. 56
      tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs
  64. 34
      tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs
  65. 32
      tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs
  66. 32
      tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs
  67. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs
  68. 2
      tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs
  69. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  70. 2
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
  71. 5
      tests/ImageSharp.Tests/TestFile.cs
  72. 11
      tests/ImageSharp.Tests/TestImages.cs
  73. 14
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs
  74. 2
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs
  75. 19
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs
  76. 4
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs
  77. 10
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs
  78. 46
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs
  79. 20
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
  80. 19
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
  81. 2
      tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs
  82. 44
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs
  83. 5
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs
  84. 2
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs
  85. 9
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  86. 2
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
  87. 6
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  88. 18
      tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs
  89. 30
      tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs
  90. 4
      tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs
  91. 2
      tests/Images/External
  92. 3
      tests/Images/Input/Png/gray-16-tRNS-interlaced.png
  93. 3
      tests/Images/Input/Png/gray-16.png
  94. 3
      tests/Images/Input/Png/gray-alpha-16.png
  95. 3
      tests/Images/Input/Png/gray-alpha-8.png
  96. 3
      tests/Images/Input/Png/rgb-16-alpha.png
  97. 3
      tests/Images/Input/Png/rgb-16-tRNS.png
  98. 3
      tests/Images/Input/Png/rgb-8-tRNS.png

2
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
return Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
}
/// <summary>

14
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -11,14 +11,20 @@ namespace SixLabors.ImageSharp.Formats.Png
internal interface IPngEncoderOptions
{
/// <summary>
/// Gets the png color type
/// Gets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType"/> values.
/// </summary>
PngColorType PngColorType { get; }
PngBitDepth BitDepth { get; }
/// <summary>
/// Gets the png filter method.
/// Gets the color type
/// </summary>
PngFilterMethod PngFilterMethod { get; }
PngColorType ColorType { get; }
/// <summary>
/// Gets the filter method.
/// </summary>
PngFilterMethod FilterMethod { get; }
/// <summary>
/// Gets the compression level 1-9.

22
src/ImageSharp/Formats/Png/PngBitDepth.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Note the value assignment, This will allow us to add 1, 2, and 4 bit encoding when we support it.
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Provides enumeration for the available PNG bit depths.
/// </summary>
public enum PngBitDepth
{
/// <summary>
/// 8 bits per sample or per palette index (not per pixel).
/// </summary>
Bit8 = 8,
/// <summary>
/// 16 bits per sample or per palette index (not per pixel).
/// </summary>
Bit16 = 16
}
}

8
src/ImageSharp/Formats/Png/PngConfigurationModule.cs

@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Png
public sealed class PngConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration config)
public void Configure(Configuration configuration)
{
config.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder());
config.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder());
config.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
configuration.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder());
configuration.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector());
}
}
}

731
src/ImageSharp/Formats/Png/PngDecoderCore.cs

File diff suppressed because it is too large

14
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -14,14 +14,20 @@ namespace SixLabors.ImageSharp.Formats.Png
public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{
/// <summary>
/// Gets or sets the png color type.
/// Gets or sets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="ColorType"/> values.
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
public PngBitDepth BitDepth { get; set; } = PngBitDepth.Bit8;
/// <summary>
/// Gets or sets the png filter method.
/// Gets or sets the color type.
/// </summary>
public PngFilterMethod PngFilterMethod { get; set; } = PngFilterMethod.Adaptive;
public PngColorType ColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the filter method.
/// </summary>
public PngFilterMethod FilterMethod { get; set; } = PngFilterMethod.Paeth;
/// <summary>
/// Gets or sets the compression level 1-9.

196
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -41,6 +41,16 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The png bit depth
/// </summary>
private readonly PngBitDepth pngBitDepth;
/// <summary>
/// Gets or sets a value indicating whether to use 16 bit encoding for supported color types.
/// </summary>
private readonly bool use16Bit;
/// <summary>
/// The png color type.
/// </summary>
@ -149,8 +159,10 @@ namespace SixLabors.ImageSharp.Formats.Png
public PngEncoderCore(MemoryAllocator memoryAllocator, IPngEncoderOptions options)
{
this.memoryAllocator = memoryAllocator;
this.pngColorType = options.PngColorType;
this.pngFilterMethod = options.PngFilterMethod;
this.pngBitDepth = options.BitDepth;
this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16);
this.pngColorType = options.ColorType;
this.pngFilterMethod = options.FilterMethod;
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
this.quantizer = options.Quantizer;
@ -197,7 +209,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
this.bitDepth = 8;
this.bitDepth = (byte)(this.use16Bit ? 16 : 8);
}
this.bytesPerPixel = this.CalculateBytesPerPixel();
@ -205,11 +217,11 @@ namespace SixLabors.ImageSharp.Formats.Png
var header = new PngHeader(
width: image.Width,
height: image.Height,
colorType: this.pngColorType,
bitDepth: this.bitDepth,
filterMethod: 0, // None
compressionMethod: 0,
interlaceMethod: 0);
colorType: this.pngColorType,
compressionMethod: 0, // None
filterMethod: 0,
interlaceMethod: 0); // TODO: Can't write interlaced yet.
this.WriteHeaderChunk(stream, header);
@ -247,28 +259,62 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
Rgba32 rgba = default;
// Use ITU-R recommendation 709 to match libpng.
const float RX = .2126F;
const float GX = .7152F;
const float BX = .0722F;
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
// Copy the pixels across from the image.
// Reuse the chunk type buffer.
for (int x = 0; x < this.width; x++)
if (this.pngColorType.Equals(PngColorType.Grayscale))
{
// Convert the color to YCbCr and store the luminance
// Optionally store the original color alpha.
int offset = x * this.bytesPerPixel;
rowSpan[x].ToRgba32(ref rgba);
byte luminance = (byte)((0.299F * rgba.R) + (0.587F * rgba.G) + (0.114F * rgba.B));
for (int i = 0; i < this.bytesPerPixel; i++)
// TODO: Realistically we should support 1, 2, 4, 8, and 16 bit grayscale images.
// we currently do the other types via palette. Maybe RC as I don't understand how the data is packed yet
// for 1, 2, and 4 bit grayscale images.
if (this.use16Bit)
{
if (i == 0)
// 16 bit grayscale
Rgb48 rgb = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
rawScanlineArray[offset] = luminance;
rowSpan[x].ToRgb48(ref rgb);
ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
}
else
}
else
{
// 8 bit grayscale
Rgb24 rgb = default;
for (int x = 0; x < rowSpan.Length; x++)
{
rawScanlineArray[offset + i] = rgba.A;
rowSpan[x].ToRgb24(ref rgb);
rawScanlineSpan[x] = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B));
}
}
}
else
{
if (this.use16Bit)
{
// 16 bit grayscale + alpha
Rgba64 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 4)
{
rowSpan[x].ToRgba64(ref rgba);
ushort luminance = (ushort)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B));
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A);
}
}
else
{
// 8 bit grayscale + alpha
Rgba32 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2)
{
rowSpan[x].ToRgba32(ref rgba);
rawScanlineSpan[o] = (byte)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B));
rawScanlineSpan[o + 1] = rgba.A;
}
}
}
@ -282,13 +328,54 @@ namespace SixLabors.ImageSharp.Formats.Png
private void CollectTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
if (this.bytesPerPixel == 4)
{
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, this.rawScanline.GetSpan(), this.width);
}
else
Span<byte> rawScanlineSpan = this.rawScanline.GetSpan();
switch (this.bytesPerPixel)
{
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, this.rawScanline.GetSpan(), this.width);
case 4:
{
// 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width);
break;
}
case 3:
{
// 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width);
break;
}
case 8:
{
// 16 bit Rgba
Rgba64 rgba = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8)
{
rowSpan[x].ToRgba64(ref rgba);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A);
}
break;
}
default:
{
// 16 bit Rgb
Rgb48 rgb = default;
for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6)
{
rowSpan[x].ToRgb48(ref rgb);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B);
}
break;
}
}
}
@ -306,9 +393,10 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Palette:
int stride = this.rawScanline.Length();
int stride = this.rawScanline.Length();
this.palettePixelData.AsSpan(row * stride, stride).CopyTo(this.rawScanline.GetSpan());
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
@ -367,6 +455,9 @@ namespace SixLabors.ImageSharp.Formats.Png
// early on which shaves a couple of milliseconds off the processing time.
UpFilter.Encode(scanSpan, prevSpan, this.up.GetSpan(), out int currentSum);
// TODO: PERF.. We should be breaking out of the encoding for each line as soon as we hit the sum.
// That way the above comment would actually be true. It used to be anyway...
// If we could use SIMD for none branching filters we could really speed it up.
int lowestSum = currentSum;
IManagedByteBuffer actualResult = this.up;
@ -405,22 +496,20 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
return this.use16Bit ? 2 : 1;
case PngColorType.GrayscaleWithAlpha:
return 2;
return this.use16Bit ? 4 : 2;
case PngColorType.Palette:
return 1;
case PngColorType.Rgb:
return 3;
return this.use16Bit ? 6 : 3;
// PngColorType.RgbWithAlpha
// TODO: Maybe figure out a way to detect if there are any transparent
// pixels and encode RGB if none.
default:
return 4;
return this.use16Bit ? 8 : 4;
}
}
@ -573,12 +662,37 @@ namespace SixLabors.ImageSharp.Formats.Png
this.rawScanline = this.memoryAllocator.AllocateCleanManagedByteBuffer(this.bytesPerScanline);
this.result = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
if (this.pngColorType != PngColorType.Palette)
switch (this.pngFilterMethod)
{
this.sub = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.up = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.average = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.paeth = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
case PngFilterMethod.None:
break;
case PngFilterMethod.Sub:
this.sub = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Up:
this.up = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Average:
this.average = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Paeth:
this.paeth = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
case PngFilterMethod.Adaptive:
this.sub = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.up = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.average = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
this.paeth = this.memoryAllocator.AllocateCleanManagedByteBuffer(resultLength);
break;
}
byte[] buffer;

21
src/ImageSharp/PixelFormats/Alpha8.cs

@ -155,6 +155,27 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = this.PackedValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackedValue = byte.MaxValue;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest)
{
dest.R = 0;
dest.G = 0;
dest.B = 0;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <summary>
/// Compares an object with the packed vector.
/// </summary>

35
src/ImageSharp/PixelFormats/Argb32.cs

@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.PixelFormats
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
this.A = byte.MaxValue;
}
/// <summary>
@ -310,6 +310,34 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Argb32 ToArgb32() => this;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = byte.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = (byte)(((source.A * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
public override bool Equals(object obj)
{
@ -336,8 +364,9 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return this.Argb.GetHashCode();
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary>

41
src/ImageSharp/PixelFormats/Bgr24.cs

@ -69,13 +69,8 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
unchecked
{
int hashCode = this.B;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.R;
return hashCode;
}
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
/// <inheritdoc/>
@ -149,7 +144,7 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
@ -159,7 +154,7 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
@ -174,7 +169,33 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
}
}

22
src/ImageSharp/PixelFormats/Bgr565.cs

@ -187,6 +187,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{
@ -223,9 +239,9 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ushort Pack(float x, float y, float z)
{
return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11) |
(((int)Math.Round(y.Clamp(0, 1) * 63F) & 0x3F) << 5) |
((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F));
return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11)
| (((int)Math.Round(y.Clamp(0, 1) * 63F) & 0x3F) << 5)
| ((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F));
}
}
}

54
src/ImageSharp/PixelFormats/Bgra32.cs

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
this.A = byte.MaxValue;
}
/// <summary>
@ -113,14 +113,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = this.B;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.R;
hashCode = (hashCode * 397) ^ this.A;
return hashCode;
}
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary>
@ -219,17 +214,11 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
dest = Unsafe.As<Bgra32, Bgr24>(ref this);
}
public void ToBgr24(ref Bgr24 dest) => dest = Unsafe.As<Bgra32, Bgr24>(ref this);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
dest = this;
}
public void ToBgra32(ref Bgra32 dest) => dest = this;
/// <summary>
/// Converts the pixel to <see cref="Rgba32"/> format.
@ -252,12 +241,41 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bgra32 ToBgra32() => this;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = byte.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = (byte)(((source.A * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <summary>
/// Packs a <see cref="Vector4"/> into a color.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Pack(ref Vector4 vector) {
private void Pack(ref Vector4 vector)
{
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);

16
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -178,6 +178,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

24
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -178,6 +178,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{
@ -222,10 +238,10 @@ namespace SixLabors.ImageSharp.PixelFormats
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)(
(((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 10) |
(((int)Math.Round(y.Clamp(0, 1) * 31F) & 0x1F) << 5) |
(((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) |
(((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15));
(((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 10)
| (((int)Math.Round(y.Clamp(0, 1) * 31F) & 0x1F) << 5)
| (((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0)
| (((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

18
src/ImageSharp/PixelFormats/Byte4.cs

@ -179,10 +179,26 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Byte4) && this.Equals((Byte4)obj);
return obj is Byte4 byte4 && this.Equals(byte4);
}
/// <inheritdoc />

142
src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs

@ -11,6 +11,148 @@ namespace SixLabors.ImageSharp.PixelFormats
public partial class PixelOperations<TPixel>
{
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgba64"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgba64"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgba64(ReadOnlySpan<Rgba64> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
var rgba = new Rgba64(0, 0, 0, 65535);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba64(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFromRgba64(ReadOnlySpan{Rgba64}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgba64"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgba64Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgba64(MemoryMarshal.Cast<byte, Rgba64>(sourceBytes), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgba64"/>-s.
/// Bulk version of <see cref="IPixel.ToRgba64(ref Rgba64)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Rgba64"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToRgba64(ReadOnlySpan<TPixel> sourcePixels, Span<Rgba64> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels);
ref Rgba64 destBaseRef = ref MemoryMarshal.GetReference(dest);
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Rgba64 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToRgba64(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToRgba64(ReadOnlySpan{TPixel}, Span{Rgba64}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgba64"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgba64Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgba64(sourceColors, MemoryMarshal.Cast<byte, Rgba64>(destBytes), count);
}
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgb48"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgb48"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgb48(ReadOnlySpan<Rgb48> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
var rgb = default(Rgb48);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgb = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgb48(rgb);
}
}
/// <summary>
/// A helper for <see cref="PackFromRgb48(ReadOnlySpan{Rgb48}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgb48"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgb48Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgb48(MemoryMarshal.Cast<byte, Rgb48>(sourceBytes), destPixels, count);
}
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgb48"/>-s.
/// Bulk version of <see cref="IPixel.ToRgb48(ref Rgb48)"/>.
/// </summary>
/// <param name="sourcePixels">The span of source pixels</param>
/// <param name="dest">The destination span of <see cref="Rgb48"/> data.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToRgb48(ReadOnlySpan<TPixel> sourcePixels, Span<Rgb48> dest, int count)
{
GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count);
ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels);
ref Rgb48 destBaseRef = ref MemoryMarshal.GetReference(dest);
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i);
ref Rgb48 dp = ref Unsafe.Add(ref destBaseRef, i);
sp.ToRgb48(ref dp);
}
}
/// <summary>
/// A helper for <see cref="ToRgb48(ReadOnlySpan{TPixel}, Span{Rgb48}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgb48"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgb48Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgb48(sourceColors, MemoryMarshal.Cast<byte, Rgb48>(destBytes), count);
}
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgba32"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>

90
src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt

@ -51,6 +51,90 @@
<#
}
void GeneratePackFromMethodUsingPackFromRgba64(string pixelType, string rgbaOperationCode)
{
#>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="<#=pixelType#>"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="<#=pixelType#>"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFrom<#=pixelType#>(ReadOnlySpan<<#=pixelType#>> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
var rgba = new Rgba64(0, 0, 0, 65535);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
<#=rgbaOperationCode#>
dp.PackFromRgba64(rgba);
}
}
/// <summary>
/// A helper for <see cref="PackFrom<#=pixelType#>(ReadOnlySpan{<#=pixelType#>}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="<#=pixelType#>"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFrom<#=pixelType#>Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFrom<#=pixelType#>(MemoryMarshal.Cast<byte, <#=pixelType#>>(sourceBytes), destPixels, count);
}
<#
}
void GeneratePackFromMethodUsingPackFromRgb48(string pixelType, string rgbaOperationCode)
{
#>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="<#=pixelType#>"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="<#=pixelType#>"/> data.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFrom<#=pixelType#>(ReadOnlySpan<<#=pixelType#>> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
var rgb = default(Rgb48);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
<#=rgbaOperationCode#>
dp.PackFromRgb48(rgb);
}
}
/// <summary>
/// A helper for <see cref="PackFrom<#=pixelType#>(ReadOnlySpan{<#=pixelType#>}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="<#=pixelType#>"/> layout.
/// </summary>
/// <param name="sourceBytes">The <see cref="ReadOnlySpan{T}"/> to the source bytes.</param>
/// <param name="destPixels">The <see cref="Span{T}"/> to the destination pixels.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFrom<#=pixelType#>Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFrom<#=pixelType#>(MemoryMarshal.Cast<byte, <#=pixelType#>>(sourceBytes), destPixels, count);
}
<#
}
void GeneratePackFromMethodUsingPackFromRgba32(string pixelType, string rgbaOperationCode)
{
#>
@ -192,6 +276,12 @@ namespace SixLabors.ImageSharp.PixelFormats
{
<#
GeneratePackFromMethodUsingPackFromRgba64("Rgba64", "rgba = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Rgba64");
GeneratePackFromMethodUsingPackFromRgb48("Rgb48", "rgb = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Rgb48");
GeneratePackFromMethodUsingPackFromRgba32("Rgba32", "rgba = Unsafe.Add(ref sourceRef, i);");
GenerateToDestFormatMethods("Rgba32");

16
src/ImageSharp/PixelFormats/HalfSingle.cs

@ -192,6 +192,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

16
src/ImageSharp/PixelFormats/HalfVector2.cs

@ -207,6 +207,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = 255;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override string ToString()
{

16
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -200,6 +200,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override string ToString()
{

24
src/ImageSharp/PixelFormats/IPixel.cs

@ -61,6 +61,18 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="source">The <see cref="Rgba32"/> value.</param>
void PackFromRgba32(Rgba32 source);
/// <summary>
/// Packs the pixel from an <see cref="Rgb48"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgb48"/> value.</param>
void PackFromRgb48(Rgb48 source);
/// <summary>
/// Packs the pixel from an <see cref="Rgba64"/> value.
/// </summary>
/// <param name="source">The <see cref="Rgba64"/> value.</param>
void PackFromRgba64(Rgba64 source);
/// <summary>
/// Packs the pixel from an <see cref="Argb32"/> value.
/// </summary>
@ -85,6 +97,18 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="dest">The destination pixel to write to</param>
void ToRgba32(ref Rgba32 dest);
/// <summary>
/// Converts the pixel to <see cref="Rgb48"/> format.
/// </summary>
/// <param name="dest">The destination pixel to write to</param>
void ToRgb48(ref Rgb48 dest);
/// <summary>
/// Converts the pixel to <see cref="Rgba64"/> format.
/// </summary>
/// <param name="dest">The destination pixel to write to</param>
void ToRgba64(ref Rgba64 dest);
/// <summary>
/// Converts the pixel to <see cref="Argb32"/> format.
/// </summary>

16
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -226,6 +226,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = 255;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

16
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -219,6 +219,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

16
src/ImageSharp/PixelFormats/NormalizedShort2.cs

@ -213,6 +213,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = 255;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.

16
src/ImageSharp/PixelFormats/NormalizedShort4.cs

@ -221,6 +221,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

16
src/ImageSharp/PixelFormats/Rg32.cs

@ -191,6 +191,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)vector.W;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

48
src/ImageSharp/PixelFormats/Rgb24.cs

@ -70,13 +70,8 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
unchecked
{
int hashCode = this.R;
hashCode = (hashCode * 397) ^ this.G;
hashCode = (hashCode * 397) ^ this.B;
return hashCode;
}
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
/// <inheritdoc/>
@ -131,21 +126,18 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Rgba32(this.R, this.G, this.B, 255).ToVector4();
return new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest = this;
}
public void ToRgb24(ref Rgb24 dest) => dest = this;
/// <inheritdoc/>
public void ToRgba32(ref Rgba32 dest)
{
dest.Rgb = this;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
@ -154,7 +146,7 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
@ -171,9 +163,35 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.R = this.R;
dest.G = this.G;
dest.B = this.B;
dest.A = 255;
dest.A = byte.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
public override string ToString()
{

256
src/ImageSharp/PixelFormats/Rgb48.cs

@ -0,0 +1,256 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Packed pixel type containing three 16-bit unsigned normalized values ranging from 0 to 635535.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Rgb48 : IPixel<Rgb48>
{
private const float Max = 65535F;
/// <summary>
/// Gets or sets the red component.
/// </summary>
public ushort R;
/// <summary>
/// Gets or sets the green component.
/// </summary>
public ushort G;
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public ushort B;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb48"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
public Rgb48(ushort r, ushort g, ushort b)
: this()
{
this.R = r;
this.G = g;
this.B = b;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgb48"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
public Rgb48(float r, float g, float b)
: this()
{
this.R = (ushort)MathF.Round(r.Clamp(0, 1) * Max);
this.G = (ushort)MathF.Round(g.Clamp(0, 1) * Max);
this.B = (ushort)MathF.Round(b.Clamp(0, 1) * Max);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgb48"/> struct.
/// </summary>
/// <param name="vector">The vector containing the components values.</param>
public Rgb48(Vector3 vector)
: this(vector.X, vector.Y, vector.Z)
{
}
/// <summary>
/// Compares two <see cref="Rgb48"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rgb48"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgb48"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Rgb48 left, Rgb48 right)
{
return left.R == right.R
&& left.G == right.G
&& left.B == right.B;
}
/// <summary>
/// Compares two <see cref="Rgb48"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Rgb48"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Rgb48"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgb48 left, Rgb48 right)
{
return left.R != right.R
|| left.G != right.G
|| left.B != right.B;
}
/// <inheritdoc />
public PixelOperations<Rgb48> CreatePixelOperations() => new PixelOperations<Rgb48>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Vector4(this.R / Max, this.G / Max, this.B / Max, 1);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max;
this.R = (ushort)MathF.Round(vector.X);
this.G = (ushort)MathF.Round(vector.Y);
this.B = (ushort)MathF.Round(vector.Z);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromArgb32(Argb32 source)
{
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromBgra32(Bgra32 source)
{
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this = source.Rgb;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToArgb32(ref Argb32 dest)
{
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = 255;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = 255;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this = source;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest = this;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest)
{
dest.Rgb = this;
dest.A = ushort.MaxValue;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is Rgb48 rgb48 && this.Equals(rgb48);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgb48 other)
{
return this.R == other.R
&& this.G == other.G
&& this.B == other.B;
}
/// <inheritdoc />
public override string ToString() => this.ToVector4().ToString();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return HashHelpers.Combine(
this.R.GetHashCode(),
HashHelpers.Combine(this.G.GetHashCode(), this.B.GetHashCode()));
}
}
}

16
src/ImageSharp/PixelFormats/Rgba1010102.cs

@ -185,6 +185,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{

22
src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs

@ -87,15 +87,15 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc />
internal override void ToVector4(ReadOnlySpan<Rgba32> sourceColors, Span<Vector4> destVectors, int count)
internal override void ToVector4(ReadOnlySpan<Rgba32> sourceColors, Span<Vector4> destinationVectors, int count)
{
Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors));
Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors));
Guard.MustBeSizedAtLeast(destinationVectors, count, nameof(destinationVectors));
if (count < 256 || !Vector.IsHardwareAccelerated)
{
// Doesn't worth to bother with SIMD:
base.ToVector4(sourceColors, destVectors, count);
base.ToVector4(sourceColors, destinationVectors, count);
return;
}
@ -104,25 +104,25 @@ namespace SixLabors.ImageSharp.PixelFormats
if (alignedCount > 0)
{
ToVector4SimdAligned(sourceColors, destVectors, alignedCount);
ToVector4SimdAligned(sourceColors, destinationVectors, alignedCount);
}
if (remainder > 0)
{
sourceColors = sourceColors.Slice(alignedCount);
destVectors = destVectors.Slice(alignedCount);
base.ToVector4(sourceColors, destVectors, remainder);
destinationVectors = destinationVectors.Slice(alignedCount);
base.ToVector4(sourceColors, destinationVectors, remainder);
}
}
/// <inheritdoc />
internal override void PackFromVector4(ReadOnlySpan<Vector4> sourceVectors, Span<Rgba32> destColors, int count)
internal override void PackFromVector4(ReadOnlySpan<Vector4> sourceVectors, Span<Rgba32> destinationColors, int count)
{
GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count);
GuardSpans(sourceVectors, nameof(sourceVectors), destinationColors, nameof(destinationColors), count);
if (!SimdUtils.IsAvx2CompatibleArchitecture)
{
base.PackFromVector4(sourceVectors, destColors, count);
base.PackFromVector4(sourceVectors, destinationColors, count);
return;
}
@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.PixelFormats
if (alignedCount > 0)
{
ReadOnlySpan<float> flatSrc = MemoryMarshal.Cast<Vector4, float>(sourceVectors.Slice(0, alignedCount));
Span<byte> flatDest = MemoryMarshal.Cast<Rgba32, byte>(destColors);
Span<byte> flatDest = MemoryMarshal.Cast<Rgba32, byte>(destinationColors);
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(flatSrc, flatDest);
}
@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{
// actually: remainder == 1
int lastIdx = count - 1;
destColors[lastIdx].PackFromVector4(sourceVectors[lastIdx]);
destinationColors[lastIdx].PackFromVector4(sourceVectors[lastIdx]);
}
}

46
src/ImageSharp/PixelFormats/Rgba32.cs

@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.PixelFormats
this.R = r;
this.G = g;
this.B = b;
this.A = 255;
this.A = byte.MaxValue;
}
/// <summary>
@ -199,7 +199,10 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public uint PackedValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.Rgba;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => this.Rgba = value;
}
@ -384,10 +387,39 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba32 ToRgba32() => this;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = byte.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source)
{
// Taken from libpng pngtran.c line: 2419
this.R = (byte)(((source.R * 255) + 32895) >> 16);
this.G = (byte)(((source.G * 255) + 32895) >> 16);
this.B = (byte)(((source.B * 255) + 32895) >> 16);
this.A = (byte)(((source.A * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Rgba32 other && this.Equals(other);
return obj is Rgba32 rgba32 && this.Equals(rgba32);
}
/// <inheritdoc/>
@ -397,10 +429,7 @@ namespace SixLabors.ImageSharp.PixelFormats
return this.Rgba == other.Rgba;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
/// <inheritdoc/>
public override string ToString()
{
return $"({this.R},{this.G},{this.B},{this.A})";
@ -410,8 +439,9 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return this.Rgba.GetHashCode();
int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.A.GetHashCode());
}
/// <summary>

187
src/ImageSharp/PixelFormats/Rgba64.cs

@ -4,27 +4,71 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
/// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 635535.
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Rgba64 : IPixel<Rgba64>, IPackedVector<ulong>
{
private const float Max = 65535F;
/// <summary>
/// Gets or sets the red component.
/// </summary>
public ushort R;
/// <summary>
/// Gets or sets the green component.
/// </summary>
public ushort G;
/// <summary>
/// Gets or sets the blue component.
/// </summary>
public ushort B;
/// <summary>
/// Gets or sets the alpha component.
/// </summary>
public ushort A;
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Rgba64(ushort r, ushort g, ushort b, ushort a)
: this()
{
this.R = r;
this.G = g;
this.B = b;
this.A = a;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Rgba64(float x, float y, float z, float w)
/// <param name="r">The red component.</param>
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
public Rgba64(float r, float g, float b, float a)
: this()
{
this.PackedValue = Pack(x, y, z, w);
this.R = (ushort)MathF.Round(r.Clamp(0, 1) * Max);
this.G = (ushort)MathF.Round(g.Clamp(0, 1) * Max);
this.B = (ushort)MathF.Round(b.Clamp(0, 1) * Max);
this.A = (ushort)MathF.Round(a.Clamp(0, 1) * Max);
}
/// <summary>
@ -32,12 +76,42 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary>
/// <param name="vector">The vector containing the components values.</param>
public Rgba64(Vector4 vector)
: this(vector.X, vector.Y, vector.Z, vector.W)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="packed">The packed value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rgba64(ulong packed)
: this()
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
this.PackedValue = packed;
}
/// <summary>
/// Gets or sets the RGB components of this struct as <see cref="Rgb48"/>
/// </summary>
public Rgb48 Rgb
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Unsafe.As<Rgba64, Rgb48>(ref this);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Unsafe.As<Rgba64, Rgb48>(ref this) = value;
}
/// <inheritdoc/>
public ulong PackedValue { get; set; }
public ulong PackedValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Unsafe.As<Rgba64, ulong>(ref this);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => Unsafe.As<Rgba64, ulong>(ref this) = value;
}
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
@ -96,18 +170,18 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
return new Vector4(
(this.PackedValue & 0xFFFF) / 65535F,
((this.PackedValue >> 16) & 0xFFFF) / 65535F,
((this.PackedValue >> 32) & 0xFFFF) / 65535F,
((this.PackedValue >> 48) & 0xFFFF) / 65535F);
return new Vector4(this.R, this.G, this.B, this.A) / Max;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max;
this.R = (ushort)MathF.Round(vector.X);
this.G = (ushort)MathF.Round(vector.Y);
this.B = (ushort)MathF.Round(vector.Z);
this.A = (ushort)MathF.Round(vector.W);
}
/// <inheritdoc />
@ -131,63 +205,79 @@ namespace SixLabors.ImageSharp.PixelFormats
this.PackFromVector4(source.ToVector4());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this = source;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
// Taken from libpng pngtran.c line: 2419
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = (byte)(((this.A * 255) + 32895) >> 16);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source)
{
this.Rgb = source;
this.A = ushort.MaxValue;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest = this.Rgb;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest = this;
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToArgb32(ref Argb32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = (byte)(((this.A * 255) + 32895) >> 16);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4() * 255F;
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4() * 255F;
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
dest.A = (byte)MathF.Round(vector.W);
dest.R = (byte)(((this.R * 255) + 32895) >> 16);
dest.G = (byte)(((this.G * 255) + 32895) >> 16);
dest.B = (byte)(((this.B * 255) + 32895) >> 16);
dest.A = (byte)(((this.A * 255) + 32895) >> 16);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is Rgba64 other && this.Equals(other);
return obj is Rgba64 rgba64 && this.Equals(rgba64);
}
/// <inheritdoc />
@ -209,22 +299,5 @@ namespace SixLabors.ImageSharp.PixelFormats
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Pack(float x, float y, float z, float w)
{
return (ulong)MathF.Round(x.Clamp(0, 1) * 65535F) |
((ulong)MathF.Round(y.Clamp(0, 1) * 65535F) << 16) |
((ulong)MathF.Round(z.Clamp(0, 1) * 65535F) << 32) |
((ulong)MathF.Round(w.Clamp(0, 1) * 65535F) << 48);
}
}
}
}

16
src/ImageSharp/PixelFormats/RgbaVector.cs

@ -298,6 +298,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)

16
src/ImageSharp/PixelFormats/Short2.cs

@ -207,6 +207,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = 255;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.

19
src/ImageSharp/PixelFormats/Short4.cs

@ -213,6 +213,22 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = (byte)MathF.Round(vector.W);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4());
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
/// <inheritdoc />
public override bool Equals(object obj)
{
@ -279,8 +295,7 @@ namespace SixLabors.ImageSharp.PixelFormats
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
return vector;
return Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
}
}
}

1
tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs

@ -86,6 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
};
TPixel color = NamedColors<TPixel>.Black;
provider.VerifyOperation(

2
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(c => c.Quantize(quantizer));
image.DebugSave(provider, new PngEncoder() { PngColorType = PngColorType.Palette }, testOutputDetails: quantizerName);
image.DebugSave(provider, new PngEncoder() { ColorType = PngColorType.Palette }, testOutputDetails: quantizerName);
}
provider.Configuration.MemoryAllocator.ReleaseRetainedResources();

2
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
if (!CustomToleranceValues.TryGetValue(file, out float tolerance))
{
bool baseline = file.ToLower().Contains("baseline");
bool baseline = file.IndexOf("baseline", StringComparison.OrdinalIgnoreCase) >= 0;
tolerance = baseline ? BaselineTolerance : ProgressiveTolerance;
}

173
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -20,79 +20,105 @@ namespace SixLabors.ImageSharp.Tests
{
private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32;
// TODO: Cannot use exact comparer since System.Drawing doesn't preserve more than 32bits.
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.1302F, 2134);
// Contains the png marker, IHDR and pHYs chunks of a 1x1 pixel 32bit png 1 a single black pixel.
private static byte[] raw1x1PngIHDRAndpHYs =
private static readonly byte[] raw1x1PngIHDRAndpHYs =
{
// PNG Identifier
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
// IHDR
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00,
// IHDR CRC
0x90, 0x77, 0x53, 0xDE,
0x90, 0x77, 0x53, 0xDE,
// pHYS
0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01,
0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01,
// pHYS CRC
0xC7, 0x6F, 0xA8, 0x64
};
// Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel.
private static byte[] raw1x1PngIDATAndIEND =
private static readonly byte[] raw1x1PngIDATAndIEND =
{
// IDAT
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00,
0x00, 0x04, 0x00, 0x01,
0x00, 0x04, 0x00, 0x01,
// IDAT CRC
0x5C, 0xCD, 0xFF, 0x69,
0x5C, 0xCD, 0xFF, 0x69,
// IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4E, 0x44,
0x4E, 0x44,
// IEND CRC
0xAE, 0x42, 0x60, 0x82
};
public static readonly string[] CommonTestImages =
{
TestImages.Png.Splash,
TestImages.Png.Indexed,
TestImages.Png.FilterVar,
TestImages.Png.Bad.ChunkLength1,
TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.VimImage1,
TestImages.Png.VersioningImage1,
TestImages.Png.VersioningImage2,
TestImages.Png.SnakeGame,
TestImages.Png.Banner7Adam7InterlaceMode,
TestImages.Png.Banner8Index,
TestImages.Png.Bad.ChunkLength2,
TestImages.Png.VimImage2,
};
{
TestImages.Png.Splash,
TestImages.Png.Indexed,
TestImages.Png.FilterVar,
TestImages.Png.Bad.ChunkLength1,
TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.VimImage1,
TestImages.Png.VersioningImage1,
TestImages.Png.VersioningImage2,
TestImages.Png.SnakeGame,
TestImages.Png.Banner7Adam7InterlaceMode,
TestImages.Png.Banner8Index,
TestImages.Png.Bad.ChunkLength2,
TestImages.Png.VimImage2,
TestImages.Png.Rgb24BppTrans,
TestImages.Png.GrayAlpha8Bit
};
public static readonly string[] TestImages48Bpp =
{
TestImages.Png.Rgb48Bpp,
TestImages.Png.Rgb48BppInterlaced
};
{
TestImages.Png.Rgb48Bpp,
TestImages.Png.Rgb48BppInterlaced
};
public static readonly string[] TestImages64Bpp =
{
TestImages.Png.Rgba64Bpp,
TestImages.Png.Rgb48BppTrans
};
public static readonly string[] TestImagesGray16Bit =
{
TestImages.Png.Gray16Bit,
};
public static readonly string[] TestImagesGrayAlpha16Bit =
{
TestImages.Png.GrayAlpha16Bit,
TestImages.Png.GrayTrns16BitInterlaced
};
// This is a workaround for Mono-s decoder being incompatible with ours and GDI+.
// We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA!
private static readonly string[] SkipOnMono =
{
TestImages.Png.Bad.ChunkLength2,
TestImages.Png.VimImage2,
TestImages.Png.Splash,
TestImages.Png.Indexed,
TestImages.Png.Bad.ChunkLength1,
TestImages.Png.VersioningImage1,
TestImages.Png.Banner7Adam7InterlaceMode,
};
{
TestImages.Png.Bad.ChunkLength2,
TestImages.Png.VimImage2,
TestImages.Png.Splash,
TestImages.Png.Indexed,
TestImages.Png.Bad.ChunkLength1,
TestImages.Png.VersioningImage1,
TestImages.Png.Banner7Adam7InterlaceMode,
TestImages.Png.GrayTrns16BitInterlaced,
TestImages.Png.Rgb48BppInterlaced
};
private static bool SkipVerification(ITestImageProvider provider)
{
@ -118,7 +144,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
}
[Theory]
[WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)]
public void Decode_Interlaced_DoesNotThrow<TPixel>(TestImageProvider<TPixel> provider)
@ -142,20 +168,66 @@ namespace SixLabors.ImageSharp.Tests
}
}
// TODO: We need to decode these into Rgba64 properly, and do 'CompareToOriginal' in a Rgba64 mode! (See #285)
[Theory]
[WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgba32)]
[WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgb48)]
public void Decode_48Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
{
image.DebugSave(provider);
var encoder = new PngEncoder { ColorType = PngColorType.Rgb, BitDepth = PngBitDepth.Bit16 };
// Workaround a bug in mono-s System.Drawing PNG decoder. It can't deal with 48Bpp png-s :(
if (!TestEnvironment.IsLinux && !TestEnvironment.IsMono)
if (!SkipVerification(provider))
{
image.CompareToOriginal(provider, ImageComparer.Exact);
image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer);
}
}
}
[Theory]
[WithFileCollection(nameof(TestImages64Bpp), PixelTypes.Rgba64)]
public void Decode_64Bpp<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
{
var encoder = new PngEncoder { ColorType = PngColorType.RgbWithAlpha, BitDepth = PngBitDepth.Bit16 };
if (!SkipVerification(provider))
{
image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer);
}
}
}
[Theory]
[WithFileCollection(nameof(TestImagesGray16Bit), PixelTypes.Rgb48)]
public void Decode_Gray16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
{
var encoder = new PngEncoder { ColorType = PngColorType.Grayscale, BitDepth = PngBitDepth.Bit16 };
if (!SkipVerification(provider))
{
image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer);
}
}
}
[Theory]
[WithFileCollection(nameof(TestImagesGrayAlpha16Bit), PixelTypes.Rgba64)]
public void Decode_GrayAlpha16Bit<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
{
var encoder = new PngEncoder { ColorType = PngColorType.GrayscaleWithAlpha, BitDepth = PngBitDepth.Bit16 };
if (!SkipVerification(provider))
{
image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer);
}
}
}
@ -233,7 +305,7 @@ namespace SixLabors.ImageSharp.Tests
[InlineData(TestImages.Png.Rgb48BppInterlaced, 48)]
public void DetectPixelSize(string imagePath, int expectedPixelSize)
{
TestFile testFile = TestFile.Create(imagePath);
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel);
@ -257,10 +329,7 @@ namespace SixLabors.ImageSharp.Tests
var decoder = new PngDecoder();
ImageFormatException exception = Assert.Throws<ImageFormatException>(() =>
{
decoder.Decode<Rgb24>(null, memStream);
});
ImageFormatException exception = Assert.Throws<ImageFormatException>(() => decoder.Decode<Rgb24>(null, memStream));
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
}

4
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -130,8 +130,8 @@ namespace SixLabors.ImageSharp.Tests
var encoder = new PngEncoder
{
PngColorType = pngColorType,
PngFilterMethod = pngFilterMethod,
ColorType = pngColorType,
FilterMethod = pngFilterMethod,
CompressionLevel = compressionLevel,
Quantizer = new WuQuantizer(paletteSize)
};

3
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -56,7 +56,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="TestUtilities\Factories\" />
</ItemGroup>
</Project>

49
tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs

@ -157,5 +157,54 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Alpha8_PackFromScaledVector4_ToRgba64()
{
// arrange
Alpha8 alpha = default;
Rgba64 actual = default;
var expected = new Rgba64(0, 0, 0, 65535);
Vector4 scaled = new Alpha8(1F).ToScaledVector4();
// act
alpha.PackFromScaledVector4(scaled);
alpha.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Alpha8_PackFromRgb48_ToRgb48()
{
// arrange
var alpha = default(Alpha8);
var actual = default(Rgb48);
var expected = new Rgb48(0, 0, 0);
// act
alpha.PackFromRgb48(expected);
alpha.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Alpha8_PackFromRgba64_ToRgba64()
{
// arrange
var alpha = default(Alpha8);
var actual = default(Rgba64);
var expected = new Rgba64(0, 0, 0, 65535);
// act
alpha.PackFromRgba64(expected);
alpha.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs

@ -189,5 +189,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Argb32_PackFromRgb48_ToRgb48()
{
// arrange
var argb = default(Argb32);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
argb.PackFromRgb48(expected);
argb.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Argb32_PackFromRgba64_ToRgba64()
{
// arrange
var argb = default(Argb32);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
argb.PackFromRgba64(expected);
argb.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

36
tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs

@ -22,13 +22,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.Equal(g, p.G);
Assert.Equal(b, p.B);
}
[Fact]
public unsafe void ByteLayoutIsSequentialBgr()
{
var color = new Bgr24(1, 2, 3);
byte* ptr = (byte*)&color;
Assert.Equal(3, ptr[0]);
Assert.Equal(2, ptr[1]);
Assert.Equal(1, ptr[2]);
@ -139,5 +139,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.Equal(new Bgra32(1, 2, 3, 255), bgra);
}
[Fact]
public void Bgr24_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Bgr24);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgr24_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Bgr24);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

34
tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests.PixelFormats
{
{
public class Bgr565Tests
{
[Fact]
@ -144,5 +144,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgr565_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Bgr565);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgr565_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Bgr565);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

34
tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
public static readonly TheoryData<byte, byte, byte, byte> ColorData =
new TheoryData<byte, byte, byte, byte>()
{
{ 1, 2, 3, 4 }, { 4, 5, 6, 7 }, { 0, 255, 42, 0 }, { 1, 2, 3, 255 }
{ 1, 2, 3, 4 }, { 4, 5, 6, 7 }, { 0, 255, 42, 0 }, { 1, 2, 3, 255 }
};
[Theory]
@ -146,5 +146,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.Equal(new Bgra32(1, 2, 3, 4), bgra);
}
[Fact]
public void Bgra32_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Bgra32);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgra32_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Bgra32);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs

@ -193,5 +193,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgra4444_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Bgra4444);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgra4444_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Bgra4444);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs

@ -192,5 +192,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgra5551_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Bgra5551);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Bgra5551_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Bgra5551);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs

@ -190,5 +190,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Byte4_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Byte4);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Byte4_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Byte4);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs

@ -141,5 +141,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfSingle_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(HalfSingle);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfSingle_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(HalfSingle);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs

@ -146,5 +146,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfVector2_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(HalfVector2);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfVector2_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(HalfVector2);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs

@ -188,5 +188,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfVector4_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(HalfVector4);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void HalfVector4_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(HalfVector4);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs

@ -157,5 +157,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedByte2_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(NormalizedByte2);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedByte2_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(NormalizedByte2);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs

@ -168,5 +168,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedByte4_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(NormalizedByte4);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedByte4_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(NormalizedByte4);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs

@ -161,5 +161,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedShort2_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(NormalizedShort2);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedShort2_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(NormalizedShort2);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs

@ -169,5 +169,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedShort4_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(NormalizedShort4);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizedShort4_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(NormalizedShort4);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

137
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs

@ -3,6 +3,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -203,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromXyzBytes(int count)
public void PackFromRgb24Bytes(int count)
{
byte[] source = CreateByteTestData(count * 3);
var expected = new TPixel[count];
@ -224,7 +225,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToXyzBytes(int count)
public void ToRgb24Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 3];
@ -248,7 +249,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromXyzwBytes(int count)
public void PackFromRgba32Bytes(int count)
{
byte[] source = CreateByteTestData(count * 4);
var expected = new TPixel[count];
@ -269,7 +270,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToXyzwBytes(int count)
public void ToRgba32Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 4];
@ -294,7 +295,109 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromZyxBytes(int count)
public void PackFromRgb48Bytes(int count)
{
byte[] source = CreateByteTestData(count * 6);
Span<byte> sourceSpan = source.AsSpan();
var expected = new TPixel[count];
var rgba64 = new Rgba64(0, 0, 0, 65535);
for (int i = 0; i < count; i++)
{
int i6 = i * 6;
rgba64.Rgb = MemoryMarshal.Cast<byte, Rgb48>(sourceSpan.Slice(i6, 6))[0];
expected[i].PackFromRgba64(rgba64);
}
TestOperation(
source,
expected,
(s, d) => Operations.PackFromRgb48Bytes(s, d.GetSpan(), count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToRgb48Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 6];
Rgb48 rgb = default;
for (int i = 0; i < count; i++)
{
int i6 = i * 6;
source[i].ToRgb48(ref rgb);
Rgba64Bytes rgb48Bytes = Unsafe.As<Rgb48, Rgba64Bytes>(ref rgb);
expected[i6] = rgb48Bytes[0];
expected[i6 + 1] = rgb48Bytes[1];
expected[i6 + 2] = rgb48Bytes[2];
expected[i6 + 3] = rgb48Bytes[3];
expected[i6 + 4] = rgb48Bytes[4];
expected[i6 + 5] = rgb48Bytes[5];
}
TestOperation(
source,
expected,
(s, d) => Operations.ToRgb48Bytes(s, d.GetSpan(), count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromRgba64Bytes(int count)
{
byte[] source = CreateByteTestData(count * 8);
Span<byte> sourceSpan = source.AsSpan();
var expected = new TPixel[count];
for (int i = 0; i < count; i++)
{
int i8 = i * 8;
expected[i].PackFromRgba64(MemoryMarshal.Cast<byte, Rgba64>(sourceSpan.Slice(i8, 8))[0]);
}
TestOperation(
source,
expected,
(s, d) => Operations.PackFromRgba64Bytes(s, d.GetSpan(), count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToRgba64Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 8];
Rgba64 rgba = default;
for (int i = 0; i < count; i++)
{
int i8 = i * 8;
source[i].ToRgba64(ref rgba);
Rgba64Bytes rgba64Bytes = Unsafe.As<Rgba64, Rgba64Bytes>(ref rgba);
expected[i8] = rgba64Bytes[0];
expected[i8 + 1] = rgba64Bytes[1];
expected[i8 + 2] = rgba64Bytes[2];
expected[i8 + 3] = rgba64Bytes[3];
expected[i8 + 4] = rgba64Bytes[4];
expected[i8 + 5] = rgba64Bytes[5];
expected[i8 + 6] = rgba64Bytes[6];
expected[i8 + 7] = rgba64Bytes[7];
}
TestOperation(
source,
expected,
(s, d) => Operations.ToRgba64Bytes(s, d.GetSpan(), count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromBgr24Bytes(int count)
{
byte[] source = CreateByteTestData(count * 3);
var expected = new TPixel[count];
@ -315,7 +418,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToZyxBytes(int count)
public void ToBgr24Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 3];
@ -339,7 +442,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromZyxwBytes(int count)
public void PackFromBgra32Bytes(int count)
{
byte[] source = CreateByteTestData(count * 4);
var expected = new TPixel[count];
@ -385,7 +488,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void PackFromWzyxBytes(int count)
public void PackFromArgb32Bytes(int count)
{
byte[] source = CreateByteTestData(count * 4);
var expected = new TPixel[count];
@ -406,7 +509,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Theory]
[MemberData(nameof(ArraySizesData))]
public void ToWzyxBytes(int count)
public void ToArgb32Bytes(int count)
{
TPixel[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 4];
@ -557,5 +660,21 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
(float)rnd.NextDouble()
);
}
[StructLayout(LayoutKind.Sequential)]
private unsafe struct Rgba64Bytes
{
public fixed byte Data[8];
public byte this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref byte self = ref Unsafe.As<Rgba64Bytes, byte>(ref this);
return Unsafe.Add(ref self, idx);
}
}
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs

@ -128,5 +128,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rg32_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rg32);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rg32_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rg32);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

34
tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.False(a.Equals(b));
Assert.False(a.Equals((object)b));
}
[Fact]
public void PackFromRgba32()
{
@ -139,5 +139,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.Equal(new Bgra32(1, 2, 3, 255), bgra);
}
[Fact]
public void Rgb24_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rgb24);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb24_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rgb24);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

199
tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs

@ -0,0 +1,199 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests.PixelFormats
{
public class Rgb48Tests
{
[Fact]
public void Rgb48_Values()
{
var rgb = new Rgba64(5243, 9830, 19660, 29491);
Assert.Equal(5243, rgb.R);
Assert.Equal(9830, rgb.G);
Assert.Equal(19660, rgb.B);
Assert.Equal(29491, rgb.A);
rgb = new Rgba64(5243 / 65535F, 9830 / 65535F, 19660 / 65535F, 29491 / 65535F);
Assert.Equal(5243, rgb.R);
Assert.Equal(9830, rgb.G);
Assert.Equal(19660, rgb.B);
Assert.Equal(29491, rgb.A);
}
[Fact]
public void Rgb48_ToVector4()
{
Assert.Equal(new Vector4(0, 0, 0, 1), new Rgb48(Vector3.Zero).ToVector4());
Assert.Equal(Vector4.One, new Rgb48(Vector3.One).ToVector4());
}
[Fact]
public void Rgb48_ToScaledVector4()
{
// arrange
var short2 = new Rgb48(Vector3.One);
// act
Vector4 actual = short2.ToScaledVector4();
// assert
Assert.Equal(1, actual.X);
Assert.Equal(1, actual.Y);
Assert.Equal(1, actual.Z);
Assert.Equal(1, actual.W);
}
[Fact]
public void Rgb48_PackFromScaledVector4()
{
// arrange
var pixel = default(Rgb48);
var short3 = new Rgb48(Vector3.One);
var expected = new Rgb48(Vector3.One);
// act
Vector4 scaled = short3.ToScaledVector4();
pixel.PackFromScaledVector4(scaled);
// assert
Assert.Equal(expected, pixel);
}
[Fact]
public void Rgb48_Clamping()
{
Assert.Equal(new Vector4(0, 0, 0, 1), new Rgb48(Vector3.One * -1234.0f).ToVector4());
Assert.Equal(Vector4.One, new Rgb48(Vector3.One * 1234.0f).ToVector4());
}
[Fact]
public void Rgb48_ToRgb24()
{
// arrange
var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f);
var actual = default(Rgb24);
var expected = new Rgb24(20, 38, 76);
// act
rgba48.ToRgb24(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_ToRgba32()
{
// arrange
var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f);
var actual = default(Rgba32);
var expected = new Rgba32(20, 38, 76, 255);
// act
rgba48.ToRgba32(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_ToArgb32()
{
// arrange
var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f);
var actual = default(Argb32);
var expected = new Argb32(20, 38, 76, 255);
// act
rgba48.ToArgb32(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba64_ToBgr24()
{
// arrange
var rgb48 = new Rgb48(0.08f, 0.15f, 0.30f);
var actual = default(Bgr24);
var expected = new Bgr24(20, 38, 76);
// act
rgb48.ToBgr24(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_ToBgra32()
{
// arrange
var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f);
var actual = default(Bgra32);
var expected = new Bgra32(20, 38, 76, 255);
// act
rgba48.ToBgra32(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_PackFromRgba32_ToRgba32()
{
// arrange
var rgb48 = default(Rgb48);
var actual = default(Rgba32);
var expected = new Rgba32(20, 38, 76, 255);
// act
rgb48.PackFromRgba32(expected);
rgb48.ToRgba32(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rgb48);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rgb48);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs

@ -178,5 +178,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba1010102_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rgba1010102);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba1010102_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rgba1010102);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs

@ -305,5 +305,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba32_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rgba32);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba32_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rgba32);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

56
tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs

@ -12,7 +12,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
[Fact]
public void Rgba64_PackedValues()
{
Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(5243, 9830, 19660, 29491).PackedValue);
Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(0.08f, 0.15f, 0.30f, 0.45f).PackedValue);
var rgba = new Rgba64(0x73334CCC2666147B);
Assert.Equal(5243, rgba.R);
Assert.Equal(9830, rgba.G);
Assert.Equal(19660, rgba.B);
Assert.Equal(29491, rgba.A);
// Test the limits.
Assert.Equal((ulong)0x0, new Rgba64(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFFFFFFFFFFFFFF, new Rgba64(Vector4.One).PackedValue);
@ -50,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// arrange
var pixel = default(Rgba64);
var short4 = new Rgba64(Vector4.One);
ulong expected = 0xFFFFFFFFFFFFFFFF;
const ulong expected = 0xFFFFFFFFFFFFFFFF;
// act
Vector4 scaled = short4.ToScaledVector4();
@ -98,6 +105,21 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba64_ToArgb32()
{
// arrange
var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f);
var actual = default(Argb32);
var expected = new Argb32(20, 38, 76, 115);
// act
rgba64.ToArgb32(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba64_ToBgr24()
{
@ -143,5 +165,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgb48_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Rgba64);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Rgba64_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Rgba64);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

34
tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs

@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests
namespace SixLabors.ImageSharp.Tests.PixelFormats
{
/// <summary>
/// Tests the <see cref="RgbaVector"/> struct.
@ -126,5 +126,37 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(3, ordered[2]);
Assert.Equal(4, ordered[3]);
}
[Fact]
public void RgbaVector_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(RgbaVector);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void RgbaVector_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(RgbaVector);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs

@ -169,5 +169,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Short2_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Short2);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 65535, 0);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Short2_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Short2);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 65535, 0, 65535);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

32
tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs

@ -205,5 +205,37 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Short4_PackFromRgb48_ToRgb48()
{
// arrange
var input = default(Short4);
var actual = default(Rgb48);
var expected = new Rgb48(65535, 0, 65535);
// act
input.PackFromRgb48(expected);
input.ToRgb48(ref actual);
// assert
Assert.Equal(expected, actual);
}
[Fact]
public void Short4_PackFromRgba64_ToRgba64()
{
// arrange
var input = default(Short4);
var actual = default(Rgba64);
var expected = new Rgba64(65535, 0, 65535, 0);
// act
input.PackFromRgba64(expected);
input.ToRgba64(ref actual);
// assert
Assert.Equal(expected, actual);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
public class DetectEdgesTest : FileTestBase
{
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.001f);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F);
public static readonly string[] CommonTestImages = { TestImages.Png.Bike };

2
tests/ImageSharp.Tests/Processing/Processors/Filters/FilterTest.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Filters
[GroupOutput("Filters")]
public class FilterTest
{
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.005f, 3);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0218f, 3);
// Testing the generic FilterProcessor with more than one pixel type intentionally.
// There is no need to do this with the specialized ones.

2
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial };
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.005f);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.069F);
public static readonly TheoryData<string, IResampler> AllReSamplers =
new TheoryData<string, IResampler>

2
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
private readonly ITestOutputHelper Output;
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.005f, 3);
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0085f, 3);
/// <summary>
/// angleDeg, sx, sy, tx, ty

5
tests/ImageSharp.Tests/TestFile.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests
/// </summary>
// ReSharper disable once InconsistentNaming
private static readonly Lazy<string> inputImagesDirectory = new Lazy<string>(() => TestEnvironment.InputImagesDirectoryFullPath);
/// <summary>
/// The image (lazy initialized value)
/// </summary>
@ -74,9 +74,10 @@ namespace SixLabors.ImageSharp.Tests
private Image<Rgba32> Image => this.image ?? (this.image = ImageSharp.Image.Load<Rgba32>(this.Bytes));
/// <summary>
/// Gets the input image directory.
/// </summary>
private static string InputImagesDirectory => inputImagesDirectory.Value;
/// <summary>
/// Gets the full qualified path to the input test file.
/// </summary>

11
tests/ImageSharp.Tests/TestImages.cs

@ -27,12 +27,19 @@ namespace SixLabors.ImageSharp.Tests
public const string Palette8Bpp = "Png/palette-8bpp.png";
public const string Bpp1 = "Png/bpp1.png";
public const string Gray4Bpp = "Png/gray_4bpp.png";
public const string Gray16Bit = "Png/gray-16.png";
public const string GrayAlpha8Bit = "Png/gray-alpha-8.png";
public const string GrayAlpha16Bit = "Png/gray-alpha-16.png";
public const string GrayTrns16BitInterlaced = "Png/gray-16-tRNS-interlaced.png";
public const string Rgb24BppTrans = "Png/rgb-8-tRNS.png";
public const string Rgb48Bpp = "Png/rgb-48bpp.png";
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
public const string Rgb48BppTrans = "Png/rgb-16-tRNS.png";
public const string Rgba64Bpp = "Png/rgb-16-alpha.png";
public const string CalliphoraPartial = "Png/CalliphoraPartial.png";
public const string CalliphoraPartialGrayscale = "Png/CalliphoraPartialGrayscale.png";
public const string Bike = "Png/Bike.png";
public const string BikeGrayscale = "Png/BikeGrayscale.png";
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
public const string SnakeGame = "Png/SnakeGame.png";
public const string Icon = "Png/icon.png";
public const string Kaboom = "Png/kaboom.png";
@ -126,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests
};
}
public class Issues
public static class Issues
{
public const string CriticalEOF214 = "Jpg/issues/Issue214-CriticalEOF.jpg";
public const string MissingFF00ProgressiveGirl159 = "Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg";

14
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs

@ -22,10 +22,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
int width = actual.Width;
// TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types.
// TODO: Comparing through Rgba64 may not be robust enough because of the existance of super high precision pixel types.
var aBuffer = new Rgba32[width];
var bBuffer = new Rgba32[width];
var aBuffer = new Rgba64[width];
var bBuffer = new Rgba64[width];
var differences = new List<PixelDifference>();
@ -34,13 +34,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y);
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y);
PixelOperations<TPixelA>.Instance.ToRgba32(aSpan, aBuffer, width);
PixelOperations<TPixelB>.Instance.ToRgba32(bSpan, bBuffer, width);
PixelOperations<TPixelA>.Instance.ToRgba64(aSpan, aBuffer, width);
PixelOperations<TPixelB>.Instance.ToRgba64(bSpan, bBuffer, width);
for (int x = 0; x < width; x++)
{
Rgba32 aPixel = aBuffer[x];
Rgba32 bPixel = bBuffer[x];
Rgba64 aPixel = aBuffer[x];
Rgba64 bPixel = bBuffer[x];
if (aPixel != bPixel)
{

2
tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
int i = 0;
foreach (ImageSimilarityReport r in reports)
{
sb.Append($"Report{i}: ");
sb.Append($"Report ImageFrame {i}: ");
sb.Append(r);
sb.Append(Environment.NewLine);
i++;

19
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs

@ -28,9 +28,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
/// <summary>
/// Returns Tolerant(imageThresholdInPercents/100)
/// </summary>
public static ImageComparer TolerantPercentage(float imageThresholdInPercents,
int perPixelManhattanThreshold = 0) =>
Tolerant(imageThresholdInPercents / 100f, perPixelManhattanThreshold);
public static ImageComparer TolerantPercentage(float imageThresholdInPercents, int perPixelManhattanThreshold = 0)
=> Tolerant(imageThresholdInPercents / 100F, perPixelManhattanThreshold);
public abstract ImageSimilarityReport<TPixelA, TPixelB> CompareImagesOrFrames<TPixelA, TPixelB>(
ImageFrame<TPixelA> expected,
@ -120,18 +119,20 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
var cleanedReports = new List<ImageSimilarityReport<TPixelA, TPixelB>>(reports.Count());
foreach (ImageSimilarityReport<TPixelA, TPixelB> r in reports)
{
IEnumerable<PixelDifference> outsideChanges = r.Differences.Where(x => !(
ignoredRegion.X <= x.Position.X &&
x.Position.X <= ignoredRegion.Right &&
ignoredRegion.Y <= x.Position.Y &&
x.Position.Y <= ignoredRegion.Bottom));
IEnumerable<PixelDifference> outsideChanges = r.Differences.Where(
x =>
!(ignoredRegion.X <= x.Position.X
&& x.Position.X <= ignoredRegion.Right
&& ignoredRegion.Y <= x.Position.Y
&& x.Position.Y <= ignoredRegion.Bottom));
if (outsideChanges.Any())
{
cleanedReports.Add(new ImageSimilarityReport<TPixelA, TPixelB>(r.ExpectedImage, r.ActualImage, outsideChanges, null));
}
}
if (cleanedReports.Any())
if (cleanedReports.Count > 0)
{
throw new ImageDifferenceIsOverThresholdException(cleanedReports);
}

4
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs

@ -19,6 +19,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
this.TotalNormalizedDifference = totalNormalizedDifference;
this.Differences = differences.ToArray();
}
public object ExpectedImage { get; }
public object ActualImage { get; }
@ -59,6 +60,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
var sb = new StringBuilder();
if (this.TotalNormalizedDifference.HasValue)
{
sb.AppendLine();
sb.AppendLine($"Total difference: {this.DifferencePercentageString}");
}
int max = Math.Min(5, this.Differences.Length);
@ -68,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
sb.Append(this.Differences[i]);
if (i < max - 1)
{
sb.Append("; ");
sb.AppendFormat(";{0}", Environment.NewLine);
}
}
if (this.Differences.Length >= 5)

10
tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs

@ -19,12 +19,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
this.AlphaDifference = alphaDifference;
}
public PixelDifference(Point position, Rgba32 expected, Rgba32 actual)
public PixelDifference(Point position, Rgba64 expected, Rgba64 actual)
: this(position,
(int)actual.R - (int)expected.R,
(int)actual.G - (int)expected.G,
(int)actual.B - (int)expected.B,
(int)actual.A - (int)expected.A)
actual.R - expected.R,
actual.G - expected.G,
actual.B - expected.B,
actual.A - expected.A)
{
}

46
tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs

@ -12,11 +12,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
public class TolerantImageComparer : ImageComparer
{
// 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit
public const float DefaultImageThreshold = 1.0f / (100 * 100 * 255);
// 257 = (1 / 255) * 65535.
public const float DefaultImageThreshold = 257F / (100 * 100 * 65535);
/// <summary>
/// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'.
/// </summary>
/// <param name="imageThreshold">The maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535.</param>
/// <param name="perPixelManhattanThreshold">Gets the threshold of the individual pixels before they acumulate towards the overall difference.</param>
public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshold = 0)
{
Guard.MustBeGreaterThanOrEqualTo(imageThreshold, 0, nameof(imageThreshold));
@ -26,25 +29,28 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
/// <summary>
/// The maximal tolerated difference represented by a value between 0.0 and 1.0.
/// <para>
/// Gets the maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535.
/// Examples of percentage differences on a single pixel:
/// 1. PixelA = (255,255,255,0) PixelB =(0,0,0,255) leads to 100% difference on a single pixel
/// 2. PixelA = (255,255,255,0) PixelB =(255,255,255,255) leads to 25% difference on a single pixel
/// 3. PixelA = (255,255,255,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel
///
/// 1. PixelA = (65535,65535,65535,0) PixelB =(0,0,0,65535) leads to 100% difference on a single pixel
/// 2. PixelA = (65535,65535,65535,0) PixelB =(65535,65535,65535,65535) leads to 25% difference on a single pixel
/// 3. PixelA = (65535,65535,65535,0) PixelB =(32767,32767,32767,32767) leads to 50% difference on a single pixel
/// </para>
/// <para>
/// The total differences is the sum of all pixel differences normalized by image dimensions!
/// The individual distances are calculated using the Manhattan function:
/// <see>
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
/// </see>
/// ImageThresholdInPercents = 1.0/255 means that we allow one byte difference per channel on a 1x1 image
/// ImageThresholdInPercents = 1.0/(100*100*255) means that we allow only one byte difference per channel on a 100x100 image
/// ImageThresholdInPercents = 1/255 = 257/65535 means that we allow one unit difference per channel on a 1x1 image
/// ImageThresholdInPercents = 1/(100*100*255) = 257/(100*100*65535) means that we allow only one unit difference per channel on a 100x100 image
/// </para>
/// </summary>
public float ImageThreshold { get; }
/// <summary>
/// The threshold of the individual pixels before they acumulate towards the overall difference.
/// For an individual <see cref="Rgba32"/> pixel pair the value is the Manhattan distance of pixels:
/// Gets the threshold of the individual pixels before they acumulate towards the overall difference.
/// For an individual <see cref="Rgba64"/> pixel pair the value is the Manhattan distance of pixels:
/// <see>
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
/// </see>
@ -60,12 +66,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
int width = actual.Width;
// TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types.
// TODO: Comparing through Rgba64 may not robust enough because of the existance of super high precision pixel types.
var aBuffer = new Rgba32[width];
var bBuffer = new Rgba32[width];
var aBuffer = new Rgba64[width];
var bBuffer = new Rgba64[width];
float totalDifference = 0.0f;
float totalDifference = 0F;
var differences = new List<PixelDifference>();
@ -74,8 +80,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y);
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y);
PixelOperations<TPixelA>.Instance.ToRgba32(aSpan, aBuffer, width);
PixelOperations<TPixelB>.Instance.ToRgba32(bSpan, bBuffer, width);
PixelOperations<TPixelA>.Instance.ToRgba64(aSpan, aBuffer, width);
PixelOperations<TPixelB>.Instance.ToRgba64(bSpan, bBuffer, width);
for (int x = 0; x < width; x++)
{
@ -91,8 +97,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
}
float normalizedDifference = totalDifference / ((float)actual.Width * (float)actual.Height);
normalizedDifference /= 4.0f * 255.0f;
float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height);
normalizedDifference /= 4F * 65535F;
if (normalizedDifference > this.ImageThreshold)
{
@ -105,12 +111,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetManhattanDistanceInRgbaSpace(ref Rgba32 a, ref Rgba32 b)
private static int GetManhattanDistanceInRgbaSpace(ref Rgba64 a, ref Rgba64 b)
{
return Diff(a.R, b.R) + Diff(a.G, b.G) + Diff(a.B, b.B) + Diff(a.A, b.A);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Diff(byte a, byte b) => Math.Abs(a - b);
private static int Diff(ushort a, ushort b) => Math.Abs(a - b);
}
}

20
tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs

@ -80,11 +80,12 @@ namespace SixLabors.ImageSharp.Tests
stride = 1;
}
TPixel[] c = {
NamedColors<TPixel>.HotPink,
NamedColors<TPixel>.Blue
};
TPixel[] c =
{
NamedColors<TPixel>.HotPink,
NamedColors<TPixel>.Blue
};
for (int y = top; y < bottom; y++)
{
int p = 0;
@ -112,10 +113,11 @@ namespace SixLabors.ImageSharp.Tests
int top = 0;
int bottom = pixels.Height / 2;
int stride = pixels.Width / 6;
TPixel[] c = {
NamedColors<TPixel>.Black,
NamedColors<TPixel>.White
};
TPixel[] c =
{
NamedColors<TPixel>.Black,
NamedColors<TPixel>.White
};
int p = 0;
for (int y = top; y < bottom; y++)

19
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Tests
{
Directory.CreateDirectory(baseDir);
}
for (int i = 0; i < frameCount; i++)
{
string filePath = $"{baseDir}/{i:D2}.{extension}";
@ -258,7 +258,7 @@ namespace SixLabors.ImageSharp.Tests
this.TestName = methodName;
this.OutputSubfolderName = outputSubfolderName;
}
internal string GetTestOutputDir()
{
string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName);
@ -281,25 +281,26 @@ namespace SixLabors.ImageSharp.Tests
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = img[x, y];
var rgbaPixel = default(Rgba32);
pixel.ToRgba32(ref rgbaPixel);
Rgba64 rgbaPixel = default;
pixel.ToRgba64(ref rgbaPixel);
ushort change = (ushort)Math.Round((perChannelChange / 255F) * 65535F);
if (rgbaPixel.R + perChannelChange <= 255)
{
rgbaPixel.R += perChannelChange;
rgbaPixel.R += change;
}
else
{
rgbaPixel.R -= perChannelChange;
rgbaPixel.R -= change;
}
if (rgbaPixel.G + perChannelChange <= 255)
{
rgbaPixel.G += perChannelChange;
rgbaPixel.G += change;
}
else
{
rgbaPixel.G -= perChannelChange;
rgbaPixel.G -= change;
}
if (rgbaPixel.B + perChannelChange <= 255)
@ -320,7 +321,7 @@ namespace SixLabors.ImageSharp.Tests
rgbaPixel.A -= perChannelChange;
}
pixel.PackFromRgba32(rgbaPixel);
pixel.PackFromRgba64(rgbaPixel);
img[x, y] = pixel;
}
}

2
tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs

@ -56,6 +56,8 @@ namespace SixLabors.ImageSharp.Tests
Bgra32 = 1 << 20,
Rgb48 = 1 << 21,
// TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper
// "All" is handled as a separate, individual case instead of using bitwise OR

44
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using SixLabors.ImageSharp.Advanced;
@ -10,26 +11,35 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
/// <summary>
/// Provides methods to convert to/from System.Drawing bitmaps.
/// </summary>
public static class SystemDrawingBridge
{
internal static unsafe Image<TPixel> FromFromArgb32SystemDrawingBitmap<TPixel>(System.Drawing.Bitmap bmp)
/// <summary>
/// Returns an image from the given System.Drawing bitmap.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="bmp">The input bitmap.</param>
/// <exception cref="ArgumentException">Thrown if the image pixel format is not of type <see cref="PixelFormat.Format32bppArgb"/></exception>
internal static unsafe Image<TPixel> From32bppArgbSystemDrawingBitmap<TPixel>(Bitmap bmp)
where TPixel : struct, IPixel<TPixel>
{
int w = bmp.Width;
int h = bmp.Height;
var fullRect = new System.Drawing.Rectangle(0, 0, w, h);
var fullRect = new Rectangle(0, 0, w, h);
if (bmp.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Argb32!", nameof(bmp));
throw new ArgumentException($"{nameof(From32bppArgbSystemDrawingBitmap)} : pixel format should be {PixelFormat.Format32bppArgb}!", nameof(bmp));
}
BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* sourcePtrBase = (byte*)data.Scan0;
long sourceRowByteCount = data.Stride;
long destRowByteCount = w * sizeof(Argb32);
long destRowByteCount = w * sizeof(Bgra32);
var image = new Image<TPixel>(w, h);
@ -41,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
byte* sourcePtr = sourcePtrBase + data.Stride * y;
byte* sourcePtr = sourcePtrBase + (data.Stride * y);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
PixelOperations<TPixel>.Instance.PackFromBgra32(workBuffer.GetSpan(), row, row.Length);
@ -53,19 +63,22 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
}
/// <summary>
/// TODO: Doesn not work yet!
/// Returns an image from the given System.Drawing bitmap.
/// </summary>
internal static unsafe Image<TPixel> FromFromRgb24SystemDrawingBitmap<TPixel>(System.Drawing.Bitmap bmp)
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="bmp">The input bitmap.</param>
/// <exception cref="ArgumentException">Thrown if the image pixel format is not of type <see cref="PixelFormat.Format24bppRgb"/></exception>
internal static unsafe Image<TPixel> From24bppRgbSystemDrawingBitmap<TPixel>(Bitmap bmp)
where TPixel : struct, IPixel<TPixel>
{
int w = bmp.Width;
int h = bmp.Height;
var fullRect = new System.Drawing.Rectangle(0, 0, w, h);
var fullRect = new Rectangle(0, 0, w, h);
if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
{
throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Rgb24!", nameof(bmp));
throw new ArgumentException($"{nameof(From24bppRgbSystemDrawingBitmap)}: pixel format should be {PixelFormat.Format24bppRgb}!", nameof(bmp));
}
BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
@ -84,12 +97,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
byte* sourcePtr = sourcePtrBase + data.Stride * y;
byte* sourcePtr = sourcePtrBase + (data.Stride * y);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
PixelOperations<TPixel>.Instance.PackFromBgr24(workBuffer.GetSpan(), row, row.Length);
// FromRgb24(workBuffer.GetSpan(), row);
}
}
}
@ -97,14 +108,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
return image;
}
internal static unsafe System.Drawing.Bitmap ToSystemDrawingBitmap<TPixel>(Image<TPixel> image)
internal static unsafe Bitmap To32bppArgbSystemDrawingBitmap<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
int w = image.Width;
int h = image.Height;
var resultBitmap = new System.Drawing.Bitmap(w, h, PixelFormat.Format32bppArgb);
var fullRect = new System.Drawing.Rectangle(0, 0, w, h);
var resultBitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb);
var fullRect = new Rectangle(0, 0, w, h);
BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat);
byte* destPtrBase = (byte*)data.Scan0;
@ -115,12 +126,11 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
fixed (Bgra32* sourcePtr = &workBuffer.GetReference())
{
for (int y = 0; y < h; y++)
{
Span<TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToBgra32(row, workBuffer.GetSpan(), row.Length);
byte* destPtr = destPtrBase + data.Stride * y;
byte* destPtr = destPtrBase + (data.Stride * y);
Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
}

5
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
if (sourceBitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(sourceBitmap);
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sourceBitmap);
}
using (var convertedBitmap = new System.Drawing.Bitmap(
@ -37,7 +37,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
}
return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(convertedBitmap);
return SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(convertedBitmap);
}
}
}

2
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image))
using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image))
{
sdBitmap.Save(stream, this.imageFormat);
}

9
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests
private static Lazy<Configuration> configuration = new Lazy<Configuration>(CreateDefaultConfiguration);
internal static Configuration Configuration => configuration.Value;
internal static IImageDecoder GetReferenceDecoder(string filePath)
{
IImageFormat format = GetImageFormat(filePath);
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests
internal static IImageFormat GetImageFormat(string filePath)
{
string extension = Path.GetExtension(filePath);
IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension);
return format;
}
@ -60,11 +60,14 @@ namespace SixLabors.ImageSharp.Tests
if (!IsLinux)
{
// TODO: System.Drawing on Windows can decode 48bit and 64bit pngs but
// it doesn't preserve the accuracy we require for comparison.
// This makes CompareToOriginal method non-useful.
configuration.ConfigureCodecs(
ImageFormats.Png,
SystemDrawingReferenceDecoder.Instance,
SystemDrawingReferenceEncoder.Png,
new PngImageFormatDetector());
new PngImageFormatDetector());
configuration.ConfigureCodecs(
ImageFormats.Bmp,

2
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests
() =>
{
bool isCi;
return Boolean.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi;
return bool.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi;
});
private static readonly Lazy<string> NetCoreVersionLazy = new Lazy<string>(GetNetCoreVersion);

6
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -38,15 +38,15 @@ namespace SixLabors.ImageSharp.Tests
{
Span<TPixel> pixelSpan = frame.GetPixelSpan();
PixelOperations<TPixel>.Instance.ToVector4(pixelSpan, tempSpan, pixelSpan.Length);
PixelOperations<TPixel>.Instance.ToScaledVector4(pixelSpan, tempSpan, pixelSpan.Length);
for (int i = 0; i < tempSpan.Length; i++)
{
ref Vector4 v = ref tempSpan[i];
v.W = 1.0f;
v.W = 1F;
}
PixelOperations<TPixel>.Instance.PackFromVector4(tempSpan, pixelSpan, pixelSpan.Length);
PixelOperations<TPixel>.Instance.PackFromScaledVector4(tempSpan, pixelSpan, pixelSpan.Length);
}
}
});

18
tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs

@ -68,16 +68,14 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image<TPixel> clone = image.Clone())
{
byte perChannelChange = 2;
byte perChannelChange = 20;
ImagingTestCaseUtility.ModifyPixel(clone, 3, 1, perChannelChange);
var comparer = ImageComparer.Tolerant();
ImageDifferenceIsOverThresholdException ex = Assert.ThrowsAny<ImageDifferenceIsOverThresholdException>(
() =>
{
comparer.VerifySimilarity(image, clone);
});
() => comparer.VerifySimilarity(image, clone));
PixelDifference diff = ex.Reports.Single().Differences.Single();
Assert.Equal(new Point(3, 1), diff.Position);
}
@ -85,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba64)]
public void TolerantImageComparer_TestPerPixelThreshold<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -93,11 +91,11 @@ namespace SixLabors.ImageSharp.Tests
{
using (Image<TPixel> clone = image.Clone())
{
ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 10);
ImagingTestCaseUtility.ModifyPixel(clone, 1, 0, 10);
ImagingTestCaseUtility.ModifyPixel(clone, 2, 0, 10);
ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 1);
ImagingTestCaseUtility.ModifyPixel(clone, 1, 0, 1);
ImagingTestCaseUtility.ModifyPixel(clone, 2, 0, 1);
var comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 42);
var comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 257 * 3);
comparer.VerifySimilarity(image, clone);
}
}

30
tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs

@ -1,3 +1,4 @@
using System.IO;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
@ -20,12 +21,12 @@ namespace SixLabors.ImageSharp.Tests
[Theory]
[WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
public void ToSystemDrawingBitmap<TPixel>(TestImageProvider<TPixel> provider)
public void To32bppArgbSystemDrawingBitmap<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image))
using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.To32bppArgbSystemDrawingBitmap(image))
{
string fileName = provider.Utility.GetTestOutputFileName("png");
sdBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
@ -35,14 +36,14 @@ namespace SixLabors.ImageSharp.Tests
[Theory]
[WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)]
public void FromFromArgb32SystemDrawingBitmap<TPixel>(TestImageProvider<TPixel> dummyProvider)
public void From32bppArgbSystemDrawingBitmap<TPixel>(TestImageProvider<TPixel> dummyProvider)
where TPixel : struct, IPixel<TPixel>
{
string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash);
using (var sdBitmap = new System.Drawing.Bitmap(path))
{
using (Image<TPixel> image = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(sdBitmap))
using (Image<TPixel> image = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sdBitmap))
{
image.DebugSave(dummyProvider);
}
@ -59,24 +60,27 @@ namespace SixLabors.ImageSharp.Tests
sourceImage.Mutate(c => c.MakeOpaque());
}
var encoder = new PngEncoder() { PngColorType = pngColorType };
var encoder = new PngEncoder() { ColorType = pngColorType };
return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder);
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void FromFromArgb32SystemDrawingBitmap2<TPixel>(TestImageProvider<TPixel> provider)
public void From32bppArgbSystemDrawingBitmap2<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
if (TestEnvironment.IsLinux) return;
if (TestEnvironment.IsLinux)
{
return;
}
string path = SavePng(provider, PngColorType.RgbWithAlpha);
using (var sdBitmap = new System.Drawing.Bitmap(path))
{
using (Image<TPixel> original = provider.GetImage())
using (Image<TPixel> resaved = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap<TPixel>(sdBitmap))
using (Image<TPixel> resaved = SystemDrawingBridge.From32bppArgbSystemDrawingBitmap<TPixel>(sdBitmap))
{
ImageComparer comparer = ImageComparer.Exact;
comparer.VerifySimilarity(original, resaved);
@ -85,20 +89,18 @@ namespace SixLabors.ImageSharp.Tests
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void FromFromRgb24SystemDrawingBitmap2<TPixel>(TestImageProvider<TPixel> provider)
[WithTestPatternImages(100, 100, PixelTypes.Rgb24)]
public void From24bppRgbSystemDrawingBitmap<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
string path = SavePng(provider, PngColorType.Rgb);
using (Image<TPixel> original = provider.GetImage())
{
original.Mutate(c => c.MakeOpaque());
using (var sdBitmap = new System.Drawing.Bitmap(path))
{
using (Image<TPixel> resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap<TPixel>(sdBitmap))
using (Image<TPixel> resaved = SystemDrawingBridge.From24bppRgbSystemDrawingBitmap<TPixel>(sdBitmap))
{
resaved.Mutate(c => c.MakeOpaque());
ImageComparer comparer = ImageComparer.Exact;
comparer.VerifySimilarity(original, resaved);
}
@ -112,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests
where TPixel : struct, IPixel<TPixel>
{
string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash);
using (Image<TPixel> image = Image.Load<TPixel>(path, SystemDrawingReferenceDecoder.Instance))
using (var image = Image.Load<TPixel>(path, SystemDrawingReferenceDecoder.Instance))
{
image.DebugSave(dummyProvider);
}

4
tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs

@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests
[InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))]
[InlineData("lol/Baz.JPG", typeof(JpegDecoder))]
[InlineData("lol/Baz.gif", typeof(GifDecoder))]
public void GetReferenceDecoder_ReturnsCorrectEncoders_Windows(string fileName, Type expectedDecoderType)
public void GetReferenceDecoder_ReturnsCorrectDecoders_Windows(string fileName, Type expectedDecoderType)
{
if (TestEnvironment.IsLinux) return;
@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests
[InlineData("lol/Rofl.bmp", typeof(BmpDecoder))]
[InlineData("lol/Baz.JPG", typeof(JpegDecoder))]
[InlineData("lol/Baz.gif", typeof(GifDecoder))]
public void GetReferenceDecoder_ReturnsCorrectEncoders_Linux(string fileName, Type expectedDecoderType)
public void GetReferenceDecoder_ReturnsCorrectDecoders_Linux(string fileName, Type expectedDecoderType)
{
if (!TestEnvironment.IsLinux) return;

2
tests/Images/External

@ -1 +1 @@
Subproject commit 0e6407be7081341526f815a4d70e7912db276a98
Subproject commit 6fcee2ccd5e8bac98a0290b467ad86bb02d00b6c

3
tests/Images/Input/Png/gray-16-tRNS-interlaced.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8367d01b7d0f926f6c89d6e545735a50cd35815ba7c13367ad5b0489069bd735
size 1448

3
tests/Images/Input/Png/gray-16.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f150b76c824e4870322d6564a214a8ef00b4b100d5fd6f5ba551f6f242005bcc
size 684

3
tests/Images/Input/Png/gray-alpha-16.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc3444bd2457c7c3414359bed6f05d1a5b84c08186b3b54a9ab4a4c503775f63
size 859

3
tests/Images/Input/Png/gray-alpha-8.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0e327f49e2b0deda69811cdf20d36f2ff54f9335e11647755ad4473a6f1408a
size 684

3
tests/Images/Input/Png/rgb-16-alpha.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ad2152ee0de2aaa8745757a9ff5e90a30b6e84365bd90f450286eb2734bd78a
size 1377

3
tests/Images/Input/Png/rgb-16-tRNS.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:733a3f940d41b320ae884ba74339bc94ec8ad5f5a37479ea23b19c1e7b0d0274
size 2624

3
tests/Images/Input/Png/rgb-8-tRNS.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d98223a9a3084f34609bb5dbf55b5ca156559743240bef3406466775a9950e9d
size 1624
Loading…
Cancel
Save