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. 185
      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. 157
      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. 32
      tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs
  45. 32
      tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs
  46. 32
      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. 32
      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. 1
      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. 18
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
  80. 15
      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. 5
      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>

185
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="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(ushort r, ushort g, ushort b, ushort a)
: this()
{
this.PackedValue = Pack(x, y, z, w);
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="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.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)
{
this.PackedValue = Pack(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 = 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;
}

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

@ -20,8 +20,11 @@ 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,
@ -39,60 +42,83 @@ namespace SixLabors.ImageSharp.Tests
};
// 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,
// IDAT CRC
0x5C, 0xCD, 0xFF, 0x69,
// IEND
0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
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)
{
@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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);
}
}
}

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

@ -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

1
tests/ImageSharp.Tests/TestFile.cs

@ -74,6 +74,7 @@ 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;

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);
}
}

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

@ -80,10 +80,11 @@ 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++)
{
@ -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++)

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

@ -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);
}

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

@ -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