diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 62a8bf2b4f..ec9258883d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
- url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AIdeas
+ url: https://github.com/SixLabors/ImageSharp/discussions/categories/ideas
about: Share ideas for new features for this project.
diff --git a/README.md b/README.md
index 2492041db9..6c669fb787 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Built against [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standa
## License
- ImageSharp is licensed under the [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0)
-- An alternative Commercial Support License can be purchased **for projects and applications requiring support**.
+- An alternative Six Labors License can be purchased **for projects and applications requiring developer support**.
Please visit https://sixlabors.com/pricing for details.
## Support Six Labors
@@ -43,7 +43,8 @@ Support the efforts of the development of the Six Labors projects.
## Questions
-- Do you have questions? We are happy to help! Please [join our Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions/category_choices), or ask them on [Stack Overflow](https://stackoverflow.com) using the `ImageSharp` tag. Please do not open issues for questions.
+- Do you have questions? We are happy to help! Simply purchase a [Six Labors License](https://sixlabors.com/pricing) for developer support. Please do not open issues for questions or misuse our [Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions).
+- For feature ideas please [join our Discussions Forum](https://github.com/SixLabors/ImageSharp/discussions/categories/ideas) and we'll be happy to discuss.
- Please read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/main/.github/CONTRIBUTING.md) before opening issues or pull requests!
## Code of Conduct
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
index 6c402fcfd6..e87f2fc573 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/LuminanceForwardConverter{TPixel}.cs
@@ -2,8 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@@ -74,6 +79,44 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
ref Block8x8F yBlock = ref this.Y;
ref L8 l8Start = ref MemoryMarshal.GetReference(this.l8Span);
+ if (RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ ConvertAvx(ref l8Start, ref yBlock);
+ }
+ else
+ {
+ ConvertScalar(ref l8Start, ref yBlock);
+ }
+ }
+
+ ///
+ /// Converts 8x8 L8 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
+ ///
+ /// Start of span of L8 pixels with size of 64
+ /// 8x8 destination matrix of Luminance(Y) converted data
+ private static void ConvertAvx(ref L8 l8Start, ref Block8x8F yBlock)
+ {
+ Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ref Vector128 l8ByteSpan = ref Unsafe.As>(ref l8Start);
+ ref Vector256 destRef = ref yBlock.V0;
+
+ const int bytesPerL8Stride = 8;
+ for (nint i = 0; i < 8; i++)
+ {
+ Unsafe.Add(ref destRef, i) = Avx2.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i)));
+ }
+#endif
+ }
+
+ ///
+ /// Converts 8x8 L8 pixel matrix to 8x8 Block of floats.
+ ///
+ /// Start of span of L8 pixels with size of 64
+ /// 8x8 destination matrix of Luminance(Y) converted data
+ private static void ConvertScalar(ref L8 l8Start, ref Block8x8F yBlock)
+ {
for (int i = 0; i < Block8x8F.Size; i++)
{
ref L8 c = ref Unsafe.Add(ref l8Start, i);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
index 789277d7d3..e2d12916c0 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbForwardConverter{TPixel}.cs
@@ -2,8 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@@ -94,10 +99,56 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
ref Block8x8F greenBlock = ref this.G;
ref Block8x8F blueBlock = ref this.B;
- CopyToBlock(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ if (RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ ConvertAvx(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ }
+ else
+ {
+ ConvertScalar(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock);
+ }
+ }
+
+ ///
+ /// Converts 8x8 RGB24 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
+ ///
+ /// Span of Rgb24 pixels with size of 64
+ /// 8x8 destination matrix of Red converted data
+ /// 8x8 destination matrix of Blue converted data
+ /// 8x8 destination matrix of Green converted data
+ private static void ConvertAvx(Span rgbSpan, ref Block8x8F rBlock, ref Block8x8F gBlock, ref Block8x8F bBlock)
+ {
+ Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter");
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ref Vector256 rgbByteSpan = ref Unsafe.As>(ref MemoryMarshal.GetReference(rgbSpan));
+ ref Vector256 redRef = ref rBlock.V0;
+ ref Vector256 greenRef = ref gBlock.V0;
+ ref Vector256 blueRef = ref bBlock.V0;
+ var zero = Vector256.Create(0).AsByte();
+
+ var extractToLanesMask = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes));
+ var extractRgbMask = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb));
+ Vector256 rgb, rg, bx;
+
+ const int bytesPerRgbStride = 24;
+ for (nint i = 0; i < 8; i++)
+ {
+ rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, bytesPerRgbStride * i).AsUInt32(), extractToLanesMask).AsByte();
+
+ rgb = Avx2.Shuffle(rgb, extractRgbMask);
+
+ rg = Avx2.UnpackLow(rgb, zero);
+ bx = Avx2.UnpackHigh(rgb, zero);
+
+ Unsafe.Add(ref redRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32());
+ Unsafe.Add(ref greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32());
+ Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32());
+ }
+#endif
}
- private static void CopyToBlock(Span rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)
+ private static void ConvertScalar(Span rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock)
{
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
index 9566ee862a..d7542d7a59 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrConverterVectorized.cs
@@ -60,13 +60,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
#if SUPPORTS_RUNTIME_INTRINSICS
- private static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[]
+ internal static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[]
{
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0,
3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0
};
- private static ReadOnlySpan ExtractRgb => new byte[]
+ internal static ReadOnlySpan ExtractRgb => new byte[]
{
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF,
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF
diff --git a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
index e2dbc6ca99..97bce2b2db 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
/// Buffer with decompressed pixel data.
/// The width of the image or strip.
/// The color type of the pixel data.
- /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ /// If set to true decodes the pixel data as big endian, otherwise as little endian.
public static void Undo(Span pixelBytes, int width, TiffColorType colorType, bool isBigEndian)
{
switch (colorType)
@@ -43,12 +43,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
case TiffColorType.Rgb888:
UndoRgb24Bit(pixelBytes, width);
break;
+ case TiffColorType.Rgba8888:
+ UndoRgba32Bit(pixelBytes, width);
+ break;
case TiffColorType.Rgb161616:
UndoRgb48Bit(pixelBytes, width, isBigEndian);
break;
+ case TiffColorType.Rgba16161616:
+ UndoRgba64Bit(pixelBytes, width, isBigEndian);
+ break;
case TiffColorType.Rgb323232:
UndoRgb96Bit(pixelBytes, width, isBigEndian);
break;
+ case TiffColorType.Rgba32323232:
+ UndoRgba128Bit(pixelBytes, width, isBigEndian);
+ break;
}
}
@@ -243,6 +252,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
+ private static void UndoRgba32Bit(Span pixelBytes, int width)
+ {
+ int rowBytesCount = width * 4;
+ int height = pixelBytes.Length / rowBytesCount;
+ for (int y = 0; y < height; y++)
+ {
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ Span rowRgb = MemoryMarshal.Cast(rowBytes).Slice(0, width);
+ ref Rgba32 rowRgbBase = ref MemoryMarshal.GetReference(rowRgb);
+ byte r = rowRgbBase.R;
+ byte g = rowRgbBase.G;
+ byte b = rowRgbBase.B;
+ byte a = rowRgbBase.A;
+
+ for (int x = 1; x < rowRgb.Length; x++)
+ {
+ ref Rgba32 pixel = ref rowRgb[x];
+ r += pixel.R;
+ g += pixel.G;
+ b += pixel.B;
+ a += pixel.A;
+ var rgb = new Rgba32(r, g, b, a);
+ pixel.FromRgba32(rgb);
+ }
+ }
+ }
+
private static void UndoRgb48Bit(Span pixelBytes, int width, bool isBigEndian)
{
int rowBytesCount = width * 6;
@@ -319,6 +355,98 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
+ private static void UndoRgba64Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 8;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort r = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort g = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort b = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort a = TiffUtils.ConvertToUShortBigEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaR = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, r);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaG = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, g);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaB = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, b);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaA = TiffUtils.ConvertToUShortBigEndian(rowSpan);
+ a += deltaA;
+ BinaryPrimitives.WriteUInt16BigEndian(rowSpan, a);
+ offset += 2;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ ushort r = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort g = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort b = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+ ushort a = TiffUtils.ConvertToUShortLittleEndian(rowBytes.Slice(offset, 2));
+ offset += 2;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaR = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, r);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaG = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, g);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaB = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, b);
+ offset += 2;
+
+ rowSpan = rowBytes.Slice(offset, 2);
+ ushort deltaA = TiffUtils.ConvertToUShortLittleEndian(rowSpan);
+ a += deltaA;
+ BinaryPrimitives.WriteUInt16LittleEndian(rowSpan, a);
+ offset += 2;
+ }
+ }
+ }
+ }
+
private static void UndoRgb96Bit(Span pixelBytes, int width, bool isBigEndian)
{
int rowBytesCount = width * 12;
@@ -394,5 +522,97 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
}
+
+ private static void UndoRgba128Bit(Span pixelBytes, int width, bool isBigEndian)
+ {
+ int rowBytesCount = width * 16;
+ int height = pixelBytes.Length / rowBytesCount;
+ if (isBigEndian)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint r = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint g = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint b = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint a = TiffUtils.ConvertToUIntBigEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaR = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, r);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaG = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, g);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaB = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, b);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaA = TiffUtils.ConvertToUIntBigEndian(rowSpan);
+ a += deltaA;
+ BinaryPrimitives.WriteUInt32BigEndian(rowSpan, a);
+ offset += 4;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < height; y++)
+ {
+ int offset = 0;
+ Span rowBytes = pixelBytes.Slice(y * rowBytesCount, rowBytesCount);
+ uint r = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint g = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint b = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+ uint a = TiffUtils.ConvertToUIntLittleEndian(rowBytes.Slice(offset, 4));
+ offset += 4;
+
+ for (int x = 1; x < width; x++)
+ {
+ Span rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaR = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ r += deltaR;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, r);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaG = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ g += deltaG;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, g);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaB = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ b += deltaB;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, b);
+ offset += 4;
+
+ rowSpan = rowBytes.Slice(offset, 4);
+ uint deltaA = TiffUtils.ConvertToUIntLittleEndian(rowSpan);
+ a += deltaA;
+ BinaryPrimitives.WriteUInt32LittleEndian(rowSpan, a);
+ offset += 4;
+ }
+ }
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
index 985ffeb182..4130ee1a29 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
@@ -30,10 +30,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);
int offset = 0;
- byte[] buffer = new byte[4];
+ Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
- Span bufferSpan = buffer.AsSpan(bufferStartIdx);
+ Span bufferSpan = buffer.Slice(bufferStartIdx);
for (int y = top; y < top + height; y++)
{
Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
index ac4435db63..e37fff1e74 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
@@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
color.FromVector4(TiffUtils.Vector4Default);
- byte[] buffer = new byte[4];
+ Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
Span redData = data[0].GetSpan();
Span greenData = data[1].GetSpan();
Span blueData = data[2].GetSpan();
- Span bufferSpan = buffer.AsSpan(bufferStartIdx);
+ Span bufferSpan = buffer.Slice(bufferStartIdx);
int offset = 0;
for (int y = top; y < top + height; y++)
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..7fd8d9879b
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 16 bits for each channel.
+ ///
+ internal class Rgba16161616TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ private readonly Configuration configuration;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration.
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba16161616TiffColor(Configuration configuration, bool isBigEndian)
+ {
+ this.configuration = configuration;
+ this.isBigEndian = isBigEndian;
+ }
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ Rgba64 rgba = TiffUtils.Rgba64Default;
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+
+ int offset = 0;
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
+ offset += 2;
+ ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
+ offset += 2;
+ ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
+ offset += 2;
+ ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
+ offset += 2;
+
+ pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ }
+ }
+ else
+ {
+ int byteCount = pixelRow.Length * 8;
+ PixelOperations.Instance.FromRgba64Bytes(
+ this.configuration,
+ data.Slice(offset, byteCount),
+ pixelRow,
+ pixelRow.Length);
+
+ offset += byteCount;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..705010ce90
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 16 bit.
+ ///
+ internal class Rgba16PlanarTiffColor : TiffBasePlanarColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ Rgba64 rgba = TiffUtils.Rgba64Default;
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+
+ Span redData = data[0].GetSpan();
+ Span greenData = data[1].GetSpan();
+ Span blueData = data[2].GetSpan();
+ Span alphaData = data[3].GetSpan();
+
+ int offset = 0;
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2));
+ ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
+
+ offset += 2;
+
+ pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
+ ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
+
+ offset += 2;
+
+ pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..71e1f7abd6
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 24 bits for each channel.
+ ///
+ internal class Rgba24242424TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba24242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+ int offset = 0;
+ Span buffer = stackalloc byte[4];
+ int bufferStartIdx = this.isBigEndian ? 1 : 0;
+
+ Span bufferSpan = buffer.Slice(bufferStartIdx);
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong r = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong g = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong b = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..03b78c3f87
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout for each color channel with 24 bit.
+ ///
+ internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+ Span buffer = stackalloc byte[4];
+ int bufferStartIdx = this.isBigEndian ? 1 : 0;
+
+ Span redData = data[0].GetSpan();
+ Span greenData = data[1].GetSpan();
+ Span blueData = data[2].GetSpan();
+ Span alphaData = data[3].GetSpan();
+ Span bufferSpan = buffer.Slice(bufferStartIdx);
+
+ int offset = 0;
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ redData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong r = TiffUtils.ConvertToUIntBigEndian(buffer);
+ greenData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong g = TiffUtils.ConvertToUIntBigEndian(buffer);
+ blueData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong b = TiffUtils.ConvertToUIntBigEndian(buffer);
+ alphaData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
+
+ offset += 3;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ redData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ greenData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ blueData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ alphaData.Slice(offset, 3).CopyTo(bufferSpan);
+ ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
+
+ offset += 3;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..fbd18ca3e0
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
+ ///
+ internal class Rgba32323232TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+ int offset = 0;
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong g = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong b = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
+ offset += 4;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..e231111590
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and a 'Planar' layout for each color channel with 32 bit.
+ ///
+ internal class Rgba32PlanarTiffColor : TiffBasePlanarColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba32PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+
+ Span redData = data[0].GetSpan();
+ Span greenData = data[1].GetSpan();
+ Span blueData = data[2].GetSpan();
+ Span alphaData = data[3].GetSpan();
+
+ int offset = 0;
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUIntBigEndian(redData.Slice(offset, 4));
+ ulong g = TiffUtils.ConvertToUIntBigEndian(greenData.Slice(offset, 4));
+ ulong b = TiffUtils.ConvertToUIntBigEndian(blueData.Slice(offset, 4));
+ ulong a = TiffUtils.ConvertToUIntBigEndian(alphaData.Slice(offset, 4));
+
+ offset += 4;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ ulong r = TiffUtils.ConvertToUIntLittleEndian(redData.Slice(offset, 4));
+ ulong g = TiffUtils.ConvertToUIntLittleEndian(greenData.Slice(offset, 4));
+ ulong b = TiffUtils.ConvertToUIntLittleEndian(blueData.Slice(offset, 4));
+ ulong a = TiffUtils.ConvertToUIntLittleEndian(alphaData.Slice(offset, 4));
+
+ offset += 4;
+
+ pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..491a42fb75
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and 8 bits per channel.
+ ///
+ internal class Rgba8888TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly Configuration configuration;
+
+ public Rgba8888TiffColor(Configuration configuration) => this.configuration = configuration;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ int offset = 0;
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ int byteCount = pixelRow.Length * 4;
+ PixelOperations.Instance.FromRgba32Bytes(
+ this.configuration,
+ data.Slice(offset, byteCount),
+ pixelRow,
+ pixelRow.Length);
+
+ offset += byteCount;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..4fb0797dc5
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
@@ -0,0 +1,97 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 32 bits for each channel.
+ ///
+ internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public RgbaFloat32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+ int offset = 0;
+ byte[] buffer = new byte[4];
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float r = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float g = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float b = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ Array.Reverse(buffer);
+ float a = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ var colorVector = new Vector4(r, g, b, a);
+ color.FromVector4(colorVector);
+ pixelRow[x] = color;
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 4).CopyTo(buffer);
+ float r = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ float g = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ float b = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ data.Slice(offset, 4).CopyTo(buffer);
+ float a = BitConverter.ToSingle(buffer, 0);
+ offset += 4;
+
+ var colorVector = new Vector4(r, g, b, a);
+ color.FromVector4(colorVector);
+ pixelRow[x] = color;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..f2ccf2ec81
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers;
+using System.Numerics;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with an alpha channel and with 'Planar' layout (for all bit depths).
+ ///
+ internal class RgbaPlanarTiffColor : TiffBasePlanarColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly float rFactor;
+
+ private readonly float gFactor;
+
+ private readonly float bFactor;
+
+ private readonly float aFactor;
+
+ private readonly ushort bitsPerSampleR;
+
+ private readonly ushort bitsPerSampleG;
+
+ private readonly ushort bitsPerSampleB;
+
+ private readonly ushort bitsPerSampleA;
+
+ public RgbaPlanarTiffColor(TiffBitsPerSample bitsPerSample)
+ {
+ this.bitsPerSampleR = bitsPerSample.Channel0;
+ this.bitsPerSampleG = bitsPerSample.Channel1;
+ this.bitsPerSampleB = bitsPerSample.Channel2;
+ this.bitsPerSampleA = bitsPerSample.Channel3;
+
+ this.rFactor = (1 << this.bitsPerSampleR) - 1.0f;
+ this.gFactor = (1 << this.bitsPerSampleG) - 1.0f;
+ this.bFactor = (1 << this.bitsPerSampleB) - 1.0f;
+ this.aFactor = (1 << this.bitsPerSampleA) - 1.0f;
+ }
+
+ ///
+ /// Decodes pixel data using the current photometric interpretation.
+ ///
+ /// The buffers to read image data from.
+ /// The image buffer to write pixels to.
+ /// The x-coordinate of the left-hand side of the image block.
+ /// The y-coordinate of the top of the image block.
+ /// The width of the image block.
+ /// The height of the image block.
+ public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ var color = default(TPixel);
+
+ var rBitReader = new BitReader(data[0].GetSpan());
+ var gBitReader = new BitReader(data[1].GetSpan());
+ var bBitReader = new BitReader(data[2].GetSpan());
+ var aBitReader = new BitReader(data[3].GetSpan());
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor;
+ float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
+ float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
+ float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor;
+
+ color.FromVector4(new Vector4(r, g, b, a));
+ pixelRow[x] = color;
+ }
+
+ rBitReader.NextRow();
+ gBitReader.NextRow();
+ bBitReader.NextRow();
+ aBitReader.NextRow();
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..8bec7da89d
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
@@ -0,0 +1,72 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with alpha channel (for all bit depths).
+ ///
+ internal class RgbaTiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly float rFactor;
+
+ private readonly float gFactor;
+
+ private readonly float bFactor;
+
+ private readonly float aFactor;
+
+ private readonly ushort bitsPerSampleR;
+
+ private readonly ushort bitsPerSampleG;
+
+ private readonly ushort bitsPerSampleB;
+
+ private readonly ushort bitsPerSampleA;
+
+ public RgbaTiffColor(TiffBitsPerSample bitsPerSample)
+ {
+ this.bitsPerSampleR = bitsPerSample.Channel0;
+ this.bitsPerSampleG = bitsPerSample.Channel1;
+ this.bitsPerSampleB = bitsPerSample.Channel2;
+ this.bitsPerSampleA = bitsPerSample.Channel3;
+
+ this.rFactor = (1 << this.bitsPerSampleR) - 1.0f;
+ this.gFactor = (1 << this.bitsPerSampleG) - 1.0f;
+ this.bFactor = (1 << this.bitsPerSampleB) - 1.0f;
+ this.aFactor = (1 << this.bitsPerSampleA) - 1.0f;
+ }
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ var color = default(TPixel);
+
+ var bitReader = new BitReader(data);
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ float r = bitReader.ReadBits(this.bitsPerSampleR) / this.rFactor;
+ float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
+ float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
+ float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor;
+
+ color.FromVector4(new Vector4(r, g, b, a));
+ pixelRow[x] = color;
+ }
+
+ bitReader.NextRow();
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index a8c4cef4ed..1c608e3033 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -116,6 +116,38 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
+ case TiffColorType.Rgba2222:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 2
+ && bitsPerSample.Channel2 == 2
+ && bitsPerSample.Channel1 == 2
+ && bitsPerSample.Channel0 == 2,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgb333:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 3
+ && bitsPerSample.Channel1 == 3
+ && bitsPerSample.Channel0 == 3,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgba3333:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 3
+ && bitsPerSample.Channel2 == 3
+ && bitsPerSample.Channel1 == 3
+ && bitsPerSample.Channel0 == 3,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
case TiffColorType.Rgb444:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -126,6 +158,59 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb444TiffColor();
+ case TiffColorType.Rgba4444:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 4
+ && bitsPerSample.Channel2 == 4
+ && bitsPerSample.Channel1 == 4
+ && bitsPerSample.Channel0 == 4,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgb555:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 5
+ && bitsPerSample.Channel1 == 5
+ && bitsPerSample.Channel0 == 5,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgba5555:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 5
+ && bitsPerSample.Channel2 == 5
+ && bitsPerSample.Channel1 == 5
+ && bitsPerSample.Channel0 == 5,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgb666:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 6
+ && bitsPerSample.Channel1 == 6
+ && bitsPerSample.Channel0 == 6,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbTiffColor(bitsPerSample);
+
+ case TiffColorType.Rgba6666:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 6
+ && bitsPerSample.Channel2 == 6
+ && bitsPerSample.Channel1 == 6
+ && bitsPerSample.Channel0 == 6,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
case TiffColorType.Rgb888:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -136,6 +221,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb888TiffColor(configuration);
+ case TiffColorType.Rgba8888:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 8
+ && bitsPerSample.Channel2 == 8
+ && bitsPerSample.Channel1 == 8
+ && bitsPerSample.Channel0 == 8,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba8888TiffColor(configuration);
+
case TiffColorType.Rgb101010:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -146,6 +242,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
+ case TiffColorType.Rgba10101010:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 10
+ && bitsPerSample.Channel2 == 10
+ && bitsPerSample.Channel1 == 10
+ && bitsPerSample.Channel0 == 10,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
case TiffColorType.Rgb121212:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -156,6 +263,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
+ case TiffColorType.Rgba12121212:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 12
+ && bitsPerSample.Channel2 == 12
+ && bitsPerSample.Channel1 == 12
+ && bitsPerSample.Channel0 == 12,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
case TiffColorType.Rgb141414:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -166,6 +284,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbTiffColor(bitsPerSample);
+ case TiffColorType.Rgba14141414:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 14
+ && bitsPerSample.Channel2 == 14
+ && bitsPerSample.Channel1 == 14
+ && bitsPerSample.Channel0 == 14,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaTiffColor(bitsPerSample);
+
case TiffColorType.Rgb161616:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -176,6 +305,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb161616TiffColor(configuration, isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba16161616:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 16
+ && bitsPerSample.Channel2 == 16
+ && bitsPerSample.Channel1 == 16
+ && bitsPerSample.Channel0 == 16,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba16161616TiffColor(configuration, isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.Rgb242424:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -186,6 +326,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb242424TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba24242424:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 24
+ && bitsPerSample.Channel2 == 24
+ && bitsPerSample.Channel1 == 24
+ && bitsPerSample.Channel0 == 24,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba24242424TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.Rgb323232:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -196,6 +347,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba32323232:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 32
+ && bitsPerSample.Channel2 == 32
+ && bitsPerSample.Channel1 == 32
+ && bitsPerSample.Channel0 == 32,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba32323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.RgbFloat323232:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
@@ -206,6 +368,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbFloat323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.RgbaFloat32323232:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 4
+ && bitsPerSample.Channel3 == 32
+ && bitsPerSample.Channel2 == 32
+ && bitsPerSample.Channel1 == 32
+ && bitsPerSample.Channel0 == 32,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaFloat32323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.PaletteColor:
DebugGuard.NotNull(colorMap, "colorMap");
return new PaletteTiffColor(bitsPerSample, colorMap);
@@ -233,6 +406,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new RgbPlanarTiffColor(bitsPerSample);
+ case TiffColorType.Rgba8888Planar:
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new RgbaPlanarTiffColor(bitsPerSample);
+
case TiffColorType.YCbCrPlanar:
return new YCbCrPlanarTiffColor(referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling);
@@ -240,14 +417,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb16PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba16161616Planar:
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba16PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.Rgb242424Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb24PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba24242424Planar:
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba24PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.Rgb323232Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb32PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgba32323232Planar:
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgba32PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+
default:
throw TiffThrowHelper.InvalidColorType(colorType.ToString());
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
index 8caefaed56..6b39224fbd 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -103,71 +103,171 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
Rgb222,
+ ///
+ /// RGBA color image with 2 bits for each channel.
+ ///
+ Rgba2222,
+
+ ///
+ /// RGB color image with 3 bits for each channel.
+ ///
+ Rgb333,
+
+ ///
+ /// RGBA color image with 3 bits for each channel.
+ ///
+ Rgba3333,
+
///
/// RGB color image with 4 bits for each channel.
///
Rgb444,
+ ///
+ /// RGBA color image with 4 bits for each channel.
+ ///
+ Rgba4444,
+
+ ///
+ /// RGB color image with 5 bits for each channel.
+ ///
+ Rgb555,
+
+ ///
+ /// RGBA color image with 5 bits for each channel.
+ ///
+ Rgba5555,
+
+ ///
+ /// RGB color image with 6 bits for each channel.
+ ///
+ Rgb666,
+
+ ///
+ /// RGBA color image with 6 bits for each channel.
+ ///
+ Rgba6666,
+
///
/// RGB Full Color. Optimized implementation for 8-bit images.
///
Rgb888,
+ ///
+ /// RGBA Full Color with 8-bit for each channel.
+ ///
+ Rgba8888,
+
///
/// RGB color image with 10 bits for each channel.
///
Rgb101010,
+ ///
+ /// RGBA color image with 10 bits for each channel.
+ ///
+ Rgba10101010,
+
///
/// RGB color image with 12 bits for each channel.
///
Rgb121212,
+ ///
+ /// RGBA color image with 12 bits for each channel.
+ ///
+ Rgba12121212,
+
///
/// RGB color image with 14 bits for each channel.
///
Rgb141414,
+ ///
+ /// RGBA color image with 14 bits for each channel.
+ ///
+ Rgba14141414,
+
///
/// RGB color image with 16 bits for each channel.
///
Rgb161616,
+ ///
+ /// RGBA color image with 16 bits for each channel.
+ ///
+ Rgba16161616,
+
///
/// RGB color image with 24 bits for each channel.
///
Rgb242424,
+ ///
+ /// RGBA color image with 24 bits for each channel.
+ ///
+ Rgba24242424,
+
///
/// RGB color image with 32 bits for each channel.
///
Rgb323232,
+ ///
+ /// RGBA color image with 32 bits for each channel.
+ ///
+ Rgba32323232,
+
///
/// RGB color image with 32 bits floats for each channel.
///
RgbFloat323232,
+ ///
+ /// RGBA color image with 32 bits floats for each channel.
+ ///
+ RgbaFloat32323232,
+
///
/// RGB Full Color. Planar configuration of data. 8 Bit per color channel.
///
Rgb888Planar,
+ ///
+ /// RGBA color image with an alpha channel. Planar configuration of data. 8 Bit per color channel.
+ ///
+ Rgba8888Planar,
+
///
/// RGB Full Color. Planar configuration of data. 16 Bit per color channel.
///
Rgb161616Planar,
+ ///
+ /// RGB Color with an alpha channel. Planar configuration of data. 16 Bit per color channel.
+ ///
+ Rgba16161616Planar,
+
///
/// RGB Full Color. Planar configuration of data. 24 Bit per color channel.
///
Rgb242424Planar,
+ ///
+ /// RGB Color with an alpha channel. Planar configuration of data. 24 Bit per color channel.
+ ///
+ Rgba24242424Planar,
+
///
/// RGB Full Color. Planar configuration of data. 32 Bit per color channel.
///
Rgb323232Planar,
+ ///
+ /// RGB Color with an alpha channel. Planar configuration of data. 32 Bit per color channel.
+ ///
+ Rgba32323232Planar,
+
///
/// The pixels are stored in YCbCr format.
///
diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md
index 51e84ef558..aa960b373c 100644
--- a/src/ImageSharp/Formats/Tiff/README.md
+++ b/src/ImageSharp/Formats/Tiff/README.md
@@ -105,7 +105,7 @@
|Artist | Y | Y | |
|HostComputer | Y | Y | |
|ColorMap | Y | Y | |
-|ExtraSamples | | - | |
+|ExtraSamples | | (Y) | Only UnassociatedAlphaData is supported so far |
|Copyright | Y | Y | |
### Extension TIFF Tags
diff --git a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
index 8fd26ac13d..9348a68839 100644
--- a/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
@@ -25,6 +25,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
public readonly ushort Channel2;
+ ///
+ /// The bits for the alpha channel.
+ ///
+ public readonly ushort Channel3;
+
///
/// The number of channels.
///
@@ -36,16 +41,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// The bits for the channel 0.
/// The bits for the channel 1.
/// The bits for the channel 2.
- public TiffBitsPerSample(ushort channel0, ushort channel1, ushort channel2)
+ /// The bits for the channel 3.
+ public TiffBitsPerSample(ushort channel0, ushort channel1, ushort channel2, ushort channel3 = 0)
{
this.Channel0 = (ushort)Numerics.Clamp(channel0, 0, 32);
this.Channel1 = (ushort)Numerics.Clamp(channel1, 0, 32);
this.Channel2 = (ushort)Numerics.Clamp(channel2, 0, 32);
+ this.Channel3 = (ushort)Numerics.Clamp(channel3, 0, 32);
this.Channels = 0;
this.Channels += (byte)(this.Channel0 != 0 ? 1 : 0);
this.Channels += (byte)(this.Channel1 != 0 ? 1 : 0);
this.Channels += (byte)(this.Channel2 != 0 ? 1 : 0);
+ this.Channels += (byte)(this.Channel3 != 0 ? 1 : 0);
}
///
@@ -62,11 +70,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return false;
}
+ ushort c3 = 0;
ushort c2;
ushort c1;
ushort c0;
switch (value.Length)
{
+ case 4:
+ c3 = value[3];
+ c2 = value[2];
+ c1 = value[1];
+ c0 = value[0];
+ break;
+
case 3:
c2 = value[2];
c1 = value[1];
@@ -84,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
break;
}
- sample = new TiffBitsPerSample(c0, c1, c2);
+ sample = new TiffBitsPerSample(c0, c1, c2, c3);
return true;
}
@@ -96,11 +112,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public bool Equals(TiffBitsPerSample other)
=> this.Channel0 == other.Channel0
&& this.Channel1 == other.Channel1
- && this.Channel2 == other.Channel2;
+ && this.Channel2 == other.Channel2
+ && this.Channel3 == other.Channel3;
///
public override int GetHashCode()
- => HashCode.Combine(this.Channel0, this.Channel1, this.Channel2);
+ => HashCode.Combine(this.Channel0, this.Channel1, this.Channel2, this.Channel3);
///
/// Converts the bits per sample struct to an ushort array.
@@ -118,7 +135,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return new[] { this.Channel0, this.Channel1 };
}
- return new[] { this.Channel0, this.Channel1, this.Channel2 };
+ if (this.Channel3 == 0)
+ {
+ return new[] { this.Channel0, this.Channel1, this.Channel2 };
+ }
+
+ return new[] { this.Channel0, this.Channel1, this.Channel2, this.Channel3 };
}
///
@@ -127,12 +149,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// Bits per pixel.
public TiffBitsPerPixel BitsPerPixel()
{
- int bitsPerPixel = this.Channel0 + this.Channel1 + this.Channel2;
+ int bitsPerPixel = this.Channel0 + this.Channel1 + this.Channel2 + this.Channel3;
return (TiffBitsPerPixel)bitsPerPixel;
}
///
public override string ToString()
- => $"TiffBitsPerSample({this.Channel0}, {this.Channel1}, {this.Channel2})";
+ => this.Channel3 is 0 ?
+ $"TiffBitsPerSample({this.Channel0}, {this.Channel1}, {this.Channel2})"
+ : $"TiffBitsPerSample({this.Channel0}, {this.Channel1}, {this.Channel2}, {this.Channel3})";
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index cd06282f18..e5b810738a 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
/// Gets the decoding mode for multi-frame images
///
- private FrameDecodingMode decodingMode;
+ private readonly FrameDecodingMode decodingMode;
///
/// The stream to decode from.
@@ -117,6 +117,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
///
public TiffFillOrder FillOrder { get; set; }
+ ///
+ /// Gets or sets the extra samples, which can contain the alpha channel data.
+ ///
+ public TiffExtraSampleType? ExtraSamples { get; set; }
+
///
/// Gets or sets the JPEG tables when jpeg compression is used.
///
@@ -275,12 +280,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return memory;
}
- else
- {
- DebugGuard.IsTrue(array is ulong[], $"Expected {nameof(UInt64)} array.");
- span = (ulong[])array;
- return null;
- }
+
+ DebugGuard.IsTrue(array is ulong[], $"Expected {nameof(UInt64)} array.");
+ span = (ulong[])array;
+ return null;
}
///
@@ -314,8 +317,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case 2:
bitsPerPixel = this.BitsPerSample.Channel2;
break;
+ case 3:
+ bitsPerPixel = this.BitsPerSample.Channel2;
+ break;
default:
- TiffThrowHelper.ThrowNotSupported("More then 3 color channels are not supported");
+ TiffThrowHelper.ThrowNotSupported("More then 4 color channels are not supported");
break;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index a187d444ac..23bc5f15f2 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -29,9 +29,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffThrowHelper.ThrowNotSupported("Tiled images are not supported.");
}
- if (exifProfile.GetValueInternal(ExifTag.ExtraSamples) is not null)
+ IExifValue extraSamplesExifValue = exifProfile.GetValueInternal(ExifTag.ExtraSamples);
+ if (extraSamplesExifValue is not null)
{
- TiffThrowHelper.ThrowNotSupported("ExtraSamples is not supported.");
+ short[] extraSamples = (short[])extraSamplesExifValue.GetValue();
+ if (extraSamples.Length != 1)
+ {
+ TiffThrowHelper.ThrowNotSupported("ExtraSamples is only supported with one extra sample for alpha data.");
+ }
+
+ var extraSamplesType = (TiffExtraSampleType)extraSamples[0];
+ options.ExtraSamples = extraSamplesType;
+ if (extraSamplesType is not TiffExtraSampleType.UnassociatedAlphaData)
+ {
+ TiffThrowHelper.ThrowNotSupported("Decoding Tiff images with ExtraSamples is only supported with UnassociatedAlphaData.");
+ }
}
TiffFillOrder fillOrder = (TiffFillOrder?)exifProfile.GetValue(ExifTag.FillOrder)?.Value ?? TiffFillOrder.MostSignificantBitFirst;
@@ -52,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
sampleFormat = sampleFormats[0];
foreach (TiffSampleFormat format in sampleFormats)
{
- if (format != TiffSampleFormat.UnsignedInteger && format != TiffSampleFormat.Float)
+ if (format is not TiffSampleFormat.UnsignedInteger and not TiffSampleFormat.Float)
{
TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger and Float SampleFormat.");
}
@@ -252,12 +264,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffPhotometricInterpretation.Rgb:
{
TiffBitsPerSample bitsPerSample = options.BitsPerSample;
- if (bitsPerSample.Channels != 3)
+ if (bitsPerSample.Channels is not (3 or 4))
{
TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
}
- if (!(bitsPerSample.Channel0 == bitsPerSample.Channel1 && bitsPerSample.Channel1 == bitsPerSample.Channel2))
+ if ((bitsPerSample.Channels == 3 && !(bitsPerSample.Channel0 == bitsPerSample.Channel1 && bitsPerSample.Channel1 == bitsPerSample.Channel2)) ||
+ (bitsPerSample.Channels == 4 && !(bitsPerSample.Channel0 == bitsPerSample.Channel1 && bitsPerSample.Channel1 == bitsPerSample.Channel2 && bitsPerSample.Channel2 == bitsPerSample.Channel3)))
{
TiffThrowHelper.ThrowNotSupported("Only BitsPerSample with equal bits per channel are supported.");
}
@@ -270,41 +283,50 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case 32:
if (options.SampleFormat == TiffSampleFormat.Float)
{
- options.ColorType = TiffColorType.RgbFloat323232;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.RgbFloat323232 : TiffColorType.RgbaFloat32323232;
return;
}
- options.ColorType = TiffColorType.Rgb323232;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb323232 : TiffColorType.Rgba32323232;
break;
case 24:
- options.ColorType = TiffColorType.Rgb242424;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb242424 : TiffColorType.Rgba24242424;
break;
case 16:
- options.ColorType = TiffColorType.Rgb161616;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb161616 : TiffColorType.Rgba16161616;
break;
case 14:
- options.ColorType = TiffColorType.Rgb141414;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb141414 : TiffColorType.Rgba14141414;
break;
case 12:
- options.ColorType = TiffColorType.Rgb121212;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb121212 : TiffColorType.Rgba12121212;
break;
case 10:
- options.ColorType = TiffColorType.Rgb101010;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb101010 : TiffColorType.Rgba10101010;
break;
case 8:
- options.ColorType = TiffColorType.Rgb888;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb888 : TiffColorType.Rgba8888;
+ break;
+ case 6:
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb666 : TiffColorType.Rgba6666;
+ break;
+ case 5:
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb555 : TiffColorType.Rgba5555;
break;
case 4:
- options.ColorType = TiffColorType.Rgb444;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb444 : TiffColorType.Rgba4444;
+ break;
+ case 3:
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb333 : TiffColorType.Rgba3333;
break;
case 2:
- options.ColorType = TiffColorType.Rgb222;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb222 : TiffColorType.Rgba2222;
break;
default:
TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported.");
@@ -317,16 +339,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (bitsPerChannel)
{
case 32:
- options.ColorType = TiffColorType.Rgb323232Planar;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb323232Planar : TiffColorType.Rgba32323232Planar;
break;
case 24:
- options.ColorType = TiffColorType.Rgb242424Planar;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb242424Planar : TiffColorType.Rgba24242424Planar;
break;
case 16:
- options.ColorType = TiffColorType.Rgb161616Planar;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb161616Planar : TiffColorType.Rgba16161616Planar;
break;
default:
- options.ColorType = TiffColorType.Rgb888Planar;
+ options.ColorType = options.BitsPerSample.Channels is 3 ? TiffColorType.Rgb888Planar : TiffColorType.Rgba8888Planar;
break;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs b/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs
new file mode 100644
index 0000000000..5fbc29177d
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/TiffExtraSampleType.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Tiff
+{
+ ///
+ /// Description of extra components.
+ ///
+ internal enum TiffExtraSampleType
+ {
+ ///
+ /// The data is unspecified, not supported.
+ ///
+ UnspecifiedData = 0,
+
+ ///
+ /// The extra data is associated alpha data (with pre-multiplied color).
+ ///
+ AssociatedAlphaData = 1,
+
+ ///
+ /// The extra data is unassociated alpha data is transparency information that logically exists independent of an image;
+ /// it is commonly called a soft matte.
+ ///
+ UnassociatedAlphaData = 2
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
index 4f71fa35c9..b7d4b6e7cc 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
@@ -18,11 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
private const float Scale32Bit = 1.0f / 0xFFFFFFFF;
- public static Vector4 Vector4Default { get; } = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
+ public static Vector4 Vector4Default { get; } = new(0.0f, 0.0f, 0.0f, 0.0f);
- public static Rgba64 Rgba64Default { get; } = new Rgba64(0, 0, 0, 0);
+ public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0);
- public static L16 L16Default { get; } = new L16(0);
+ public static L16 L16Default { get; } = new(0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort ConvertToUShortBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer);
@@ -54,6 +54,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorFromRgba64(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48);
+ color.FromRgba64(rgba);
+ return color;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel
@@ -63,6 +72,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ var colorVector = new Vector4(r * Scale24Bit, g * Scale24Bit, b * Scale24Bit, a * Scale24Bit);
+ color.FromVector4(colorVector);
+ return color;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel
@@ -72,6 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ var colorVector = new Vector4(r * Scale32Bit, g * Scale32Bit, b * Scale32Bit, a * Scale32Bit);
+ color.FromVector4(colorVector);
+ return color;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color)
where TPixel : unmanaged, IPixel
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index fe037003e3..716004f39c 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -362,10 +362,8 @@ namespace SixLabors.ImageSharp
return;
}
- this.PixelBuffer.FastMemoryGroup.TransformTo(destination, (s, d) =>
- {
- PixelOperations.Instance.To(this.GetConfiguration(), s, d);
- });
+ this.PixelBuffer.FastMemoryGroup.TransformTo(destination, (s, d)
+ => PixelOperations.Instance.To(this.GetConfiguration(), s, d));
}
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs
index 3fc353211d..286b31786e 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs
@@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Gets the MDFileUnits exif tag.
///
- public static ExifTag MDFileUnits => new ExifTag(ExifTagValue.MDFileUnits);
+ public static ExifTag MDFileUnits { get; } = new ExifTag(ExifTagValue.MDFileUnits);
///
/// Gets the SEMInfo exif tag.
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs
index c66526bbe5..e78a644af3 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/PixelOperations/RgbaVector.PixelOperations.cs
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.PixelFormats
internal class PixelOperations : PixelOperations
{
private static readonly Lazy LazyInfo =
- new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true);
+ new(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), true);
///
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value;
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
{
Span destinationVectors = MemoryMarshal.Cast(destinationPixels);
- PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors);
+ PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.Scale);
}
///
diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
index f748a4b574..710eb9c083 100644
--- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.PixelFormats
public partial class PixelOperations
where TPixel : unmanaged, IPixel
{
- private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(), true);
+ private static readonly Lazy LazyInfo = new(() => PixelTypeInfo.Create(), true);
///
/// Gets the global instance for the pixel type
@@ -116,29 +116,29 @@ namespace SixLabors.ImageSharp.PixelFormats
Span destinationPixels)
where TSourcePixel : unmanaged, IPixel
{
- const int SliceLength = 1024;
- int numberOfSlices = sourcePixels.Length / SliceLength;
+ const int sliceLength = 1024;
+ int numberOfSlices = sourcePixels.Length / sliceLength;
- using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(SliceLength);
+ using IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(sliceLength);
Span vectorSpan = tempVectors.GetSpan();
for (int i = 0; i < numberOfSlices; i++)
{
- int start = i * SliceLength;
- ReadOnlySpan s = sourcePixels.Slice(start, SliceLength);
- Span d = destinationPixels.Slice(start, SliceLength);
- PixelOperations.Instance.ToVector4(configuration, s, vectorSpan);
- this.FromVector4Destructive(configuration, vectorSpan, d);
+ int start = i * sliceLength;
+ ReadOnlySpan s = sourcePixels.Slice(start, sliceLength);
+ Span d = destinationPixels.Slice(start, sliceLength);
+ PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale);
+ this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale);
}
- int endOfCompleteSlices = numberOfSlices * SliceLength;
+ int endOfCompleteSlices = numberOfSlices * sliceLength;
int remainder = sourcePixels.Length - endOfCompleteSlices;
if (remainder > 0)
{
ReadOnlySpan s = sourcePixels.Slice(endOfCompleteSlices);
Span d = destinationPixels.Slice(endOfCompleteSlices);
vectorSpan = vectorSpan.Slice(0, remainder);
- PixelOperations.Instance.ToVector4(configuration, s, vectorSpan);
- this.FromVector4Destructive(configuration, vectorSpan, d);
+ PixelOperations.Instance.ToVector4(configuration, s, vectorSpan, PixelConversionModifiers.Scale);
+ this.FromVector4Destructive(configuration, vectorSpan, d, PixelConversionModifiers.Scale);
}
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
deleted file mode 100644
index 0e9bed1d9e..0000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Drawing.Imaging;
-using System.IO;
-using BenchmarkDotNet.Attributes;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Tests;
-using SDImage = System.Drawing.Image;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
-{
- public class EncodeJpeg
- {
- [Params(75, 90, 100)]
- public int Quality;
-
- private const string TestImage = TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr;
-
- // System.Drawing
- private SDImage bmpDrawing;
- private Stream bmpStream;
- private ImageCodecInfo jpegCodec;
- private EncoderParameters encoderParameters;
-
- // ImageSharp
- private Image bmpCore;
- private JpegEncoder encoder420;
- private JpegEncoder encoder444;
-
- private MemoryStream destinationStream;
-
- [GlobalSetup]
- public void ReadImages()
- {
- if (this.bmpStream == null)
- {
- this.bmpStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
-
- this.bmpCore = Image.Load(this.bmpStream);
- this.bmpCore.Metadata.ExifProfile = null;
- this.encoder420 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
- this.encoder444 = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio444 };
-
- this.bmpStream.Position = 0;
- this.bmpDrawing = SDImage.FromStream(this.bmpStream);
- this.jpegCodec = GetEncoder(ImageFormat.Jpeg);
- this.encoderParameters = new EncoderParameters(1);
-
- // Quality cast to long is necessary
-#pragma warning disable IDE0004 // Remove Unnecessary Cast
- this.encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)this.Quality);
-#pragma warning restore IDE0004 // Remove Unnecessary Cast
-
- this.destinationStream = new MemoryStream();
- }
- }
-
- [GlobalCleanup]
- public void Cleanup()
- {
- this.bmpStream.Dispose();
- this.bmpStream = null;
-
- this.destinationStream.Dispose();
- this.destinationStream = null;
-
- this.bmpCore.Dispose();
- this.bmpDrawing.Dispose();
-
- this.encoderParameters.Dispose();
- }
-
- [Benchmark(Baseline = true, Description = "System.Drawing Jpeg 4:2:0")]
- public void JpegSystemDrawing()
- {
- this.bmpDrawing.Save(this.destinationStream, this.jpegCodec, this.encoderParameters);
- this.destinationStream.Seek(0, SeekOrigin.Begin);
- }
-
- [Benchmark(Description = "ImageSharp Jpeg 4:2:0")]
- public void JpegCore420()
- {
- this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder420);
- this.destinationStream.Seek(0, SeekOrigin.Begin);
- }
-
- [Benchmark(Description = "ImageSharp Jpeg 4:4:4")]
- public void JpegCore444()
- {
- this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder444);
- this.destinationStream.Seek(0, SeekOrigin.Begin);
- }
-
- // https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.encoderparameter?redirectedfrom=MSDN&view=net-5.0
- private static ImageCodecInfo GetEncoder(ImageFormat format)
- {
- ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
- foreach (ImageCodecInfo codec in codecs)
- {
- if (codec.FormatID == format.Guid)
- {
- return codec;
- }
- }
-
- return null;
- }
- }
-}
-
-/*
-BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042
-Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
-.NET SDK=6.0.100-preview.3.21202.5
- [Host] : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT
- DefaultJob : .NET Core 3.1.18 (CoreCLR 4.700.21.35901, CoreFX 4.700.21.36305), X64 RyuJIT
-
-
-| Method | Quality | Mean | Error | StdDev | Ratio |
-|---------------------------- |-------- |---------:|---------:|---------:|------:|
-| 'System.Drawing Jpeg 4:2:0' | 75 | 30.04 ms | 0.540 ms | 0.479 ms | 1.00 |
-| 'ImageSharp Jpeg 4:2:0' | 75 | 19.32 ms | 0.290 ms | 0.257 ms | 0.64 |
-| 'ImageSharp Jpeg 4:4:4' | 75 | 26.76 ms | 0.332 ms | 0.294 ms | 0.89 |
-| | | | | | |
-| 'System.Drawing Jpeg 4:2:0' | 90 | 32.82 ms | 0.184 ms | 0.163 ms | 1.00 |
-| 'ImageSharp Jpeg 4:2:0' | 90 | 25.00 ms | 0.408 ms | 0.361 ms | 0.76 |
-| 'ImageSharp Jpeg 4:4:4' | 90 | 31.83 ms | 0.636 ms | 0.595 ms | 0.97 |
-| | | | | | |
-| 'System.Drawing Jpeg 4:2:0' | 100 | 39.30 ms | 0.359 ms | 0.318 ms | 1.00 |
-| 'ImageSharp Jpeg 4:2:0' | 100 | 34.49 ms | 0.265 ms | 0.235 ms | 0.88 |
-| 'ImageSharp Jpeg 4:4:4' | 100 | 56.40 ms | 0.565 ms | 0.501 ms | 1.44 |
-*/
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs
new file mode 100644
index 0000000000..2c4686eddf
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegComparison.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Drawing.Imaging;
+using System.IO;
+using BenchmarkDotNet.Attributes;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests;
+using SkiaSharp;
+using SDImage = System.Drawing.Image;
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ ///
+ /// Benchmark for performance comparison between other codecs.
+ ///
+ ///
+ /// This benchmarks tests baseline 4:2:0 chroma sampling path.
+ ///
+ public class EncodeJpegComparison
+ {
+ // Big enough, 4:4:4 chroma sampling
+ private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
+
+ // Change/add parameters for extra benchmarks
+ [Params(75, 90, 100)]
+ public int Quality;
+
+ private MemoryStream destinationStream;
+
+ // ImageSharp
+ private Image imageImageSharp;
+ private JpegEncoder encoderImageSharp;
+
+ // SkiaSharp
+ private SKBitmap imageSkiaSharp;
+
+ [GlobalSetup(Target = nameof(BenchmarkImageSharp))]
+ public void SetupImageSharp()
+ {
+ using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
+
+ this.imageImageSharp = Image.Load(imageBinaryStream);
+ this.encoderImageSharp = new JpegEncoder { Quality = this.Quality, ColorType = JpegColorType.YCbCrRatio420 };
+
+ this.destinationStream = new MemoryStream();
+ }
+
+ [GlobalCleanup(Target = nameof(BenchmarkImageSharp))]
+ public void CleanupImageSharp()
+ {
+ this.imageImageSharp.Dispose();
+ this.imageImageSharp = null;
+
+ this.destinationStream.Dispose();
+ this.destinationStream = null;
+ }
+
+ [Benchmark(Description = "ImageSharp")]
+ public void BenchmarkImageSharp()
+ {
+ this.imageImageSharp.SaveAsJpeg(this.destinationStream, this.encoderImageSharp);
+ this.destinationStream.Seek(0, SeekOrigin.Begin);
+ }
+
+ [GlobalSetup(Target = nameof(BenchmarkSkiaSharp))]
+ public void SetupSkiaSharp()
+ {
+ using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
+
+ this.imageSkiaSharp = SKBitmap.Decode(imageBinaryStream);
+
+ this.destinationStream = new MemoryStream();
+ }
+
+ [GlobalCleanup(Target = nameof(BenchmarkSkiaSharp))]
+ public void CleanupSkiaSharp()
+ {
+ this.imageSkiaSharp.Dispose();
+ this.imageSkiaSharp = null;
+
+ this.destinationStream.Dispose();
+ this.destinationStream = null;
+ }
+
+ [Benchmark(Description = "SkiaSharp")]
+ public void BenchmarkSkiaSharp()
+ {
+ this.imageSkiaSharp.Encode(SKEncodedImageFormat.Jpeg, this.Quality).SaveTo(this.destinationStream);
+ this.destinationStream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+}
+
+/*
+BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044
+Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
+.NET SDK=6.0.100-preview.3.21202.5
+ [Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
+ DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
+
+
+| Method | Quality | Mean | Error | StdDev |
+|----------- |-------- |----------:|----------:|----------:|
+| ImageSharp | 75 | 6.820 ms | 0.0374 ms | 0.0312 ms |
+| SkiaSharp | 75 | 16.417 ms | 0.3238 ms | 0.4747 ms |
+| ImageSharp | 90 | 7.849 ms | 0.1565 ms | 0.3126 ms |
+| SkiaSharp | 90 | 16.893 ms | 0.2200 ms | 0.2058 ms |
+| ImageSharp | 100 | 11.016 ms | 0.2087 ms | 0.1850 ms |
+| SkiaSharp | 100 | 20.410 ms | 0.2583 ms | 0.2290 ms |
+*/
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs
new file mode 100644
index 0000000000..83fb556d5b
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegFeatures.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+using System.IO;
+using BenchmarkDotNet.Attributes;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Tests;
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
+{
+ ///
+ /// Benchmark for all available encoding features of the Jpeg file type.
+ ///
+ ///
+ /// This benchmark does NOT compare ImageSharp to any other jpeg codecs.
+ ///
+ public class EncodeJpegFeatures
+ {
+ // Big enough, 4:4:4 chroma sampling
+ // No metadata
+ private const string TestImage = TestImages.Jpeg.Baseline.Calliphora;
+
+ public static IEnumerable ColorSpaceValues =>
+ new[] { JpegColorType.Luminance, JpegColorType.Rgb, JpegColorType.YCbCrRatio420, JpegColorType.YCbCrRatio444 };
+
+ [Params(75, 90, 100)]
+ public int Quality;
+
+ [ParamsSource(nameof(ColorSpaceValues), Priority = -100)]
+ public JpegColorType TargetColorSpace;
+
+ private Image bmpCore;
+ private JpegEncoder encoder;
+
+ private MemoryStream destinationStream;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ using FileStream imageBinaryStream = File.OpenRead(Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, TestImage));
+ this.bmpCore = Image.Load(imageBinaryStream);
+ this.encoder = new JpegEncoder { Quality = this.Quality, ColorType = this.TargetColorSpace };
+ this.destinationStream = new MemoryStream();
+ }
+
+ [GlobalCleanup]
+ public void Cleanup()
+ {
+ this.bmpCore.Dispose();
+ this.bmpCore = null;
+
+ this.destinationStream.Dispose();
+ this.destinationStream = null;
+ }
+
+ [Benchmark]
+ public void Benchmark()
+ {
+ this.bmpCore.SaveAsJpeg(this.destinationStream, this.encoder);
+ this.destinationStream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+}
+
+/*
+BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19044
+Intel Core i7-6700K CPU 4.00GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
+.NET SDK=6.0.100-preview.3.21202.5
+ [Host] : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
+ DefaultJob : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
+
+
+| Method | TargetColorSpace | Quality | Mean | Error | StdDev |
+|---------- |----------------- |-------- |----------:|----------:|----------:|
+| Benchmark | Luminance | 75 | 7.055 ms | 0.1411 ms | 0.3297 ms |
+| Benchmark | Rgb | 75 | 12.139 ms | 0.0645 ms | 0.0538 ms |
+| Benchmark | YCbCrRatio420 | 75 | 6.463 ms | 0.0282 ms | 0.0235 ms |
+| Benchmark | YCbCrRatio444 | 75 | 8.616 ms | 0.0422 ms | 0.0374 ms |
+| Benchmark | Luminance | 90 | 7.011 ms | 0.0361 ms | 0.0301 ms |
+| Benchmark | Rgb | 90 | 13.119 ms | 0.0947 ms | 0.0886 ms |
+| Benchmark | YCbCrRatio420 | 90 | 6.786 ms | 0.0328 ms | 0.0274 ms |
+| Benchmark | YCbCrRatio444 | 90 | 8.672 ms | 0.0772 ms | 0.0722 ms |
+| Benchmark | Luminance | 100 | 9.554 ms | 0.1211 ms | 0.1012 ms |
+| Benchmark | Rgb | 100 | 19.475 ms | 0.1080 ms | 0.0958 ms |
+| Benchmark | YCbCrRatio420 | 100 | 10.146 ms | 0.0585 ms | 0.0519 ms |
+| Benchmark | YCbCrRatio444 | 100 | 15.317 ms | 0.0709 ms | 0.0592 ms |
+*/
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegMultiple.cs
deleted file mode 100644
index 71276597a7..0000000000
--- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpegMultiple.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Collections.Generic;
-using System.Drawing.Imaging;
-using BenchmarkDotNet.Attributes;
-using SixLabors.ImageSharp.Formats.Jpeg;
-
-namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg
-{
- [Config(typeof(Config.ShortMultiFramework))]
- public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded
- {
- protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" };
-
- protected override IEnumerable SearchPatterns => new[] { "*.bmp", "*.jpg" };
-
- [Benchmark(Description = "EncodeJpegMultiple - ImageSharp")]
- public void EncodeJpegImageSharp()
- => this.ForEachImageSharpImage((img, ms) =>
- {
- img.Save(ms, new JpegEncoder());
- return null;
- });
-
- [Benchmark(Baseline = true, Description = "EncodeJpegMultiple - System.Drawing")]
- public void EncodeJpegSystemDrawing()
- => this.ForEachSystemDrawingImage((img, ms) =>
- {
- img.Save(ms, ImageFormat.Jpeg);
- return null;
- });
- }
-}
diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs
index 2997848211..60d0e76613 100644
--- a/tests/ImageSharp.Benchmarks/Config.cs
+++ b/tests/ImageSharp.Benchmarks/Config.cs
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public MultiFramework() => this.AddJob(
Job.Default.WithRuntime(ClrRuntime.Net472),
Job.Default.WithRuntime(CoreRuntime.Core31),
- Job.Default.WithRuntime(CoreRuntime.Core50).With(new Argument[] { new MsBuildArgument("/p:DebugType=portable") }));
+ Job.Default.WithRuntime(CoreRuntime.Core50).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") }));
}
public class ShortMultiFramework : Config
@@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Benchmarks
public ShortMultiFramework() => this.AddJob(
Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3),
Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3),
- Job.Default.WithRuntime(CoreRuntime.Core50).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).With(new Argument[] { new MsBuildArgument("/p:DebugType=portable") }));
+ Job.Default.WithRuntime(CoreRuntime.Core50).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") }));
}
public class ShortCore31 : Config
diff --git a/tests/ImageSharp.Tests.ruleset b/tests/ImageSharp.Tests.ruleset
index 673f005170..50c275cd76 100644
--- a/tests/ImageSharp.Tests.ruleset
+++ b/tests/ImageSharp.Tests.ruleset
@@ -1,6 +1,9 @@
-
+
+
+
+
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index e4f4d26b85..bd300f799a 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -78,9 +78,42 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(FlowerRgb888Planar6Strips, PixelTypes.Rgba32)]
[WithFile(FlowerRgb888Planar15Strips, PixelTypes.Rgba32)]
- public void TiffDecoder_Planar(TestImageProvider provider)
+ public void TiffDecoder_CanDecode_Planar(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba8BitPlanarUnassociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_Planar_32Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(Rgba16BitPlanarUnassociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba16BitPlanarUnassociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_Planar_64Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(Rgba24BitPlanarUnassociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba24BitPlanarUnassociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_Planar_96Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ // Note: because the MagickReferenceDecoder fails to load the image, we only debug save them.
+ using Image image = provider.GetImage();
+ image.DebugSave(provider);
+ }
+
+ [Theory]
+ [WithFile(Rgba32BitPlanarUnassociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba32BitPlanarUnassociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_Planar_128Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ // Note: because the MagickReferenceDecoder fails to load the image, we only debug save them.
+ using Image image = provider.GetImage();
+ image.DebugSave(provider);
+ }
+
[Theory]
[WithFile(Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
[WithFile(PaletteDeflateMultistrip, PixelTypes.Rgba32)]
@@ -123,6 +156,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_8Bit_Gray(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba2BitUnassociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_8Bit_WithUnassociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
+ [Theory]
+ [WithFile(FLowerRgb3Bit, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_9Bit_WithUnassociatedAlpha