diff --git a/Directory.Build.props b/Directory.Build.props
index 12a4a5c2a3..50c09fbb3c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -31,21 +31,21 @@
- $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE
+ $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_RUNTIME_INTRINSICS;SUPPORTS_CODECOVERAGE;SUPPORTS_HOTPATH$(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING;SUPPORTS_CODECOVERAGE
diff --git a/Directory.Build.targets b/Directory.Build.targets
index e5c44f7761..0f02d7e320 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -18,7 +18,7 @@
-
+
diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index 0273f02f56..a79733fec3 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -78,84 +78,6 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : unmanaged, IPixel
=> source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source));
- ///
- /// Gets the representation of the pixels as a in the source image's pixel format
- /// stored in row major order, if the backing buffer is contiguous.
- ///
- /// The type of the pixel.
- /// The source image.
- /// The
- /// Thrown when the backing buffer is discontiguous.
- [Obsolete(
- @"GetPixelSpan might fail, because the backing buffer could be discontiguous for large images. Use GetPixelMemoryGroup or GetPixelRowSpan instead!")]
- public static Span GetPixelSpan(this ImageFrame source)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(source, nameof(source));
-
- IMemoryGroup mg = source.GetPixelMemoryGroup();
- if (mg.Count > 1)
- {
- throw new InvalidOperationException($"GetPixelSpan is invalid, since the backing buffer of this {source.Width}x{source.Height} sized image is discontiguous!");
- }
-
- return mg.Single().Span;
- }
-
- ///
- /// Gets the representation of the pixels as a of contiguous memory in the source image's pixel format
- /// stored in row major order.
- ///
- /// The type of the pixel.
- /// The source.
- /// The
- /// Thrown when the backing buffer is discontiguous.
- [Obsolete(
- @"GetPixelSpan might fail, because the backing buffer could be discontiguous for large images. Use GetPixelMemoryGroup or GetPixelRowSpan instead!")]
- public static Span GetPixelSpan(this Image source)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(source, nameof(source));
-
- return source.Frames.RootFrame.GetPixelSpan();
- }
-
- ///
- /// Gets the representation of the pixels as a of contiguous memory
- /// at row beginning from the the first pixel on that row.
- ///
- /// The type of the pixel.
- /// The source.
- /// The row.
- /// The
- public static Span GetPixelRowSpan(this ImageFrame source, int rowIndex)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(source, nameof(source));
- Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
- Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex));
-
- return source.PixelBuffer.GetRowSpan(rowIndex);
- }
-
- ///
- /// Gets the representation of the pixels as of of contiguous memory
- /// at row beginning from the the first pixel on that row.
- ///
- /// The type of the pixel.
- /// The source.
- /// The row.
- /// The
- public static Span GetPixelRowSpan(this Image source, int rowIndex)
- where TPixel : unmanaged, IPixel
- {
- Guard.NotNull(source, nameof(source));
- Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
- Guard.MustBeLessThan(rowIndex, source.Height, nameof(rowIndex));
-
- return source.Frames.RootFrame.PixelBuffer.GetRowSpan(rowIndex);
- }
-
///
/// Gets the representation of the pixels as a of contiguous memory
/// at row beginning from the the first pixel on that row.
diff --git a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
index 3c75a6418a..ccd0b71e54 100644
--- a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
+++ b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -10,6 +10,13 @@ namespace SixLabors.ImageSharp
///
public sealed class ImageProcessingException : Exception
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ImageProcessingException()
+ {
+ }
+
///
/// Initializes a new instance of the class with the name of the
/// parameter that causes this exception.
@@ -32,4 +39,4 @@ namespace SixLabors.ImageSharp
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs b/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs
new file mode 100644
index 0000000000..7069e89823
--- /dev/null
+++ b/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// The exception that is thrown when the library tries to load
+ /// an image which contains invalid content.
+ ///
+ public sealed class InvalidImageContentException : ImageFormatException
+ {
+ ///
+ /// Initializes a new instance of the class with the name of the
+ /// parameter that causes this exception.
+ ///
+ /// The error message that explains the reason for this exception.
+ public InvalidImageContentException(string errorMessage)
+ : base(errorMessage)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs
index 069a426d75..895b6250f6 100644
--- a/src/ImageSharp/Common/Helpers/InliningOptions.cs
+++ b/src/ImageSharp/Common/Helpers/InliningOptions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN!
@@ -13,10 +13,16 @@ namespace SixLabors.ImageSharp
internal static class InliningOptions
{
#if PROFILING
+ public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
+#if SUPPORTS_HOTPATH
+ public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
+#else
+ public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
+#endif
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 15a3ed40fd..7b3e5e44a0 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Net.Http;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
@@ -74,6 +75,12 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Gets a set of properties for the Congiguration.
+ ///
+ /// This can be used for storing global settings and defaults to be accessable to processors.
+ public IDictionary
/// The current match.
/// True if a match greater than the minimum length is found
+ [MethodImpl(InliningOptions.HotPath)]
private bool FindLongestMatch(int curMatch)
{
int match;
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
index 543a1fe302..022d3d4d0d 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
@@ -36,11 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private const int EofSymbol = 256;
- private static readonly short[] StaticLCodes;
- private static readonly byte[] StaticLLength;
- private static readonly short[] StaticDCodes;
- private static readonly byte[] StaticDLength;
-
private Tree literalTree;
private Tree distTree;
private Tree blTree;
@@ -58,49 +53,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private int extraBits;
private bool isDisposed;
- // TODO: These should be pre-generated array/readonlyspans.
- static DeflaterHuffman()
- {
- // See RFC 1951 3.2.6
- // Literal codes
- StaticLCodes = new short[LiteralNumber];
- StaticLLength = new byte[LiteralNumber];
-
- int i = 0;
- while (i < 144)
- {
- StaticLCodes[i] = BitReverse((0x030 + i) << 8);
- StaticLLength[i++] = 8;
- }
-
- while (i < 256)
- {
- StaticLCodes[i] = BitReverse((0x190 - 144 + i) << 7);
- StaticLLength[i++] = 9;
- }
-
- while (i < 280)
- {
- StaticLCodes[i] = BitReverse((0x000 - 256 + i) << 9);
- StaticLLength[i++] = 7;
- }
-
- while (i < LiteralNumber)
- {
- StaticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8);
- StaticLLength[i++] = 8;
- }
-
- // Distance codes
- StaticDCodes = new short[DistanceNumber];
- StaticDLength = new byte[DistanceNumber];
- for (i = 0; i < DistanceNumber; i++)
- {
- StaticDCodes[i] = BitReverse(i << 11);
- StaticDLength[i] = 5;
- }
- }
-
///
/// Initializes a new instance of the class.
///
@@ -122,12 +74,80 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
}
+#pragma warning disable SA1201 // Elements should appear in the correct order
+
+ // See RFC 1951 3.2.6
+ // Literal codes
+ private static readonly short[] StaticLCodes = new short[]
+ {
+ 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+ 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+ 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+ 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+ 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+ 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9,
+ 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5,
+ 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13,
+ 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 19,
+ 275, 147, 403, 83, 339, 211, 467, 51, 307, 179, 435, 115, 371, 243, 499,
+ 11, 267, 139, 395, 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491,
+ 27, 283, 155, 411, 91, 347, 219, 475, 59, 315, 187, 443, 123, 379, 251, 507,
+ 7, 263, 135, 391, 71, 327, 199, 455, 39, 295, 167, 423, 103, 359, 231, 487,
+ 23, 279, 151, 407, 87, 343, 215, 471, 55, 311, 183, 439, 119, 375, 247, 503,
+ 15, 271, 143, 399, 79, 335, 207, 463, 47, 303, 175, 431, 111, 367, 239, 495,
+ 31, 287, 159, 415, 95, 351, 223, 479, 63, 319, 191, 447, 127, 383, 255, 511,
+ 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36,
+ 100, 20, 84, 52, 116, 3, 131, 67, 195, 35, 163
+ };
+
+ private static ReadOnlySpan StaticLLength => new byte[]
+ {
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8
+ };
+
+ // Distance codes and lengths.
+ private static readonly short[] StaticDCodes = new short[]
+ {
+ 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14,
+ 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23
+ };
+
+ private static ReadOnlySpan StaticDLength => new byte[]
+ {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+ };
+#pragma warning restore SA1201 // Elements should appear in the correct order
+
///
/// Gets the lengths of the bit length codes are sent in order of decreasing probability, to avoid transmitting the lengths for unused bit length codes.
///
- private static ReadOnlySpan BitLengthOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+ private static ReadOnlySpan BitLengthOrder => new byte[]
+ {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+ };
- private static ReadOnlySpan Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+ private static ReadOnlySpan Bit4Reverse => new byte[]
+ {
+ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
+ };
///
/// Gets the pending buffer to use.
@@ -413,8 +433,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.distTree = null;
this.isDisposed = true;
}
-
- GC.SuppressFinalize(this);
}
[MethodImpl(InliningOptions.ShortMethod)]
@@ -553,6 +571,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
}
}
+ [MethodImpl(InliningOptions.HotPath)]
public void BuildTree()
{
int numSymbols = this.elementCount;
@@ -964,8 +983,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.isDisposed = true;
}
-
- GC.SuppressFinalize(this);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
index 731c9e80f0..0414ca2f87 100644
--- a/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
+++ b/src/ImageSharp/Formats/Png/Zlib/DeflaterPendingBuffer.cs
@@ -172,8 +172,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.bufferMemoryOwner = null;
this.isDisposed = true;
}
-
- GC.SuppressFinalize(this);
}
}
}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index 816a472fb2..057ec1bfc7 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -114,12 +114,12 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
if (this.fileHeader.CMapLength <= 0)
{
- TgaThrowHelper.ThrowImageFormatException("Missing tga color map length");
+ TgaThrowHelper.ThrowInvalidImageContentException("Missing tga color map length");
}
if (this.fileHeader.CMapDepth <= 0)
{
- TgaThrowHelper.ThrowImageFormatException("Missing tga color map depth");
+ TgaThrowHelper.ThrowInvalidImageContentException("Missing tga color map depth");
}
int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
@@ -898,7 +898,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
var alphaBits = this.fileHeader.ImageDescriptor & 0xf;
if (alphaBits != 0 && alphaBits != 1 && alphaBits != 8)
{
- TgaThrowHelper.ThrowImageFormatException("Invalid alpha channel bits");
+ TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits");
}
this.tgaMetadata.AlphaChannelBits = (byte)alphaBits;
diff --git a/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs b/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs
index 845d009227..1714a2025e 100644
--- a/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs
+++ b/src/ImageSharp/Formats/Tga/TgaThrowHelper.cs
@@ -9,23 +9,19 @@ namespace SixLabors.ImageSharp.Formats.Tga
internal static class TgaThrowHelper
{
///
- /// Cold path optimization for throwing -s
+ /// Cold path optimization for throwing 's
///
/// The error message for the exception.
[MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowImageFormatException(string errorMessage)
- {
- throw new ImageFormatException(errorMessage);
- }
+ public static void ThrowInvalidImageContentException(string errorMessage)
+ => throw new InvalidImageContentException(errorMessage);
///
- /// Cold path optimization for throwing -s
+ /// Cold path optimization for throwing 's
///
/// The error message for the exception.
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNotSupportedException(string errorMessage)
- {
- throw new NotSupportedException(errorMessage);
- }
+ => throw new NotSupportedException(errorMessage);
}
}
diff --git a/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs b/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs
new file mode 100644
index 0000000000..c8beea8e85
--- /dev/null
+++ b/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs
@@ -0,0 +1,100 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Processing;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Adds extensions that allow the processing of images to the type.
+ ///
+ public static class GraphicOptionsDefaultsExtensions
+ {
+ ///
+ /// Sets the default options against the image processing context.
+ ///
+ /// The image processing context to store default against.
+ /// The action to update instance of the default options used.
+ /// The passed in to allow chaining.
+ public static IImageProcessingContext SetGraphicsOptions(this IImageProcessingContext context, Action optionsBuilder)
+ {
+ var cloned = context.GetGraphicsOptions().DeepClone();
+ optionsBuilder(cloned);
+ context.Properties[typeof(GraphicsOptions)] = cloned;
+ return context;
+ }
+
+ ///
+ /// Sets the default options against the configuration.
+ ///
+ /// The configuration to store default against.
+ /// The default options to use.
+ public static void SetGraphicsOptions(this Configuration configuration, Action optionsBuilder)
+ {
+ var cloned = configuration.GetGraphicsOptions().DeepClone();
+ optionsBuilder(cloned);
+ configuration.Properties[typeof(GraphicsOptions)] = cloned;
+ }
+
+ ///
+ /// Sets the default options against the image processing context.
+ ///
+ /// The image processing context to store default against.
+ /// The default options to use.
+ /// The passed in to allow chaining.
+ public static IImageProcessingContext SetGraphicsOptions(this IImageProcessingContext context, GraphicsOptions options)
+ {
+ context.Properties[typeof(GraphicsOptions)] = options;
+ return context;
+ }
+
+ ///
+ /// Sets the default options against the configuration.
+ ///
+ /// The configuration to store default against.
+ /// The default options to use.
+ public static void SetGraphicsOptions(this Configuration configuration, GraphicsOptions options)
+ {
+ configuration.Properties[typeof(GraphicsOptions)] = options;
+ }
+
+ ///
+ /// Gets the default options against the image processing context.
+ ///
+ /// The image processing context to retrieve defaults from.
+ /// The globaly configued default options.
+ public static GraphicsOptions GetGraphicsOptions(this IImageProcessingContext context)
+ {
+ if (context.Properties.TryGetValue(typeof(GraphicsOptions), out var options) && options is GraphicsOptions go)
+ {
+ return go;
+ }
+
+ var configOptions = context.Configuration.GetGraphicsOptions();
+
+ // do not cache the fall back to config into the the processing context
+ // in case someone want to change the value on the config and expects it re trflow thru
+ return configOptions;
+ }
+
+ ///
+ /// Gets the default options against the image processing context.
+ ///
+ /// The configuration to retrieve defaults from.
+ /// The globaly configued default options.
+ public static GraphicsOptions GetGraphicsOptions(this Configuration configuration)
+ {
+ if (configuration.Properties.TryGetValue(typeof(GraphicsOptions), out var options) && options is GraphicsOptions go)
+ {
+ return go;
+ }
+
+ var configOptions = new GraphicsOptions();
+
+ // capture the fallback so the same instance will always be returned in case its mutated
+ configuration.Properties[typeof(GraphicsOptions)] = configOptions;
+ return configOptions;
+ }
+ }
+}
diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs
index 06b05fe3c4..a0e8097d8e 100644
--- a/src/ImageSharp/Image.FromBytes.cs
+++ b/src/ImageSharp/Image.FromBytes.cs
@@ -17,21 +17,23 @@ namespace SixLabors.ImageSharp
/// By reading the header on the provided byte array this calculates the images format.
///
/// The byte array containing encoded image data to read the header from.
+ /// The data is null.
/// The format or null if none found.
public static IImageFormat DetectFormat(byte[] data)
- {
- return DetectFormat(Configuration.Default, data);
- }
+ => DetectFormat(Configuration.Default, data);
///
/// By reading the header on the provided byte array this calculates the images format.
///
/// The configuration.
/// The byte array containing encoded image data to read the header from.
+ /// The configuration is null.
+ /// The data is null.
/// The mime type or null if none found.
public static IImageFormat DetectFormat(Configuration configuration, byte[] data)
{
- Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return DetectFormat(configuration, stream);
@@ -42,7 +44,8 @@ namespace SixLabors.ImageSharp
/// Reads the raw image information from the specified stream without fully decoding it.
///
/// The byte array containing encoded image data to read the header from.
- /// Thrown if the stream is not readable.
+ /// The data is null.
+ /// The data is not readable.
///
/// The or null if suitable info detector not found.
///
@@ -53,7 +56,8 @@ namespace SixLabors.ImageSharp
///
/// The byte array containing encoded image data to read the header from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
+ /// The data is null.
+ /// The data is not readable.
///
/// The or null if suitable info detector not found.
///
@@ -65,13 +69,16 @@ namespace SixLabors.ImageSharp
/// The configuration.
/// The byte array containing encoded image data to read the header from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
+ /// The configuration is null.
+ /// The data is null.
+ /// The data is not readable.
///
/// The or null if suitable info detector is not found.
///
public static IImageInfo Identify(Configuration configuration, byte[] data, out IImageFormat format)
{
- Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Identify(configuration, stream, out format);
@@ -82,14 +89,20 @@ namespace SixLabors.ImageSharp
/// Load a new instance of from the given encoded byte array.
///
/// The byte array containing image data.
+ /// The configuration is null.
+ /// The data is null.
/// A new .
- public static Image Load(byte[] data) => Load(Configuration.Default, data);
+ public static Image Load(byte[] data)
+ => Load(Configuration.Default, data);
///
/// Load a new instance of from the given encoded byte array.
///
/// The byte array containing encoded image data.
/// The pixel format.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(byte[] data)
where TPixel : unmanaged, IPixel
@@ -101,6 +114,9 @@ namespace SixLabors.ImageSharp
/// The byte array containing image data.
/// The mime type of the decoded image.
/// The pixel format.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(byte[] data, out IImageFormat format)
where TPixel : unmanaged, IPixel
@@ -112,10 +128,16 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The byte array containing encoded image data.
/// The pixel format.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(Configuration configuration, byte[] data)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(configuration, stream);
@@ -129,10 +151,16 @@ namespace SixLabors.ImageSharp
/// The byte array containing encoded image data.
/// The of the decoded image.
/// The pixel format.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(Configuration configuration, byte[] data, out IImageFormat format)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(configuration, stream, out format);
@@ -145,10 +173,15 @@ namespace SixLabors.ImageSharp
/// The byte array containing encoded image data.
/// The decoder.
/// The pixel format.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(byte[] data, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(stream, decoder);
@@ -162,10 +195,16 @@ namespace SixLabors.ImageSharp
/// The byte array containing encoded image data.
/// The decoder.
/// The pixel format.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(data, nameof(data));
+
using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(configuration, stream, decoder);
@@ -173,9 +212,9 @@ namespace SixLabors.ImageSharp
}
///
- /// By reading the header on the provided byte array this calculates the images format.
+ /// By reading the header on the provided byte span this calculates the images format.
///
- /// The byte array containing encoded image data to read the header from.
+ /// The byte span containing encoded image data to read the header from.
/// The format or null if none found.
public static IImageFormat DetectFormat(ReadOnlySpan data)
{
@@ -183,13 +222,16 @@ namespace SixLabors.ImageSharp
}
///
- /// By reading the header on the provided byte array this calculates the images format.
+ /// By reading the header on the provided byte span this calculates the images format.
///
/// The configuration.
- /// The byte array containing encoded image data to read the header from.
+ /// The byte span containing encoded image data to read the header from.
+ /// The configuration is null.
/// The mime type or null if none found.
public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpan data)
{
+ Guard.NotNull(configuration, nameof(configuration));
+
int maxHeaderSize = configuration.MaxHeaderSize;
if (maxHeaderSize <= 0)
{
@@ -214,28 +256,34 @@ namespace SixLabors.ImageSharp
///
/// The byte span containing encoded image data.
/// The pixel format.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(ReadOnlySpan data)
where TPixel : unmanaged, IPixel
=> Load(Configuration.Default, data);
///
- /// Load a new instance of from the given encoded byte array.
+ /// Load a new instance of from the given encoded byte span.
///
/// The byte span containing image data.
/// The mime type of the decoded image.
/// The pixel format.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(ReadOnlySpan data, out IImageFormat format)
where TPixel : unmanaged, IPixel
=> Load(Configuration.Default, data, out format);
///
- /// Load a new instance of from the given encoded byte array.
+ /// Load a new instance of from the given encoded byte span.
///
/// The byte span containing encoded image data.
/// The decoder.
/// The pixel format.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(ReadOnlySpan data, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
@@ -247,6 +295,9 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The byte span containing encoded image data.
/// The pixel format.
+ /// The configuration is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static unsafe Image Load(Configuration configuration, ReadOnlySpan data)
where TPixel : unmanaged, IPixel
@@ -267,6 +318,9 @@ namespace SixLabors.ImageSharp
/// The byte span containing image data.
/// The decoder.
/// The pixel format.
+ /// The configuration is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static unsafe Image Load(
Configuration configuration,
@@ -290,6 +344,9 @@ namespace SixLabors.ImageSharp
/// The byte span containing image data.
/// The of the decoded image.
/// The pixel format.
+ /// The configuration is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static unsafe Image Load(
Configuration configuration,
@@ -311,25 +368,38 @@ namespace SixLabors.ImageSharp
///
/// The byte array containing image data.
/// The detected format.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(byte[] data, out IImageFormat format) =>
- Load(Configuration.Default, data, out format);
+ public static Image Load(byte[] data, out IImageFormat format)
+ => Load(Configuration.Default, data, out format);
///
/// Load a new instance of from the given encoded byte array.
///
/// The byte array containing encoded image data.
/// The decoder.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(byte[] data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder);
+ public static Image Load(byte[] data, IImageDecoder decoder)
+ => Load(Configuration.Default, data, decoder);
///
/// Load a new instance of from the given encoded byte array.
///
/// The configuration for the decoder.
/// The byte array containing encoded image data.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(Configuration configuration, byte[] data) => Load(configuration, data, out _);
+ public static Image Load(Configuration configuration, byte[] data)
+ => Load(configuration, data, out _);
///
/// Load a new instance of from the given encoded byte array.
@@ -337,6 +407,10 @@ namespace SixLabors.ImageSharp
/// The configuration for the decoder.
/// The byte array containing image data.
/// The decoder.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder)
{
@@ -352,6 +426,10 @@ namespace SixLabors.ImageSharp
/// The configuration for the decoder.
/// The byte array containing image data.
/// The mime type of the decoded image.
+ /// The configuration is null.
+ /// The data is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static Image Load(Configuration configuration, byte[] data, out IImageFormat format)
{
@@ -365,26 +443,36 @@ namespace SixLabors.ImageSharp
/// Load a new instance of from the given encoded byte span.
///
/// The byte span containing image data.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data);
+ public static Image Load(ReadOnlySpan data)
+ => Load(Configuration.Default, data);
///
/// Load a new instance of from the given encoded byte span.
///
/// The byte span containing image data.
/// The decoder.
+ /// The data is null.
+ /// The decoder is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(ReadOnlySpan data, IImageDecoder decoder) =>
- Load(Configuration.Default, data, decoder);
+ public static Image Load(ReadOnlySpan data, IImageDecoder decoder)
+ => Load(Configuration.Default, data, decoder);
///
/// Load a new instance of from the given encoded byte array.
///
/// The byte span containing image data.
/// The detected format.
+ /// The decoder is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(ReadOnlySpan data, out IImageFormat format) =>
- Load(Configuration.Default, data, out format);
+ public static Image Load(ReadOnlySpan data, out IImageFormat format)
+ => Load(Configuration.Default, data, out format);
///
/// Decodes a new instance of from the given encoded byte span.
@@ -392,7 +480,8 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The byte span containing image data.
/// The .
- public static Image Load(Configuration configuration, ReadOnlySpan data) => Load(configuration, data, out _);
+ public static Image Load(Configuration configuration, ReadOnlySpan data)
+ => Load(configuration, data, out _);
///
/// Load a new instance of from the given encoded byte span.
@@ -400,6 +489,11 @@ namespace SixLabors.ImageSharp
/// The Configuration.
/// The byte span containing image data.
/// The decoder.
+ /// The configuration is null.
+ /// The decoder is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static unsafe Image Load(
Configuration configuration,
@@ -421,6 +515,9 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The byte span containing image data.
/// The of the decoded image.>
+ /// The configuration is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static unsafe Image Load(
Configuration configuration,
diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs
index 1a9fa15462..8546dd2700 100644
--- a/src/ImageSharp/Image.FromFile.cs
+++ b/src/ImageSharp/Image.FromFile.cs
@@ -19,19 +19,19 @@ namespace SixLabors.ImageSharp
/// The image file to open and to read the header from.
/// The mime type or null if none found.
public static IImageFormat DetectFormat(string filePath)
- {
- return DetectFormat(Configuration.Default, filePath);
- }
+ => DetectFormat(Configuration.Default, filePath);
///
/// By reading the header on the provided file this calculates the images mime type.
///
/// The configuration.
/// The image file to open and to read the header from.
+ /// The configuration is null.
/// The mime type or null if none found.
public static IImageFormat DetectFormat(Configuration configuration, string filePath)
{
Guard.NotNull(configuration, nameof(configuration));
+
using (Stream file = configuration.FileSystem.OpenRead(filePath))
{
return DetectFormat(configuration, file);
@@ -42,22 +42,22 @@ namespace SixLabors.ImageSharp
/// Reads the raw image information from the specified stream without fully decoding it.
///
/// The image file to open and to read the header from.
- /// Thrown if the stream is not readable.
///
/// The or null if suitable info detector not found.
///
- public static IImageInfo Identify(string filePath) => Identify(filePath, out IImageFormat _);
+ public static IImageInfo Identify(string filePath)
+ => Identify(filePath, out IImageFormat _);
///
/// Reads the raw image information from the specified stream without fully decoding it.
///
/// The image file to open and to read the header from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
///
/// The or null if suitable info detector not found.
///
- public static IImageInfo Identify(string filePath, out IImageFormat format) => Identify(Configuration.Default, filePath, out format);
+ public static IImageInfo Identify(string filePath, out IImageFormat format)
+ => Identify(Configuration.Default, filePath, out format);
///
/// Reads the raw image information from the specified stream without fully decoding it.
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp
/// The configuration.
/// The image file to open and to read the header from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
+ /// The configuration is null.
///
/// The or null if suitable info detector is not found.
///
@@ -86,7 +86,8 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
///
/// The .
- public static Image Load(string path) => Load(Configuration.Default, path);
+ public static Image Load(string path)
+ => Load(Configuration.Default, path);
///
/// Create a new instance of the class from the given file.
@@ -97,18 +98,21 @@ namespace SixLabors.ImageSharp
/// Thrown if the stream is not readable nor seekable.
///
/// A new .
- public static Image Load(string path, out IImageFormat format) => Load(Configuration.Default, path, out format);
+ public static Image Load(string path, out IImageFormat format)
+ => Load(Configuration.Default, path, out format);
///
/// Create a new instance of the class from the given file.
///
/// The configuration for the decoder.
/// The file path to the image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(Configuration configuration, string path) => Load(configuration, path, out _);
+ public static Image Load(Configuration configuration, string path)
+ => Load(configuration, path, out _);
///
/// Create a new instance of the class from the given file.
@@ -116,13 +120,17 @@ namespace SixLabors.ImageSharp
/// The Configuration.
/// The file path to the image.
/// The decoder.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// The decoder is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static Image Load(Configuration configuration, string path, IImageDecoder decoder)
{
Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(path, nameof(path));
+
using (Stream stream = configuration.FileSystem.OpenRead(path))
{
return Load(configuration, stream, decoder);
@@ -134,57 +142,58 @@ namespace SixLabors.ImageSharp
///
/// The file path to the image.
/// The decoder.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The path is null.
+ /// The decoder is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(string path, IImageDecoder decoder) => Load(Configuration.Default, path, decoder);
+ public static Image Load(string path, IImageDecoder decoder)
+ => Load(Configuration.Default, path, decoder);
///
/// Create a new instance of the class from the given file.
///
/// The file path to the image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(string path)
where TPixel : unmanaged, IPixel
- {
- return Load(Configuration.Default, path);
- }
+ => Load(Configuration.Default, path);
///
/// Create a new instance of the class from the given file.
///
/// The file path to the image.
/// The mime type of the decoded image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(string path, out IImageFormat format)
where TPixel : unmanaged, IPixel
- {
- return Load(Configuration.Default, path, out format);
- }
+ => Load(Configuration.Default, path, out format);
///
/// Create a new instance of the class from the given file.
///
/// The configuration options.
/// The file path to the image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(Configuration configuration, string path)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(path, nameof(path));
+
using (Stream stream = configuration.FileSystem.OpenRead(path))
{
return Load(configuration, stream);
@@ -197,15 +206,18 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The file path to the image.
/// The mime type of the decoded image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(Configuration configuration, string path, out IImageFormat format)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(path, nameof(path));
+
using (Stream stream = configuration.FileSystem.OpenRead(path))
{
return Load(configuration, stream, out format);
@@ -219,13 +231,16 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The file path to the image.
/// The mime type of the decoded image.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(Configuration configuration, string path, out IImageFormat format)
{
Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(path, nameof(path));
+
using (Stream stream = configuration.FileSystem.OpenRead(path))
{
return Load(configuration, stream, out format);
@@ -237,16 +252,14 @@ namespace SixLabors.ImageSharp
///
/// The file path to the image.
/// The decoder.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The path is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(string path, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
- {
- return Load(Configuration.Default, path, decoder);
- }
+ => Load(Configuration.Default, path, decoder);
///
/// Create a new instance of the class from the given file.
@@ -254,15 +267,19 @@ namespace SixLabors.ImageSharp
/// The Configuration.
/// The file path to the image.
/// The decoder.
- ///
- /// Thrown if the stream is not readable nor seekable.
- ///
+ /// The configuration is null.
+ /// The path is null.
+ /// The decoder is null.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(Configuration configuration, string path, IImageDecoder decoder)
where TPixel : unmanaged, IPixel
{
Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(path, nameof(path));
+
using (Stream stream = configuration.FileSystem.OpenRead(path))
{
return Load(configuration, stream, decoder);
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index 52d71409bb..332ca471e9 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -19,16 +19,20 @@ namespace SixLabors.ImageSharp
/// By reading the header on the provided stream this calculates the images format type.
///
/// The image stream to read the header from.
- /// Thrown if the stream is not readable.
+ /// The stream is null.
+ /// The stream is not readable.
/// The format type or null if none found.
- public static IImageFormat DetectFormat(Stream stream) => DetectFormat(Configuration.Default, stream);
+ public static IImageFormat DetectFormat(Stream stream)
+ => DetectFormat(Configuration.Default, stream);
///
/// By reading the header on the provided stream this calculates the images format type.
///
/// The configuration.
/// The image stream to read the header from.
- /// Thrown if the stream is not readable.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
/// The format type or null if none found.
public static IImageFormat DetectFormat(Configuration configuration, Stream stream)
=> WithSeekableStream(configuration, stream, s => InternalDetectFormat(s, configuration));
@@ -37,22 +41,28 @@ namespace SixLabors.ImageSharp
/// Reads the raw image information from the specified stream without fully decoding it.
///
/// The image stream to read the header from.
- /// Thrown if the stream is not readable.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
///
/// The or null if suitable info detector not found.
///
- public static IImageInfo Identify(Stream stream) => Identify(stream, out IImageFormat _);
+ public static IImageInfo Identify(Stream stream)
+ => Identify(stream, out IImageFormat _);
///
/// Reads the raw image information from the specified stream without fully decoding it.
///
/// The image stream to read the header from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
///
/// The or null if suitable info detector not found.
///
- public static IImageInfo Identify(Stream stream, out IImageFormat format) => Identify(Configuration.Default, stream, out format);
+ public static IImageInfo Identify(Stream stream, out IImageFormat format)
+ => Identify(Configuration.Default, stream, out format);
///
/// Reads the raw image information from the specified stream without fully decoding it.
@@ -60,7 +70,10 @@ namespace SixLabors.ImageSharp
/// The configuration.
/// The image stream to read the information from.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image contains invalid content.
///
/// The or null if suitable info detector is not found.
///
@@ -78,18 +91,23 @@ namespace SixLabors.ImageSharp
///
/// The stream containing image information.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format);
+ public static Image Load(Stream stream, out IImageFormat format)
+ => Load(Configuration.Default, stream, out format);
///
/// Decode a new instance of the class from the given stream.
/// The pixel format is selected by the decoder.
///
/// The stream containing image information.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
public static Image Load(Stream stream) => Load(Configuration.Default, stream);
@@ -99,10 +117,14 @@ namespace SixLabors.ImageSharp
///
/// The stream containing image information.
/// The decoder.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The decoder is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The .
- public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder);
+ public static Image Load(Stream stream, IImageDecoder decoder)
+ => Load(Configuration.Default, stream, decoder);
///
/// Decode a new instance of the class from the given stream.
@@ -111,19 +133,29 @@ namespace SixLabors.ImageSharp
/// The configuration for the decoder.
/// The stream containing image information.
/// The decoder.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The decoder is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .>
- public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) =>
- WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s));
+ public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder)
+ {
+ Guard.NotNull(decoder, nameof(decoder));
+ return WithSeekableStream(configuration, stream, s => decoder.Decode(configuration, s));
+ }
///
/// Decode a new instance of the class from the given stream.
///
/// The configuration for the decoder.
/// The stream containing image information.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .>
public static Image Load(Configuration configuration, Stream stream) => Load(configuration, stream, out _);
@@ -131,8 +163,10 @@ namespace SixLabors.ImageSharp
/// Create a new instance of the class from the given stream.
///
/// The stream containing image information.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .>
public static Image Load(Stream stream)
@@ -144,8 +178,10 @@ namespace SixLabors.ImageSharp
///
/// The stream containing image information.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .>
public static Image Load(Stream stream, out IImageFormat format)
@@ -157,8 +193,10 @@ namespace SixLabors.ImageSharp
///
/// The stream containing image information.
/// The decoder.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .>
public static Image Load(Stream stream, IImageDecoder decoder)
@@ -171,8 +209,11 @@ namespace SixLabors.ImageSharp
/// The Configuration.
/// The stream containing image information.
/// The decoder.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .>
public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder)
@@ -184,8 +225,11 @@ namespace SixLabors.ImageSharp
///
/// The configuration options.
/// The stream containing image information.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .>
public static Image Load(Configuration configuration, Stream stream)
@@ -198,14 +242,16 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The stream containing image information.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// The pixel format.
/// A new .
public static Image Load(Configuration configuration, Stream stream, out IImageFormat format)
where TPixel : unmanaged, IPixel
{
- Guard.NotNull(configuration, nameof(configuration));
(Image img, IImageFormat format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration));
format = data.format;
@@ -220,7 +266,7 @@ namespace SixLabors.ImageSharp
foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders)
{
- sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
+ sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new UnknownImageFormatException(sb.ToString());
@@ -233,12 +279,14 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The stream containing image information.
/// The format type of the decoded image.
- /// Thrown if the stream is not readable.
- /// Image cannot be loaded.
+ /// The configuration is null.
+ /// The stream is null.
+ /// The stream is not readable.
+ /// Image format not recognised.
+ /// Image contains invalid content.
/// A new .
public static Image Load(Configuration configuration, Stream stream, out IImageFormat format)
{
- Guard.NotNull(configuration, nameof(configuration));
(Image img, IImageFormat format) data = WithSeekableStream(configuration, stream, s => Decode(s, configuration));
format = data.format;
@@ -253,7 +301,7 @@ namespace SixLabors.ImageSharp
foreach (KeyValuePair val in configuration.ImageFormatsManager.ImageDecoders)
{
- sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
+ sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new UnknownImageFormatException(sb.ToString());
@@ -261,6 +309,9 @@ namespace SixLabors.ImageSharp
private static T WithSeekableStream(Configuration configuration, Stream stream, Func action)
{
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(stream, nameof(stream));
+
if (!stream.CanRead)
{
throw new NotSupportedException("Cannot read from the stream.");
diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs
index f8f2e84850..f36243cc3e 100644
--- a/src/ImageSharp/Image.LoadPixelData.cs
+++ b/src/ImageSharp/Image.LoadPixelData.cs
@@ -1,9 +1,8 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -21,6 +20,7 @@ namespace SixLabors.ImageSharp
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The data length is incorrect.
/// A new .
public static Image LoadPixelData(TPixel[] data, int width, int height)
where TPixel : unmanaged, IPixel
@@ -33,6 +33,7 @@ namespace SixLabors.ImageSharp
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The data length is incorrect.
/// A new .
public static Image LoadPixelData(ReadOnlySpan data, int width, int height)
where TPixel : unmanaged, IPixel
@@ -45,6 +46,7 @@ namespace SixLabors.ImageSharp
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The data length is incorrect.
/// A new .
public static Image LoadPixelData(byte[] data, int width, int height)
where TPixel : unmanaged, IPixel
@@ -57,6 +59,7 @@ namespace SixLabors.ImageSharp
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The data length is incorrect.
/// A new .
public static Image LoadPixelData(ReadOnlySpan data, int width, int height)
where TPixel : unmanaged, IPixel
@@ -65,60 +68,68 @@ namespace SixLabors.ImageSharp
///
/// Create a new instance of the class from the given byte array in format.
///
- /// The config for the decoder.
+ /// The configuration for the decoder.
/// The byte array containing image data.
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The configuration is null.
+ /// The data length is incorrect.
/// A new .
- public static Image LoadPixelData(Configuration config, byte[] data, int width, int height)
+ public static Image LoadPixelData(Configuration configuration, byte[] data, int width, int height)
where TPixel : unmanaged, IPixel
- => LoadPixelData(config, MemoryMarshal.Cast(new ReadOnlySpan(data)), width, height);
+ => LoadPixelData(configuration, MemoryMarshal.Cast(new ReadOnlySpan(data)), width, height);
///
/// Create a new instance of the class from the given byte array in format.
///
- /// The config for the decoder.
+ /// The configuration for the decoder.
/// The byte array containing image data.
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The configuration is null.
+ /// The data length is incorrect.
/// A new .
- public static Image LoadPixelData(Configuration config, ReadOnlySpan data, int width, int height)
+ public static Image LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
where TPixel : unmanaged, IPixel
- => LoadPixelData(config, MemoryMarshal.Cast(data), width, height);
+ => LoadPixelData(configuration, MemoryMarshal.Cast(data), width, height);
///
/// Create a new instance of the class from the raw data.
///
- /// The config for the decoder.
+ /// The configuration for the decoder.
/// The Span containing the image Pixel data.
/// The width of the final image.
/// The height of the final image.
/// The pixel format.
+ /// The configuration is null.
+ /// The data length is incorrect.
/// A new .
- public static Image LoadPixelData(Configuration config, TPixel[] data, int width, int height)
+ public static Image LoadPixelData(Configuration configuration, TPixel[] data, int width, int height)
where TPixel : unmanaged, IPixel
- {
- return LoadPixelData(config, new ReadOnlySpan(data), width, height);
- }
+ => LoadPixelData(configuration, new ReadOnlySpan(data), width, height);
///
/// Create a new instance of the class from the raw data.
///
- /// The config for the decoder.
+ /// The configuration for the decoder.
/// The Span containing the image Pixel data.
/// The width of the final image.
/// The height of the final image.
+ /// The configuration is null.
+ /// The data length is incorrect.
/// The pixel format.
/// A new .
- public static Image LoadPixelData(Configuration config, ReadOnlySpan data, int width, int height)
+ public static Image LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(configuration, nameof(configuration));
+
int count = width * height;
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
- var image = new Image(config, width, height);
+ var image = new Image(configuration, width, height);
data = data.Slice(0, count);
data.CopyTo(image.Frames.RootFrame.PixelBuffer.FastMemoryGroup);
diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs
index e5181a0db3..0dd8c814d0 100644
--- a/src/ImageSharp/Image.WrapMemory.cs
+++ b/src/ImageSharp/Image.WrapMemory.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -20,22 +20,27 @@ namespace SixLabors.ImageSharp
/// allowing to view/manipulate it as an ImageSharp instance.
///
/// The pixel type
- /// The
+ /// The
/// The pixel memory.
/// The width of the memory image.
/// The height of the memory image.
/// The .
+ /// The configuration is null.
+ /// The metadata is null.
/// An instance
public static Image WrapMemory(
- Configuration config,
+ Configuration configuration,
Memory pixelMemory,
int width,
int height,
ImageMetadata metadata)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(metadata, nameof(metadata));
+
var memorySource = MemoryGroup.Wrap(pixelMemory);
- return new Image(config, memorySource, width, height, metadata);
+ return new Image(configuration, memorySource, width, height, metadata);
}
///
@@ -43,20 +48,19 @@ namespace SixLabors.ImageSharp
/// allowing to view/manipulate it as an ImageSharp instance.
///
/// The pixel type
- /// The
+ /// The
/// The pixel memory.
/// The width of the memory image.
/// The height of the memory image.
+ /// The configuration is null.
/// An instance.
public static Image WrapMemory(
- Configuration config,
+ Configuration configuration,
Memory pixelMemory,
int width,
int height)
where TPixel : unmanaged, IPixel
- {
- return WrapMemory(config, pixelMemory, width, height, new ImageMetadata());
- }
+ => WrapMemory(configuration, pixelMemory, width, height, new ImageMetadata());
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
@@ -73,9 +77,7 @@ namespace SixLabors.ImageSharp
int width,
int height)
where TPixel : unmanaged, IPixel
- {
- return WrapMemory(Configuration.Default, pixelMemory, width, height);
- }
+ => WrapMemory(Configuration.Default, pixelMemory, width, height);
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
@@ -85,22 +87,27 @@ namespace SixLabors.ImageSharp
/// It will be disposed together with the result image.
///
/// The pixel type
- /// The
+ /// The
/// The that is being transferred to the image
/// The width of the memory image.
/// The height of the memory image.
/// The
+ /// The configuration is null.
+ /// The metadata is null.
/// An instance
public static Image WrapMemory(
- Configuration config,
+ Configuration configuration,
IMemoryOwner pixelMemoryOwner,
int width,
int height,
ImageMetadata metadata)
where TPixel : unmanaged, IPixel
{
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.NotNull(metadata, nameof(metadata));
+
var memorySource = MemoryGroup.Wrap(pixelMemoryOwner);
- return new Image(config, memorySource, width, height, metadata);
+ return new Image(configuration, memorySource, width, height, metadata);
}
///
@@ -111,20 +118,19 @@ namespace SixLabors.ImageSharp
/// It will be disposed together with the result image.
///
/// The pixel type.
- /// The
+ /// The
/// The that is being transferred to the image.
/// The width of the memory image.
/// The height of the memory image.
+ /// The configuration is null.
/// An instance
public static Image WrapMemory(
- Configuration config,
+ Configuration configuration,
IMemoryOwner pixelMemoryOwner,
int width,
int height)
where TPixel : unmanaged, IPixel
- {
- return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetadata());
- }
+ => WrapMemory(configuration, pixelMemoryOwner, width, height, new ImageMetadata());
///
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
@@ -143,8 +149,6 @@ namespace SixLabors.ImageSharp
int width,
int height)
where TPixel : unmanaged, IPixel
- {
- return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
- }
+ => WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
}
}
diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs
index 0bdbcc4ab3..e5b2a32a99 100644
--- a/src/ImageSharp/ImageExtensions.cs
+++ b/src/ImageSharp/ImageExtensions.cs
@@ -7,12 +7,11 @@ using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
-using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
- /// Extension methods over Image{TPixel}.
+ /// Extension methods for the type.
///
public static partial class ImageExtensions
{
@@ -20,13 +19,13 @@ namespace SixLabors.ImageSharp
/// Writes the image to the given stream using the currently loaded image format.
///
/// The source image.
- /// The file path to save the image to.
- /// Thrown if the stream is null.
- public static void Save(this Image source, string filePath)
+ /// The file path to save the image to.
+ /// The path is null.
+ public static void Save(this Image source, string path)
{
- Guard.NotNullOrWhiteSpace(filePath, nameof(filePath));
+ Guard.NotNull(path, nameof(path));
- string ext = Path.GetExtension(filePath);
+ string ext = Path.GetExtension(path);
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format is null)
{
@@ -34,7 +33,7 @@ namespace SixLabors.ImageSharp
sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
- sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
+ sb.AppendFormat(" - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@@ -48,26 +47,28 @@ namespace SixLabors.ImageSharp
sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
- sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
+ sb.AppendFormat(" - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
}
- source.Save(filePath, encoder);
+ source.Save(path, encoder);
}
///
/// Writes the image to the given stream using the currently loaded image format.
///
/// The source image.
- /// The file path to save the image to.
+ /// The file path to save the image to.
/// The encoder to save the image with.
- /// Thrown if the encoder is null.
- public static void Save(this Image source, string filePath, IImageEncoder encoder)
+ /// The path is null.
+ /// The encoder is null.
+ public static void Save(this Image source, string path, IImageEncoder encoder)
{
+ Guard.NotNull(path, nameof(path));
Guard.NotNull(encoder, nameof(encoder));
- using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath))
+ using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
{
source.Save(fs, encoder);
}
@@ -79,10 +80,20 @@ namespace SixLabors.ImageSharp
/// The source image.
/// The stream to save the image to.
/// The format to save the image in.
- /// Thrown if the stream is null.
+ /// The stream is null.
+ /// The format is null.
+ /// The stream is not writable.
+ /// No encoder available for provided format.
public static void Save(this Image source, Stream stream, IImageFormat format)
{
+ Guard.NotNull(stream, nameof(stream));
Guard.NotNull(format, nameof(format));
+
+ if (!stream.CanWrite)
+ {
+ throw new NotSupportedException("Cannot write to the stream.");
+ }
+
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder is null)
@@ -92,7 +103,7 @@ namespace SixLabors.ImageSharp
foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
- sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
+ sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@@ -103,15 +114,22 @@ namespace SixLabors.ImageSharp
///
/// Returns a Base64 encoded string from the given image.
+ /// The result is prepended with a Data URI
+ ///
+ ///
+ /// For example:
+ ///
+ ///
+ ///
///
- ///
- /// The pixel format.
/// The source image
/// The format.
+ /// The format is null.
/// The
- public static string ToBase64String(this Image source, IImageFormat format)
- where TPixel : unmanaged, IPixel
+ public static string ToBase64String(this Image source, IImageFormat format)
{
+ Guard.NotNull(format, nameof(format));
+
using var stream = new MemoryStream();
source.Save(stream, format);
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index a35443ec9d..0171f3d07f 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@@ -166,6 +167,40 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Gets the representation of the pixels as a of contiguous memory
+ /// at row beginning from the first pixel on that row.
+ ///
+ /// The row.
+ /// The
+ /// Thrown when row index is out of range.
+ public Span GetPixelRowSpan(int rowIndex)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
+ Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));
+
+ return this.PixelBuffer.GetRowSpan(rowIndex);
+ }
+
+ ///
+ /// Gets the representation of the pixels as a in the source image's pixel format
+ /// stored in row major order, if the backing buffer is contiguous.
+ ///
+ /// The .
+ /// The .
+ public bool TryGetSinglePixelSpan(out Span span)
+ {
+ IMemoryGroup mg = this.GetPixelMemoryGroup();
+ if (mg.Count > 1)
+ {
+ span = default;
+ return false;
+ }
+
+ span = mg.Single().Span;
+ return true;
+ }
+
///
/// Gets a reference to the pixel at the specified position.
///
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 56f1f6b7bf..f6173db972 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -163,6 +163,40 @@ namespace SixLabors.ImageSharp
}
}
+ ///
+ /// Gets the representation of the pixels as a of contiguous memory
+ /// at row beginning from the first pixel on that row.
+ ///
+ /// The row.
+ /// The
+ /// Thrown when row index is out of range.
+ public Span GetPixelRowSpan(int rowIndex)
+ {
+ Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, nameof(rowIndex));
+ Guard.MustBeLessThan(rowIndex, this.Height, nameof(rowIndex));
+
+ return this.PixelSource.PixelBuffer.GetRowSpan(rowIndex);
+ }
+
+ ///
+ /// Gets the representation of the pixels as a in the source image's pixel format
+ /// stored in row major order, if the backing buffer is contiguous.
+ ///
+ /// The .
+ /// The .
+ public bool TryGetSinglePixelSpan(out Span span)
+ {
+ IMemoryGroup mg = this.GetPixelMemoryGroup();
+ if (mg.Count > 1)
+ {
+ span = default;
+ return false;
+ }
+
+ span = mg.Single().Span;
+ return true;
+ }
+
///
/// Clones the current image
///
diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs
index bf8630931a..ada1d29b6d 100644
--- a/src/ImageSharp/Memory/Buffer2D{T}.cs
+++ b/src/ImageSharp/Memory/Buffer2D{T}.cs
@@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Memory
{
DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
- return this.FastMemoryGroup.View.GetBoundedSlice(y * this.Width, this.Width);
+ return this.FastMemoryGroup.View.GetBoundedSlice(y * (long)this.Width, this.Width);
}
///
@@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Memory
}
[MethodImpl(InliningOptions.ColdPath)]
- private Memory GetRowMemorySlow(int y) => this.FastMemoryGroup.GetBoundedSlice(y * this.Width, this.Width);
+ private Memory GetRowMemorySlow(int y) => this.FastMemoryGroup.GetBoundedSlice(y * (long)this.Width, this.Width);
[MethodImpl(InliningOptions.ColdPath)]
private Memory GetSingleMemorySlow() => this.FastMemoryGroup.Single();
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
index 89aca914d0..3bb6b8d336 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
@@ -33,5 +33,15 @@ namespace SixLabors.ImageSharp.Memory
/// the image buffers internally.
///
bool IsValid { get; }
+
+ ///
+ /// Returns a value-type implementing an allocation-free enumerator of the memory groups in the current
+ /// instance. The return type shouldn't be used directly: just use a block on
+ /// the instance in use and the C# compiler will automatically invoke this
+ /// method behind the scenes. This method takes precedence over the
+ /// implementation, which is still available when casting to one of the underlying interfaces.
+ ///
+ /// A new instance mapping the current values in use.
+ new MemoryGroupEnumerator GetEnumerator();
}
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupEnumerator{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupEnumerator{T}.cs
new file mode 100644
index 0000000000..1bc44e33e1
--- /dev/null
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupEnumerator{T}.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp.Memory
+{
+ ///
+ /// A value-type enumerator for instances.
+ ///
+ /// The element type.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public ref struct MemoryGroupEnumerator
+ where T : struct
+ {
+ private readonly IMemoryGroup memoryGroup;
+ private readonly int count;
+ private int index;
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal MemoryGroupEnumerator(MemoryGroup.Owned memoryGroup)
+ {
+ this.memoryGroup = memoryGroup;
+ this.count = memoryGroup.Count;
+ this.index = -1;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal MemoryGroupEnumerator(MemoryGroup.Consumed memoryGroup)
+ {
+ this.memoryGroup = memoryGroup;
+ this.count = memoryGroup.Count;
+ this.index = -1;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal MemoryGroupEnumerator(MemoryGroupView memoryGroup)
+ {
+ this.memoryGroup = memoryGroup;
+ this.count = memoryGroup.Count;
+ this.index = -1;
+ }
+
+ ///
+ public Memory Current
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => this.memoryGroup[this.index];
+ }
+
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool MoveNext()
+ {
+ int index = this.index + 1;
+
+ if (index < this.count)
+ {
+ this.index = index;
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
index 28da49263e..295f9190a9 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -38,6 +38,12 @@ namespace SixLabors.ImageSharp.Memory
Guard.MustBeLessThan(start, group.TotalLength, nameof(start));
int bufferIdx = (int)(start / group.BufferLength);
+
+ if (bufferIdx < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(start));
+ }
+
if (bufferIdx >= group.Count)
{
throw new ArgumentOutOfRangeException(nameof(start));
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
index 3f39ba12f5..1698f08d17 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
@@ -5,6 +5,7 @@ using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
@@ -37,6 +38,7 @@ namespace SixLabors.ImageSharp.Memory
public int Count
{
+ [MethodImpl(InliningOptions.ShortMethod)]
get
{
this.EnsureIsValid();
@@ -73,7 +75,15 @@ namespace SixLabors.ImageSharp.Memory
}
}
- public IEnumerator> GetEnumerator()
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public MemoryGroupEnumerator GetEnumerator()
+ {
+ return new MemoryGroupEnumerator(this);
+ }
+
+ ///
+ IEnumerator> IEnumerable>.GetEnumerator()
{
this.EnsureIsValid();
for (int i = 0; i < this.Count; i++)
@@ -82,7 +92,8 @@ namespace SixLabors.ImageSharp.Memory
}
}
- IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator();
internal void Invalidate()
{
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
index f1fe4ed9c5..1dfbaea932 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
@@ -2,16 +2,17 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Buffers;
using System.Collections.Generic;
-using System.Linq;
+using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
internal abstract partial class MemoryGroup
{
- // Analogous to the "consumed" variant of MemorySource
- private sealed class Consumed : MemoryGroup
+ ///
+ /// A implementation that consumes the underlying memory buffers.
+ ///
+ public sealed class Consumed : MemoryGroup, IEnumerable>
{
private readonly Memory[] source;
@@ -22,16 +23,31 @@ namespace SixLabors.ImageSharp.Memory
this.View = new MemoryGroupView(this);
}
- public override int Count => this.source.Length;
+ public override int Count
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => this.source.Length;
+ }
public override Memory this[int index] => this.source[index];
- public override IEnumerator> GetEnumerator()
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public override MemoryGroupEnumerator GetEnumerator()
+ {
+ return new MemoryGroupEnumerator(this);
+ }
+
+ ///
+ IEnumerator> IEnumerable>.GetEnumerator()
{
- for (int i = 0; i < this.source.Length; i++)
- {
- yield return this.source[i];
- }
+ /* The runtime sees the Array class as if it implemented the
+ * type-generic collection interfaces explicitly, so here we
+ * can just cast the source array to IList> (or to
+ * an equivalent type), and invoke the generic GetEnumerator
+ * method directly from that interface reference. This saves
+ * having to create our own iterator block here. */
+ return ((IList>)this.source).GetEnumerator();
}
public override void Dispose()
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
index b42b90d286..5a86ac4268 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
@@ -5,13 +5,16 @@ using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
- // Analogous to the "owned" variant of MemorySource
internal abstract partial class MemoryGroup
{
- private sealed class Owned : MemoryGroup
+ ///
+ /// A implementation that owns the underlying memory buffers.
+ ///
+ public sealed class Owned : MemoryGroup, IEnumerable>
{
private IMemoryOwner[] memoryOwners;
@@ -29,6 +32,7 @@ namespace SixLabors.ImageSharp.Memory
public override int Count
{
+ [MethodImpl(InliningOptions.ShortMethod)]
get
{
this.EnsureNotDisposed();
@@ -45,7 +49,15 @@ namespace SixLabors.ImageSharp.Memory
}
}
- public override IEnumerator> GetEnumerator()
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public override MemoryGroupEnumerator GetEnumerator()
+ {
+ return new MemoryGroupEnumerator(this);
+ }
+
+ ///
+ IEnumerator> IEnumerable>.GetEnumerator()
{
this.EnsureNotDisposed();
return this.memoryOwners.Select(mo => mo.Memory).GetEnumerator();
@@ -69,14 +81,21 @@ namespace SixLabors.ImageSharp.Memory
this.IsValid = false;
}
+ [MethodImpl(InliningOptions.ShortMethod)]
private void EnsureNotDisposed()
{
- if (this.memoryOwners == null)
+ if (this.memoryOwners is null)
{
- throw new ObjectDisposedException(nameof(MemoryGroup));
+ ThrowObjectDisposedException();
}
}
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowObjectDisposedException()
+ {
+ throw new ObjectDisposedException(nameof(MemoryGroup));
+ }
+
internal static void SwapContents(Owned a, Owned b)
{
a.EnsureNotDisposed();
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
index 38de57b4ac..6fd93f12ea 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
@@ -6,7 +6,6 @@ using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Memory.Internals;
namespace SixLabors.ImageSharp.Memory
{
@@ -48,10 +47,21 @@ namespace SixLabors.ImageSharp.Memory
public abstract void Dispose();
///
- public abstract IEnumerator> GetEnumerator();
+ public abstract MemoryGroupEnumerator GetEnumerator();
///
- IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ /* This method is implemented in each derived class.
+ * Implementing the method here as non-abstract and throwing,
+ * then reimplementing it explicitly in each derived class, is
+ * a workaround for the lack of support for abstract explicit
+ * interface method implementations in C#. */
+ throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()");
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator();
///
/// Creates a new memory group, allocating it's buffers with the provided allocator.
diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs
index b3751bfbdc..716e89e68d 100644
--- a/src/ImageSharp/Metadata/ImageMetadata.cs
+++ b/src/ImageSharp/Metadata/ImageMetadata.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
namespace SixLabors.ImageSharp.Metadata
{
@@ -65,6 +66,7 @@ namespace SixLabors.ImageSharp.Metadata
this.ExifProfile = other.ExifProfile?.DeepClone();
this.IccProfile = other.IccProfile?.DeepClone();
+ this.IptcProfile = other.IptcProfile?.DeepClone();
}
///
@@ -122,6 +124,11 @@ namespace SixLabors.ImageSharp.Metadata
///
public IccProfile IccProfile { get; set; }
+ ///
+ /// Gets or sets the iptc profile.
+ ///
+ public IptcProfile IptcProfile { get; set; }
+
///
/// Gets the metadata value associated with the specified key.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
index 11d0bd01b0..29c21d6113 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
@@ -57,8 +57,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// by making a copy from another EXIF profile.
///
/// The other EXIF profile, where the clone should be made from.
+ /// is null.>
private ExifProfile(ExifProfile other)
{
+ Guard.NotNull(other, nameof(other));
+
this.Parts = other.Parts;
this.thumbnailLength = other.thumbnailLength;
this.thumbnailOffset = other.thumbnailOffset;
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IIMV4.2_IPTC.pdf b/src/ImageSharp/Metadata/Profiles/IPTC/IIMV4.2_IPTC.pdf
new file mode 100644
index 0000000000..b00355181c
Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/IPTC/IIMV4.2_IPTC.pdf differ
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
new file mode 100644
index 0000000000..f138cc650f
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
@@ -0,0 +1,298 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
+{
+ ///
+ /// Represents an IPTC profile providing access to the collection of values.
+ ///
+ public sealed class IptcProfile : IDeepCloneable
+ {
+ private Collection values;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public IptcProfile()
+ : this((byte[])null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The byte array to read the iptc profile from.
+ public IptcProfile(byte[] data)
+ {
+ this.Data = data;
+ this.Initialize();
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// by making a copy from another IPTC profile.
+ ///
+ /// The other IPTC profile, from which the clone should be made from.
+ private IptcProfile(IptcProfile other)
+ {
+ Guard.NotNull(other, nameof(other));
+
+ if (other.values != null)
+ {
+ this.values = new Collection();
+
+ foreach (IptcValue value in other.Values)
+ {
+ this.values.Add(value.DeepClone());
+ }
+ }
+
+ if (other.Data != null)
+ {
+ this.Data = new byte[other.Data.Length];
+ other.Data.AsSpan().CopyTo(this.Data);
+ }
+ }
+
+ ///
+ /// Gets the byte data of the IPTC profile.
+ ///
+ public byte[] Data { get; private set; }
+
+ ///
+ /// Gets the values of this iptc profile.
+ ///
+ public IEnumerable Values
+ {
+ get
+ {
+ this.Initialize();
+ return this.values;
+ }
+ }
+
+ ///
+ public IptcProfile DeepClone() => new IptcProfile(this);
+
+ ///
+ /// Returns all values with the specified tag.
+ ///
+ /// The tag of the iptc value.
+ /// The values found with the specified tag.
+ public List GetValues(IptcTag tag)
+ {
+ var iptcValues = new List();
+ foreach (IptcValue iptcValue in this.Values)
+ {
+ if (iptcValue.Tag == tag)
+ {
+ iptcValues.Add(iptcValue);
+ }
+ }
+
+ return iptcValues;
+ }
+
+ ///
+ /// Removes all values with the specified tag.
+ ///
+ /// The tag of the iptc value to remove.
+ /// True when the value was found and removed.
+ public bool RemoveValue(IptcTag tag)
+ {
+ this.Initialize();
+
+ bool removed = false;
+ for (int i = this.values.Count - 1; i >= 0; i--)
+ {
+ if (this.values[i].Tag == tag)
+ {
+ this.values.RemoveAt(i);
+ removed = true;
+ }
+ }
+
+ return removed;
+ }
+
+ ///
+ /// Removes values with the specified tag and value.
+ ///
+ /// The tag of the iptc value to remove.
+ /// The value of the iptc item to remove.
+ /// True when the value was found and removed.
+ public bool RemoveValue(IptcTag tag, string value)
+ {
+ this.Initialize();
+
+ bool removed = false;
+ for (int i = this.values.Count - 1; i >= 0; i--)
+ {
+ if (this.values[i].Tag == tag && this.values[i].Value.Equals(value))
+ {
+ this.values.RemoveAt(i);
+ removed = true;
+ }
+ }
+
+ return removed;
+ }
+
+ ///
+ /// Changes the encoding for all the values.
+ ///
+ /// The encoding to use when storing the bytes.
+ public void SetEncoding(Encoding encoding)
+ {
+ Guard.NotNull(encoding, nameof(encoding));
+
+ foreach (IptcValue value in this.Values)
+ {
+ value.Encoding = encoding;
+ }
+ }
+
+ ///
+ /// Sets the value for the specified tag.
+ ///
+ /// The tag of the iptc value.
+ /// The encoding to use when storing the bytes.
+ /// The value.
+ ///
+ /// Indicates if length restrictions from the specification should be followed strictly.
+ /// Defaults to true.
+ ///
+ public void SetValue(IptcTag tag, Encoding encoding, string value, bool strict = true)
+ {
+ Guard.NotNull(encoding, nameof(encoding));
+ Guard.NotNull(value, nameof(value));
+
+ if (!tag.IsRepeatable())
+ {
+ foreach (IptcValue iptcValue in this.Values)
+ {
+ if (iptcValue.Tag == tag)
+ {
+ iptcValue.Strict = strict;
+ iptcValue.Encoding = encoding;
+ iptcValue.Value = value;
+ return;
+ }
+ }
+ }
+
+ this.values.Add(new IptcValue(tag, encoding, value, strict));
+ }
+
+ ///
+ /// Makes sure the datetime is formatted according to the iptc specification.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ /// A time value will be formatted as HHMMSS±HHMM, e.g. "090000+0200" for 9 o'clock Berlin time,
+ /// two hours ahead of UTC.
+ ///
+ ///
+ /// The tag of the iptc value.
+ /// The datetime.
+ public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset)
+ {
+ if (!tag.IsDate() && !tag.IsTime())
+ {
+ throw new ArgumentException("iptc tag is not a time or date type");
+ }
+
+ var formattedDate = tag.IsDate()
+ ? dateTimeOffset.ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture)
+ : dateTimeOffset.ToString("HHmmsszzzz", System.Globalization.CultureInfo.InvariantCulture)
+ .Replace(":", string.Empty);
+
+ this.SetValue(tag, Encoding.UTF8, formattedDate);
+ }
+
+ ///
+ /// Sets the value of the specified tag.
+ ///
+ /// The tag of the iptc value.
+ /// The value.
+ ///
+ /// Indicates if length restrictions from the specification should be followed strictly.
+ /// Defaults to true.
+ ///
+ public void SetValue(IptcTag tag, string value, bool strict = true) => this.SetValue(tag, Encoding.UTF8, value, strict);
+
+ ///
+ /// Updates the data of the profile.
+ ///
+ public void UpdateData()
+ {
+ var length = 0;
+ foreach (IptcValue value in this.Values)
+ {
+ length += value.Length + 5;
+ }
+
+ this.Data = new byte[length];
+
+ int i = 0;
+ foreach (IptcValue value in this.Values)
+ {
+ this.Data[i++] = 28;
+ this.Data[i++] = 2;
+ this.Data[i++] = (byte)value.Tag;
+ this.Data[i++] = (byte)(value.Length >> 8);
+ this.Data[i++] = (byte)value.Length;
+ if (value.Length > 0)
+ {
+ Buffer.BlockCopy(value.ToByteArray(), 0, this.Data, i, value.Length);
+ i += value.Length;
+ }
+ }
+ }
+
+ private void Initialize()
+ {
+ if (this.values != null)
+ {
+ return;
+ }
+
+ this.values = new Collection();
+
+ if (this.Data == null || this.Data[0] != 0x1c)
+ {
+ return;
+ }
+
+ int i = 0;
+ while (i + 4 < this.Data.Length)
+ {
+ if (this.Data[i++] != 28)
+ {
+ continue;
+ }
+
+ i++;
+
+ var tag = (IptcTag)this.Data[i++];
+
+ int count = BinaryPrimitives.ReadInt16BigEndian(this.Data.AsSpan(i, 2));
+ i += 2;
+
+ var iptcData = new byte[count];
+ if ((count > 0) && (i + count <= this.Data.Length))
+ {
+ Buffer.BlockCopy(this.Data, i, iptcData, 0, count);
+ this.values.Add(new IptcValue(tag, iptcData, false));
+ }
+
+ i += count;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs
new file mode 100644
index 0000000000..7258a02917
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs
@@ -0,0 +1,397 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
+{
+ ///
+ /// Provides enumeration of all IPTC tags relevant for images.
+ ///
+ public enum IptcTag
+ {
+ ///
+ /// Unknown.
+ ///
+ Unknown = -1,
+
+ ///
+ /// Record version identifying the version of the Information Interchange Model.
+ /// Not repeatable. Max length is 2.
+ ///
+ RecordVersion = 0,
+
+ ///
+ /// Object type, not repeatable. Max Length is 67.
+ ///
+ ObjectType = 3,
+
+ ///
+ /// Object attribute. Max length is 68.
+ ///
+ ObjectAttribute = 4,
+
+ ///
+ /// Object Name, not repeatable. Max length is 64.
+ ///
+ Name = 5,
+
+ ///
+ /// Edit status, not repeatable. Max length is 64.
+ ///
+ EditStatus = 7,
+
+ ///
+ /// Editorial update, not repeatable. Max length is 2.
+ ///
+ EditorialUpdate = 8,
+
+ ///
+ /// Urgency, not repeatable. Max length is 2.
+ ///
+ Urgency = 10,
+
+ ///
+ /// Subject Reference. Max length is 236.
+ ///
+ SubjectReference = 12,
+
+ ///
+ /// Category, not repeatable. Max length is 3.
+ ///
+ Category = 15,
+
+ ///
+ /// Supplemental categories. Max length is 32.
+ ///
+ SupplementalCategories = 20,
+
+ ///
+ /// Fixture identifier, not repeatable. Max length is 32.
+ ///
+ FixtureIdentifier = 22,
+
+ ///
+ /// Keywords. Max length is 64.
+ ///
+ Keywords = 25,
+
+ ///
+ /// Location code. Max length is 3.
+ ///
+ LocationCode = 26,
+
+ ///
+ /// Location name. Max length is 64.
+ ///
+ LocationName = 27,
+
+ ///
+ /// Release date. Format should be CCYYMMDD.
+ /// Not repeatable, max length is 8.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ ///
+ ///
+ ReleaseDate = 30,
+
+ ///
+ /// Release time. Format should be HHMMSS±HHMM.
+ /// Not repeatable, max length is 11.
+ ///
+ /// A time value will be formatted as HHMMSS±HHMM, e.g. "090000+0200" for 9 o'clock Berlin time,
+ /// two hours ahead of UTC.
+ ///
+ ///
+ ReleaseTime = 35,
+
+ ///
+ /// Expiration date. Format should be CCYYMMDD.
+ /// Not repeatable, max length is 8.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ ///
+ ///
+ ExpirationDate = 37,
+
+ ///
+ /// Expiration time. Format should be HHMMSS±HHMM.
+ /// Not repeatable, max length is 11.
+ ///
+ /// A time value will be formatted as HHMMSS±HHMM, e.g. "090000+0200" for 9 o'clock Berlin time,
+ /// two hours ahead of UTC.
+ ///
+ ///
+ ExpirationTime = 38,
+
+ ///
+ /// Special instructions, not repeatable. Max length is 256.
+ ///
+ SpecialInstructions = 40,
+
+ ///
+ /// Action advised, not repeatable. Max length is 2.
+ ///
+ ActionAdvised = 42,
+
+ ///
+ /// Reference service. Max length is 10.
+ ///
+ ReferenceService = 45,
+
+ ///
+ /// Reference date. Format should be CCYYMMDD.
+ /// Not repeatable, max length is 8.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ ///
+ ///
+ ReferenceDate = 47,
+
+ ///
+ /// ReferenceNumber. Max length is 8.
+ ///
+ ReferenceNumber = 50,
+
+ ///
+ /// Created date. Format should be CCYYMMDD.
+ /// Not repeatable, max length is 8.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ ///
+ ///
+ CreatedDate = 55,
+
+ ///
+ /// Created time. Format should be HHMMSS±HHMM.
+ /// Not repeatable, max length is 11.
+ ///
+ /// A time value will be formatted as HHMMSS±HHMM, e.g. "090000+0200" for 9 o'clock Berlin time,
+ /// two hours ahead of UTC.
+ ///
+ ///
+ CreatedTime = 60,
+
+ ///
+ /// Digital creation date. Format should be CCYYMMDD.
+ /// Not repeatable, max length is 8.
+ ///
+ /// A date will be formatted as CCYYMMDD, e.g. "19890317" for 17 March 1989.
+ ///
+ ///
+ DigitalCreationDate = 62,
+
+ ///
+ /// Digital creation time. Format should be HHMMSS±HHMM.
+ /// Not repeatable, max length is 11.
+ ///
+ /// A time value will be formatted as HHMMSS±HHMM, e.g. "090000+0200" for 9 o'clock Berlin time,
+ /// two hours ahead of UTC.
+ ///
+ ///
+ DigitalCreationTime = 63,
+
+ ///
+ /// Originating program, not repeatable. Max length is 32.
+ ///
+ OriginatingProgram = 65,
+
+ ///
+ /// Program version, not repeatable. Max length is 10.
+ ///
+ ProgramVersion = 70,
+
+ ///
+ /// Object cycle, not repeatable. Max length is 1.
+ ///
+ ObjectCycle = 75,
+
+ ///
+ /// Byline. Max length is 32.
+ ///
+ Byline = 80,
+
+ ///
+ /// Byline title. Max length is 32.
+ ///
+ BylineTitle = 85,
+
+ ///
+ /// City, not repeatable. Max length is 32.
+ ///
+ City = 90,
+
+ ///
+ /// Sub location, not repeatable. Max length is 32.
+ ///
+ SubLocation = 92,
+
+ ///
+ /// Province/State, not repeatable. Max length is 32.
+ ///
+ ProvinceState = 95,
+
+ ///
+ /// Country code, not repeatable. Max length is 3.
+ ///
+ CountryCode = 100,
+
+ ///
+ /// Country, not repeatable. Max length is 64.
+ ///
+ Country = 101,
+
+ ///
+ /// Original transmission reference, not repeatable. Max length is 32.
+ ///
+ OriginalTransmissionReference = 103,
+
+ ///
+ /// Headline, not repeatable. Max length is 256.
+ ///
+ Headline = 105,
+
+ ///
+ /// Credit, not repeatable. Max length is 32.
+ ///
+ Credit = 110,
+
+ ///
+ /// Source, not repeatable. Max length is 32.
+ ///
+ Source = 115,
+
+ ///
+ /// Copyright notice, not repeatable. Max length is 128.
+ ///
+ CopyrightNotice = 116,
+
+ ///
+ /// Contact. Max length 128.
+ ///
+ Contact = 118,
+
+ ///
+ /// Caption, not repeatable. Max length is 2000.
+ ///
+ Caption = 120,
+
+ ///
+ /// Local caption.
+ ///
+ LocalCaption = 121,
+
+ ///
+ /// Caption writer. Max length is 32.
+ ///
+ CaptionWriter = 122,
+
+ ///
+ /// Image type, not repeatable. Max length is 2.
+ ///
+ ImageType = 130,
+
+ ///
+ /// Image orientation, not repeatable. Max length is 1.
+ ///
+ ImageOrientation = 131,
+
+ ///
+ /// Custom field 1
+ ///
+ CustomField1 = 200,
+
+ ///
+ /// Custom field 2
+ ///
+ CustomField2 = 201,
+
+ ///
+ /// Custom field 3
+ ///
+ CustomField3 = 202,
+
+ ///
+ /// Custom field 4
+ ///
+ CustomField4 = 203,
+
+ ///
+ /// Custom field 5
+ ///
+ CustomField5 = 204,
+
+ ///
+ /// Custom field 6
+ ///
+ CustomField6 = 205,
+
+ ///
+ /// Custom field 7
+ ///
+ CustomField7 = 206,
+
+ ///
+ /// Custom field 8
+ ///
+ CustomField8 = 207,
+
+ ///
+ /// Custom field 9
+ ///
+ CustomField9 = 208,
+
+ ///
+ /// Custom field 10
+ ///
+ CustomField10 = 209,
+
+ ///
+ /// Custom field 11
+ ///
+ CustomField11 = 210,
+
+ ///
+ /// Custom field 12
+ ///
+ CustomField12 = 211,
+
+ ///
+ /// Custom field 13
+ ///
+ CustomField13 = 212,
+
+ ///
+ /// Custom field 14
+ ///
+ CustomField14 = 213,
+
+ ///
+ /// Custom field 15
+ ///
+ CustomField15 = 214,
+
+ ///
+ /// Custom field 16
+ ///
+ CustomField16 = 215,
+
+ ///
+ /// Custom field 17
+ ///
+ CustomField17 = 216,
+
+ ///
+ /// Custom field 18
+ ///
+ CustomField18 = 217,
+
+ ///
+ /// Custom field 19
+ ///
+ CustomField19 = 218,
+
+ ///
+ /// Custom field 20
+ ///
+ CustomField20 = 219,
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs
new file mode 100644
index 0000000000..6b39769a7f
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs
@@ -0,0 +1,162 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
+{
+ ///
+ /// Extension methods for IPTC tags.
+ ///
+ public static class IptcTagExtensions
+ {
+ ///
+ /// Maximum length of the IPTC value with the given tag according to the specification.
+ ///
+ /// The tag to check the max length for.
+ /// The maximum length.
+ public static int MaxLength(this IptcTag tag)
+ {
+ return tag switch
+ {
+ IptcTag.RecordVersion => 2,
+ IptcTag.ObjectType => 67,
+ IptcTag.ObjectAttribute => 68,
+ IptcTag.Name => 64,
+ IptcTag.EditStatus => 64,
+ IptcTag.EditorialUpdate => 2,
+ IptcTag.Urgency => 1,
+ IptcTag.SubjectReference => 236,
+ IptcTag.Category => 3,
+ IptcTag.SupplementalCategories => 32,
+ IptcTag.FixtureIdentifier => 32,
+ IptcTag.Keywords => 64,
+ IptcTag.LocationCode => 3,
+ IptcTag.LocationName => 64,
+ IptcTag.ReleaseDate => 8,
+ IptcTag.ReleaseTime => 11,
+ IptcTag.ExpirationDate => 8,
+ IptcTag.ExpirationTime => 11,
+ IptcTag.SpecialInstructions => 256,
+ IptcTag.ActionAdvised => 2,
+ IptcTag.ReferenceService => 10,
+ IptcTag.ReferenceDate => 8,
+ IptcTag.ReferenceNumber => 8,
+ IptcTag.CreatedDate => 8,
+ IptcTag.CreatedTime => 11,
+ IptcTag.DigitalCreationDate => 8,
+ IptcTag.DigitalCreationTime => 11,
+ IptcTag.OriginatingProgram => 32,
+ IptcTag.ProgramVersion => 10,
+ IptcTag.ObjectCycle => 1,
+ IptcTag.Byline => 32,
+ IptcTag.BylineTitle => 32,
+ IptcTag.City => 32,
+ IptcTag.SubLocation => 32,
+ IptcTag.ProvinceState => 32,
+ IptcTag.CountryCode => 3,
+ IptcTag.Country => 64,
+ IptcTag.OriginalTransmissionReference => 32,
+ IptcTag.Headline => 256,
+ IptcTag.Credit => 32,
+ IptcTag.Source => 32,
+ IptcTag.CopyrightNotice => 128,
+ IptcTag.Contact => 128,
+ IptcTag.Caption => 2000,
+ IptcTag.CaptionWriter => 32,
+ IptcTag.ImageType => 2,
+ IptcTag.ImageOrientation => 1,
+ _ => 256
+ };
+ }
+
+ ///
+ /// Determines if the given tag can be repeated according to the specification.
+ ///
+ /// The tag to check.
+ /// True, if the tag can occur multiple times.
+ public static bool IsRepeatable(this IptcTag tag)
+ {
+ switch (tag)
+ {
+ case IptcTag.RecordVersion:
+ case IptcTag.ObjectType:
+ case IptcTag.Name:
+ case IptcTag.EditStatus:
+ case IptcTag.EditorialUpdate:
+ case IptcTag.Urgency:
+ case IptcTag.Category:
+ case IptcTag.FixtureIdentifier:
+ case IptcTag.ReleaseDate:
+ case IptcTag.ReleaseTime:
+ case IptcTag.ExpirationDate:
+ case IptcTag.ExpirationTime:
+ case IptcTag.SpecialInstructions:
+ case IptcTag.ActionAdvised:
+ case IptcTag.CreatedDate:
+ case IptcTag.CreatedTime:
+ case IptcTag.DigitalCreationDate:
+ case IptcTag.DigitalCreationTime:
+ case IptcTag.OriginatingProgram:
+ case IptcTag.ProgramVersion:
+ case IptcTag.ObjectCycle:
+ case IptcTag.City:
+ case IptcTag.SubLocation:
+ case IptcTag.ProvinceState:
+ case IptcTag.CountryCode:
+ case IptcTag.Country:
+ case IptcTag.OriginalTransmissionReference:
+ case IptcTag.Headline:
+ case IptcTag.Credit:
+ case IptcTag.Source:
+ case IptcTag.CopyrightNotice:
+ case IptcTag.Caption:
+ case IptcTag.ImageType:
+ case IptcTag.ImageOrientation:
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+ ///
+ /// Determines if the tag is a datetime tag which needs to be formatted as CCYYMMDD.
+ ///
+ /// The tag to check.
+ /// True, if its a datetime tag.
+ public static bool IsDate(this IptcTag tag)
+ {
+ switch (tag)
+ {
+ case IptcTag.CreatedDate:
+ case IptcTag.DigitalCreationDate:
+ case IptcTag.ExpirationDate:
+ case IptcTag.ReferenceDate:
+ case IptcTag.ReleaseDate:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Determines if the tag is a time tag which need to be formatted as HHMMSS±HHMM.
+ ///
+ /// The tag to check.
+ /// True, if its a time tag.
+ public static bool IsTime(this IptcTag tag)
+ {
+ switch (tag)
+ {
+ case IptcTag.CreatedTime:
+ case IptcTag.DigitalCreationTime:
+ case IptcTag.ExpirationTime:
+ case IptcTag.ReleaseTime:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs
new file mode 100644
index 0000000000..e63781012a
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs
@@ -0,0 +1,219 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Text;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
+{
+ ///
+ /// Represents a single value of the IPTC profile.
+ ///
+ public sealed class IptcValue : IDeepCloneable
+ {
+ private byte[] data = Array.Empty();
+ private Encoding encoding;
+
+ internal IptcValue(IptcValue other)
+ {
+ if (other.data != null)
+ {
+ this.data = new byte[other.data.Length];
+ other.data.AsSpan().CopyTo(this.data);
+ }
+
+ if (other.Encoding != null)
+ {
+ this.Encoding = (Encoding)other.Encoding.Clone();
+ }
+
+ this.Tag = other.Tag;
+ this.Strict = other.Strict;
+ }
+
+ internal IptcValue(IptcTag tag, byte[] value, bool strict)
+ {
+ Guard.NotNull(value, nameof(value));
+
+ this.Strict = strict;
+ this.Tag = tag;
+ this.data = value;
+ this.encoding = Encoding.UTF8;
+ }
+
+ internal IptcValue(IptcTag tag, Encoding encoding, string value, bool strict)
+ {
+ this.Strict = strict;
+ this.Tag = tag;
+ this.encoding = encoding;
+ this.Value = value;
+ }
+
+ internal IptcValue(IptcTag tag, string value, bool strict)
+ {
+ this.Strict = strict;
+ this.Tag = tag;
+ this.encoding = Encoding.UTF8;
+ this.Value = value;
+ }
+
+ ///
+ /// Gets or sets the encoding to use for the Value.
+ ///
+ public Encoding Encoding
+ {
+ get => this.encoding;
+ set
+ {
+ if (value != null)
+ {
+ this.encoding = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets the tag of the iptc value.
+ ///
+ public IptcTag Tag { get; }
+
+ ///
+ /// Gets or sets a value indicating whether to be enforce value length restrictions according
+ /// to the specification.
+ ///
+ public bool Strict { get; set; }
+
+ ///
+ /// Gets or sets the value.
+ ///
+ public string Value
+ {
+ get => this.encoding.GetString(this.data);
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ this.data = Array.Empty();
+ }
+ else
+ {
+ int maxLength = this.Tag.MaxLength();
+ byte[] valueBytes;
+ if (this.Strict && value.Length > maxLength)
+ {
+ var cappedValue = value.Substring(0, maxLength);
+ valueBytes = this.encoding.GetBytes(cappedValue);
+
+ // It is still possible that the bytes of the string exceed the limit.
+ if (valueBytes.Length > maxLength)
+ {
+ throw new ArgumentException($"The iptc value exceeds the limit of {maxLength} bytes for the tag {this.Tag}");
+ }
+ }
+ else
+ {
+ valueBytes = this.encoding.GetBytes(value);
+ }
+
+ this.data = valueBytes;
+ }
+ }
+ }
+
+ ///
+ /// Gets the length of the value.
+ ///
+ public int Length => this.data.Length;
+
+ ///
+ public IptcValue DeepClone() => new IptcValue(this);
+
+ ///
+ /// Determines whether the specified object is equal to the current .
+ ///
+ /// The object to compare this with.
+ /// True when the specified object is equal to the current .
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ return this.Equals(obj as IptcValue);
+ }
+
+ ///
+ /// Determines whether the specified iptc value is equal to the current .
+ ///
+ /// The iptc value to compare this with.
+ /// True when the specified iptc value is equal to the current .
+ public bool Equals(IptcValue other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ if (this.Tag != other.Tag)
+ {
+ return false;
+ }
+
+ if (this.data.Length != other.data.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < this.data.Length; i++)
+ {
+ if (this.data[i] != other.data[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Serves as a hash of this type.
+ ///
+ /// A hash code for the current instance.
+ public override int GetHashCode() => HashCode.Combine(this.data, this.Tag);
+
+ ///
+ /// Converts this instance to a byte array.
+ ///
+ /// A array.
+ public byte[] ToByteArray()
+ {
+ var result = new byte[this.data.Length];
+ this.data.CopyTo(result, 0);
+ return result;
+ }
+
+ ///
+ /// Returns a string that represents the current value.
+ ///
+ /// A string that represents the current value.
+ public override string ToString() => this.Value;
+
+ ///
+ /// Returns a string that represents the current value with the specified encoding.
+ ///
+ /// The encoding to use.
+ /// A string that represents the current value with the specified encoding.
+ public string ToString(Encoding encoding)
+ {
+ Guard.NotNull(encoding, nameof(encoding));
+
+ return encoding.GetString(this.data);
+ }
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/README.md b/src/ImageSharp/Metadata/Profiles/IPTC/README.md
new file mode 100644
index 0000000000..1217ca0c70
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/IPTC/README.md
@@ -0,0 +1,11 @@
+IPTC source code is from [Magick.NET](https://github.com/dlemstra/Magick.NET)
+
+Information about IPTC can be found here in the following sources:
+
+- [metacpan.org, APP13-segment](https://metacpan.org/pod/Image::MetaData::JPEG::Structures#Structure-of-a-Photoshop-style-APP13-segment)
+
+- [iptc.org](https://www.iptc.org/std/photometadata/documentation/userguide/)
+
+- [Adobe File Formats Specification](http://oldschoolprg.x10.mx/downloads/ps6ffspecsv2.pdf)
+
+- [Tag Overview](https://exiftool.org/TagNames/IPTC.html)
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs
index dde7beb3e9..1478d2951b 100644
--- a/src/ImageSharp/Processing/AffineTransformBuilder.cs
+++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -247,15 +247,33 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a raw matrix.
///
/// The matrix to prepend.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
- public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) => this.Prepend(_ => matrix);
+ public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix)
+ {
+ CheckDegenerate(matrix);
+ return this.Prepend(_ => matrix);
+ }
///
/// Appends a raw matrix.
///
/// The matrix to append.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
- public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) => this.Append(_ => matrix);
+ public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix)
+ {
+ CheckDegenerate(matrix);
+ return this.Append(_ => matrix);
+ }
///
/// Returns the combined matrix for a given source size.
@@ -268,6 +286,11 @@ namespace SixLabors.ImageSharp.Processing
/// Returns the combined matrix for a given source rectangle.
///
/// The rectangle in the source image.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
public Matrix3x2 BuildMatrix(Rectangle sourceRectangle)
{
@@ -284,9 +307,19 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size);
}
+ CheckDegenerate(matrix);
+
return matrix;
}
+ private static void CheckDegenerate(Matrix3x2 matrix)
+ {
+ if (TransformUtilities.IsDegenerate(matrix))
+ {
+ throw new DegenerateTransformException("Matrix is degenerate. Check input values.");
+ }
+ }
+
private AffineTransformBuilder Prepend(Func factory)
{
this.matrixFactories.Insert(0, factory);
@@ -299,4 +332,4 @@ namespace SixLabors.ImageSharp.Processing
return this;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
index 2e5919d1e3..714a45f5f0 100644
--- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
+++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
@@ -39,6 +40,9 @@ namespace SixLabors.ImageSharp.Processing
///
public Configuration Configuration { get; }
+ ///
+ public IDictionary Properties { get; } = new Dictionary();
+
///
public Image GetResultImage()
{
diff --git a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
index 4717c09eaf..3c25bb7c40 100644
--- a/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Drawing/DrawImageExtensions.cs
@@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing
Image image,
float opacity)
{
- var options = new GraphicsOptions();
+ var options = source.GetGraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
@@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing
image,
Point.Empty,
colorBlending,
- new GraphicsOptions().AlphaCompositionMode,
+ source.GetGraphicsOptions().AlphaCompositionMode,
opacity));
///
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Processing
Point location,
float opacity)
{
- var options = new GraphicsOptions();
+ var options = source.GetGraphicsOptions();
return source.ApplyProcessor(
new DrawImageProcessor(
image,
@@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Processing
image,
location,
colorBlending,
- new GraphicsOptions().AlphaCompositionMode,
+ source.GetGraphicsOptions().AlphaCompositionMode,
opacity));
///
diff --git a/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs
index 84b11c5e71..3f8a67feb3 100644
--- a/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Filters/LomographExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing
/// The image this method extends.
/// The to allow chaining of operations.
public static IImageProcessingContext Lomograph(this IImageProcessingContext source)
- => source.ApplyProcessor(new LomographProcessor());
+ => source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()));
///
/// Alters the colors of the image recreating an old Lomograph camera effect.
@@ -28,6 +28,6 @@ namespace SixLabors.ImageSharp.Processing
///
/// The to allow chaining of operations.
public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle)
- => source.ApplyProcessor(new LomographProcessor(), rectangle);
+ => source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()), rectangle);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs
index 94ced7108d..ab75ea56b5 100644
--- a/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Filters/PolaroidExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Filters;
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing
/// The image this method extends.
/// The to allow chaining of operations.
public static IImageProcessingContext Polaroid(this IImageProcessingContext source)
- => source.ApplyProcessor(new PolaroidProcessor());
+ => source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()));
///
/// Alters the colors of the image recreating an old Polaroid camera effect.
@@ -28,6 +28,6 @@ namespace SixLabors.ImageSharp.Processing
///
/// The to allow chaining of operations.
public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle)
- => source.ApplyProcessor(new PolaroidProcessor(), rectangle);
+ => source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()), rectangle);
}
}
diff --git a/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs
index d068ba10b5..21e244f0a3 100644
--- a/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Overlays/BackgroundColorExtensions.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing
/// The color to set as the background.
/// The to allow chaining of operations.
public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) =>
- BackgroundColor(source, new GraphicsOptions(), color);
+ BackgroundColor(source, source.GetGraphicsOptions(), color);
///
/// Replaces the background color of image with the given one.
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
Color color,
Rectangle rectangle) =>
- BackgroundColor(source, new GraphicsOptions(), color, rectangle);
+ BackgroundColor(source, source.GetGraphicsOptions(), color, rectangle);
///
/// Replaces the background color of image with the given one.
diff --git a/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs
index d5114e30ab..c3ce32e636 100644
--- a/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Overlays/GlowExtensions.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing
/// The image this method extends.
/// The to allow chaining of operations.
public static IImageProcessingContext Glow(this IImageProcessingContext source) =>
- Glow(source, new GraphicsOptions());
+ Glow(source, source.GetGraphicsOptions());
///
/// Applies a radial glow effect to an image.
@@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing
/// The to allow chaining of operations.
public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color)
{
- return Glow(source, new GraphicsOptions(), color);
+ return Glow(source, source.GetGraphicsOptions(), color);
}
///
@@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing
/// The the radius.
/// The to allow chaining of operations.
public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) =>
- Glow(source, new GraphicsOptions(), radius);
+ Glow(source, source.GetGraphicsOptions(), radius);
///
/// Applies a radial glow effect to an image.
@@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// The to allow chaining of operations.
public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) =>
- source.Glow(new GraphicsOptions(), rectangle);
+ source.Glow(source.GetGraphicsOptions(), rectangle);
///
/// Applies a radial glow effect to an image.
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing
Color color,
float radius,
Rectangle rectangle) =>
- source.Glow(new GraphicsOptions(), color, ValueSize.Absolute(radius), rectangle);
+ source.Glow(source.GetGraphicsOptions(), color, ValueSize.Absolute(radius), rectangle);
///
/// Applies a radial glow effect to an image.
diff --git a/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs
index 799b30e01e..b53880fc12 100644
--- a/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Overlays/VignetteExtensions.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing
/// The image this method extends.
/// The to allow chaining of operations.
public static IImageProcessingContext Vignette(this IImageProcessingContext source) =>
- Vignette(source, new GraphicsOptions());
+ Vignette(source, source.GetGraphicsOptions());
///
/// Applies a radial vignette effect to an image.
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing
/// The color to set as the vignette.
/// The to allow chaining of operations.
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) =>
- Vignette(source, new GraphicsOptions(), color);
+ Vignette(source, source.GetGraphicsOptions(), color);
///
/// Applies a radial vignette effect to an image.
@@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing
this IImageProcessingContext source,
float radiusX,
float radiusY) =>
- Vignette(source, new GraphicsOptions(), radiusX, radiusY);
+ Vignette(source, source.GetGraphicsOptions(), radiusX, radiusY);
///
/// Applies a radial vignette effect to an image.
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// The to allow chaining of operations.
public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) =>
- Vignette(source, new GraphicsOptions(), rectangle);
+ Vignette(source, source.GetGraphicsOptions(), rectangle);
///
/// Applies a radial vignette effect to an image.
@@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing
float radiusX,
float radiusY,
Rectangle rectangle) =>
- source.Vignette(new GraphicsOptions(), color, radiusX, radiusY, rectangle);
+ source.Vignette(source.GetGraphicsOptions(), color, radiusX, radiusY, rectangle);
///
/// Applies a radial vignette effect to an image.
diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs
index 59be5bf02c..45cff93982 100644
--- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs
@@ -19,6 +19,10 @@ namespace SixLabors.ImageSharp.Processing
///
/// The image to mutate.
/// The operation to perform on the source.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, Action operation)
=> Mutate(source, source.GetConfiguration(), operation);
@@ -28,6 +32,11 @@ namespace SixLabors.ImageSharp.Processing
/// The image to mutate.
/// The configuration which allows altering default behaviour or extending the library.
/// The operation to perform on the source.
+ /// The configuration is null.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, Configuration configuration, Action operation)
{
Guard.NotNull(configuration, nameof(configuration));
@@ -44,6 +53,10 @@ namespace SixLabors.ImageSharp.Processing
/// The pixel format.
/// The image to mutate.
/// The operation to perform on the source.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, Action operation)
where TPixel : unmanaged, IPixel
=> Mutate(source, source.GetConfiguration(), operation);
@@ -55,6 +68,11 @@ namespace SixLabors.ImageSharp.Processing
/// The image to mutate.
/// The configuration which allows altering default behaviour or extending the library.
/// The operation to perform on the source.
+ /// The configuration is null.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, Configuration configuration, Action operation)
where TPixel : unmanaged, IPixel
{
@@ -75,6 +93,10 @@ namespace SixLabors.ImageSharp.Processing
/// The pixel format.
/// The image to mutate.
/// The operations to perform on the source.
+ /// The source is null.
+ /// The operations are null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, params IImageProcessor[] operations)
where TPixel : unmanaged, IPixel
=> Mutate(source, source.GetConfiguration(), operations);
@@ -86,6 +108,11 @@ namespace SixLabors.ImageSharp.Processing
/// The image to mutate.
/// The configuration which allows altering default behaviour or extending the library.
/// The operations to perform on the source.
+ /// The configuration is null.
+ /// The source is null.
+ /// The operations are null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static void Mutate(this Image source, Configuration configuration, params IImageProcessor[] operations)
where TPixel : unmanaged, IPixel
{
@@ -104,7 +131,11 @@ namespace SixLabors.ImageSharp.Processing
///
/// The image to clone.
/// The operation to perform on the clone.
- /// The new .
+ /// The new .
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
public static Image Clone(this Image source, Action operation)
=> Clone(source, source.GetConfiguration(), operation);
@@ -114,7 +145,12 @@ namespace SixLabors.ImageSharp.Processing
/// The image to clone.
/// The configuration which allows altering default behaviour or extending the library.
/// The operation to perform on the clone.
- /// The new .
+ /// The configuration is null.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
+ /// The new .
public static Image Clone(this Image source, Configuration configuration, Action operation)
{
Guard.NotNull(configuration, nameof(configuration));
@@ -133,7 +169,11 @@ namespace SixLabors.ImageSharp.Processing
/// The pixel format.
/// The image to clone.
/// The operation to perform on the clone.
- /// The new
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
+ /// The new .
public static Image Clone(this Image source, Action operation)
where TPixel : unmanaged, IPixel
=> Clone(source, source.GetConfiguration(), operation);
@@ -145,7 +185,12 @@ namespace SixLabors.ImageSharp.Processing
/// The image to clone.
/// The configuration which allows altering default behaviour or extending the library.
/// The operation to perform on the clone.
- /// The new
+ /// The configuration is null.
+ /// The source is null.
+ /// The operation is null.
+ /// The source has been disposed.
+ /// The processing operation failed.
+ /// The new
public static Image Clone(this Image source, Configuration configuration, Action operation)
where TPixel : unmanaged, IPixel
{
@@ -167,7 +212,11 @@ namespace SixLabors.ImageSharp.Processing
/// The pixel format.
/// The image to clone.
/// The operations to perform on the clone.
- /// The new
+ /// The source is null.
+ /// The operations are null.
+ /// The source has been disposed.
+ /// The processing operation failed.
+ /// The new
public static Image Clone(this Image source, params IImageProcessor[] operations)
where TPixel : unmanaged, IPixel
=> Clone(source, source.GetConfiguration(), operations);
@@ -179,7 +228,12 @@ namespace SixLabors.ImageSharp.Processing
/// The image to clone.
/// The configuration which allows altering default behaviour or extending the library.
/// The operations to perform on the clone.
- /// The new
+ /// The configuration is null.
+ /// The source is null.
+ /// The operations are null.
+ /// The source has been disposed.
+ /// The processing operation failed.
+ /// The new
public static Image Clone(this Image source, Configuration configuration, params IImageProcessor[] operations)
where TPixel : unmanaged, IPixel
{
@@ -200,6 +254,7 @@ namespace SixLabors.ImageSharp.Processing
///
/// The image processing context.
/// The operations to perform on the source.
+ /// The processing operation failed.
/// The to allow chaining of operations.
public static IImageProcessingContext ApplyProcessors(
this IImageProcessingContext source,
diff --git a/src/ImageSharp/Processing/IImageProcessingContext.cs b/src/ImageSharp/Processing/IImageProcessingContext.cs
index 8b57a289d4..cb39766a94 100644
--- a/src/ImageSharp/Processing/IImageProcessingContext.cs
+++ b/src/ImageSharp/Processing/IImageProcessingContext.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Collections.Generic;
using SixLabors.ImageSharp.Processing.Processors;
namespace SixLabors.ImageSharp.Processing
@@ -15,6 +16,12 @@ namespace SixLabors.ImageSharp.Processing
///
Configuration Configuration { get; }
+ ///
+ /// Gets a set of properties for the Image Processing Context.
+ ///
+ /// This can be used for storing global settings and defaults to be accessable to processors.
+ IDictionary Properties { get; }
+
///
/// Gets the image dimensions at the current point in the processing pipeline.
///
diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
index 3c150d7ebf..bb6ea51c12 100644
--- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
@@ -11,11 +11,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
///
/// Initializes a new instance of the class.
///
- public LomographProcessor()
+ /// Graphics options to use within the processor.
+ public LomographProcessor(GraphicsOptions graphicsOptions)
: base(KnownFilterMatrices.LomographFilter)
{
+ this.GraphicsOptions = graphicsOptions;
}
+ ///
+ /// Gets the options effecting blending and composition
+ ///
+ public GraphicsOptions GraphicsOptions { get; }
+
///
public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) =>
new LomographProcessor(configuration, this, source, sourceRectangle);
diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs
index 5a19b78516..0706e9fc8d 100644
--- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs
@@ -13,6 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
where TPixel : unmanaged, IPixel
{
private static readonly Color VeryDarkGreen = Color.FromRgba(0, 10, 0, 255);
+ private readonly LomographProcessor definition;
///
/// Initializes a new instance of the class.
@@ -24,12 +25,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
public LomographProcessor(Configuration configuration, LomographProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, definition, source, sourceRectangle)
{
+ this.definition = definition;
}
///
protected override void AfterImageApply()
{
- new VignetteProcessor(VeryDarkGreen).Execute(this.Configuration, this.Source, this.SourceRectangle);
+ new VignetteProcessor(this.definition.GraphicsOptions, VeryDarkGreen).Execute(this.Configuration, this.Source, this.SourceRectangle);
base.AfterImageApply();
}
}
diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
index a5cf268625..965a35be15 100644
--- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
@@ -11,11 +11,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
///
/// Initializes a new instance of the class.
///
- public PolaroidProcessor()
+ /// Graphics options to use within the processor.
+ public PolaroidProcessor(GraphicsOptions graphicsOptions)
: base(KnownFilterMatrices.PolaroidFilter)
{
+ this.GraphicsOptions = graphicsOptions;
}
+ ///
+ /// Gets the options effecting blending and composition
+ ///
+ public GraphicsOptions GraphicsOptions { get; }
+
///
public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) =>
new PolaroidProcessor(configuration, this, source, sourceRectangle);
diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs
index 9f547be1c9..470d553c2c 100644
--- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs
@@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
private static readonly Color LightOrange = Color.FromRgba(255, 153, 102, 128);
private static readonly Color VeryDarkOrange = Color.FromRgb(102, 34, 0);
+ private readonly PolaroidProcessor definition;
///
/// Initializes a new instance of the class.
@@ -25,13 +26,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
public PolaroidProcessor(Configuration configuration, PolaroidProcessor definition, Image source, Rectangle sourceRectangle)
: base(configuration, definition, source, sourceRectangle)
{
+ this.definition = definition;
}
///
protected override void AfterImageApply()
{
- new VignetteProcessor(VeryDarkOrange).Execute(this.Configuration, this.Source, this.SourceRectangle);
- new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Configuration, this.Source, this.SourceRectangle);
+ new VignetteProcessor(this.definition.GraphicsOptions, VeryDarkOrange).Execute(this.Configuration, this.Source, this.SourceRectangle);
+ new GlowProcessor(this.definition.GraphicsOptions, LightOrange, this.Source.Width / 4F).Execute(this.Configuration, this.Source, this.SourceRectangle);
base.AfterImageApply();
}
}
diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
index 87e93ca215..5e0d1cbf7e 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
@@ -10,15 +10,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
///
public sealed class GlowProcessor : IImageProcessor
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The color or the glow.
- public GlowProcessor(Color color)
- : this(color, 0)
- {
- }
-
///
/// Initializes a new instance of the class.
///
@@ -29,16 +20,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The color or the glow.
- /// The radius of the glow.
- internal GlowProcessor(Color color, ValueSize radius)
- : this(new GraphicsOptions(), color, radius)
- {
- }
-
///
/// Initializes a new instance of the class.
///
diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
index 5654eccfa4..3b16f8bc85 100644
--- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
@@ -10,15 +10,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
///
public sealed class VignetteProcessor : IImageProcessor
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The color of the vignette.
- public VignetteProcessor(Color color)
- : this(new GraphicsOptions(), color)
- {
- }
-
///
/// Initializes a new instance of the class.
///
diff --git a/src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs b/src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs
new file mode 100644
index 0000000000..4d46540bcc
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Transforms/DegenerateTransformException.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Processing.Processors.Transforms
+{
+ ///
+ /// Represents an error that occurs during a transform operation.
+ ///
+ public sealed class DegenerateTransformException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DegenerateTransformException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ /// The message that describes the error.
+ public DegenerateTransformException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception that is
+ /// the cause of this exception.
+ ///
+ /// The error message that explains the reason for the exception.
+ /// The exception that is the cause of the current exception, or a null reference ( in Visual Basic) if no inner exception is specified.
+ public DegenerateTransformException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
index 0760d2e3e7..b474b43712 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs
@@ -12,6 +12,57 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
internal static class TransformUtilities
{
+ ///
+ /// Returns a value that indicates whether the specified matrix is degenerate
+ /// containing one or more values equivalent to or a
+ /// zero determinant and therefore cannot be used for linear transforms.
+ ///
+ /// The transform matrix.
+ public static bool IsDegenerate(Matrix3x2 matrix)
+ => IsNaN(matrix) || IsZero(matrix.GetDeterminant());
+
+ ///
+ /// Returns a value that indicates whether the specified matrix is degenerate
+ /// containing one or more values equivalent to or a
+ /// zero determinant and therefore cannot be used for linear transforms.
+ ///
+ /// The transform matrix.
+ public static bool IsDegenerate(Matrix4x4 matrix)
+ => IsNaN(matrix) || IsZero(matrix.GetDeterminant());
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static bool IsZero(float a)
+ => a > -Constants.EpsilonSquared && a < Constants.EpsilonSquared;
+
+ ///
+ /// Returns a value that indicates whether the specified matrix contains any values
+ /// that are not a number .
+ ///
+ /// The transform matrix.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool IsNaN(Matrix3x2 matrix)
+ {
+ return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12)
+ || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22)
+ || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32);
+ }
+
+ ///
+ /// Returns a value that indicates whether the specified matrix contains any values
+ /// that are not a number .
+ ///
+ /// The transform matrix.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool IsNaN(Matrix4x4 matrix)
+ {
+ return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14)
+ || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24)
+ || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34)
+ || float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44);
+ }
+
///
/// Applies the projective transform against the given coordinates flattened into the 2D space.
///
diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
index ef44dc16d0..b7e65b4cc0 100644
--- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
+++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
@@ -1,9 +1,10 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Numerics;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
namespace SixLabors.ImageSharp.Processing
@@ -263,27 +264,51 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a raw matrix.
///
/// The matrix to prepend.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
- public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) => this.Prepend(_ => matrix);
+ public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix)
+ {
+ CheckDegenerate(matrix);
+ return this.Prepend(_ => matrix);
+ }
///
/// Appends a raw matrix.
///
/// The matrix to append.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
- public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) => this.Append(_ => matrix);
+ public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix)
+ {
+ CheckDegenerate(matrix);
+ return this.Append(_ => matrix);
+ }
///
/// Returns the combined matrix for a given source size.
///
/// The source image size.
/// The .
- public Matrix4x4 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
+ public Matrix4x4 BuildMatrix(Size sourceSize)
+ => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize));
///
/// Returns the combined matrix for a given source rectangle.
///
/// The rectangle in the source image.
+ ///
+ /// The resultant matrix is degenerate containing one or more values equivalent
+ /// to or a zero determinant and therefore cannot be used
+ /// for linear transforms.
+ ///
/// The .
public Matrix4x4 BuildMatrix(Rectangle sourceRectangle)
{
@@ -300,9 +325,19 @@ namespace SixLabors.ImageSharp.Processing
matrix *= factory(size);
}
+ CheckDegenerate(matrix);
+
return matrix;
}
+ private static void CheckDegenerate(Matrix4x4 matrix)
+ {
+ if (TransformUtilities.IsDegenerate(matrix))
+ {
+ throw new DegenerateTransformException("Matrix is degenerate. Check input values.");
+ }
+ }
+
private ProjectiveTransformBuilder Prepend(Func factory)
{
this.matrixFactories.Insert(0, factory);
@@ -315,4 +350,4 @@ namespace SixLabors.ImageSharp.Processing
return this;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
index dca124849a..97731be94e 100644
--- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
+++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
@@ -61,7 +61,10 @@ namespace SixLabors.ImageSharp.Tests.Advanced
{
using Image image0 = provider.GetImage();
var targetBuffer = new TPixel[image0.Width * image0.Height];
- image0.GetPixelSpan().CopyTo(targetBuffer);
+
+ Assert.True(image0.TryGetSinglePixelSpan(out Span sourceBuffer));
+
+ sourceBuffer.CopyTo(targetBuffer);
var managerOfExternalMemory = new TestMemoryManager(targetBuffer);
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
new file mode 100644
index 0000000000..0aff95d994
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageExtensionsTests.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Processing.Processors.Drawing;
+using SixLabors.ImageSharp.Tests.Processing;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Drawing
+{
+ public class DrawImageExtensionsTests : BaseImageOperationsExtensionTest
+ {
+ [Fact]
+ public void DrawImage_OpacityOnly_VerifyGraphicOptionsTakenFromContext()
+ {
+ // non-default values as we cant easly defect usage otherwise
+ this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
+ this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
+
+ this.operations.DrawImage(null, 0.5f);
+ DrawImageProcessor dip = this.Verify();
+
+ Assert.Equal(0.5, dip.Opacity);
+ Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
+ Assert.Equal(this.options.ColorBlendingMode, dip.ColorBlendingMode);
+ }
+
+ [Fact]
+ public void DrawImage_OpacityAndBlending_VerifyGraphicOptionsTakenFromContext()
+ {
+ // non-default values as we cant easly defect usage otherwise
+ this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
+ this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
+
+ this.operations.DrawImage(null, PixelColorBlendingMode.Multiply, 0.5f);
+ DrawImageProcessor dip = this.Verify();
+
+ Assert.Equal(0.5, dip.Opacity);
+ Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
+ Assert.Equal(PixelColorBlendingMode.Multiply, dip.ColorBlendingMode);
+ }
+
+ [Fact]
+ public void DrawImage_LocationAndOpacity_VerifyGraphicOptionsTakenFromContext()
+ {
+ // non-default values as we cant easly defect usage otherwise
+ this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
+ this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
+
+ this.operations.DrawImage(null, Point.Empty, 0.5f);
+ DrawImageProcessor dip = this.Verify();
+
+ Assert.Equal(0.5, dip.Opacity);
+ Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
+ Assert.Equal(this.options.ColorBlendingMode, dip.ColorBlendingMode);
+ }
+
+ [Fact]
+ public void DrawImage_LocationAndOpacityAndBlending_VerifyGraphicOptionsTakenFromContext()
+ {
+ // non-default values as we cant easly defect usage otherwise
+ this.options.AlphaCompositionMode = PixelAlphaCompositionMode.Xor;
+ this.options.ColorBlendingMode = PixelColorBlendingMode.Screen;
+
+ this.operations.DrawImage(null, Point.Empty, PixelColorBlendingMode.Multiply, 0.5f);
+ DrawImageProcessor dip = this.Verify();
+
+ Assert.Equal(0.5, dip.Opacity);
+ Assert.Equal(this.options.AlphaCompositionMode, dip.AlphaCompositionMode);
+ Assert.Equal(PixelColorBlendingMode.Multiply, dip.ColorBlendingMode);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
index c3bc2f2ae2..d08d81899b 100644
--- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs
@@ -128,7 +128,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (Image background = provider.GetImage())
using (var overlay = new Image(50, 50))
{
- overlay.GetPixelSpan().Fill(Color.Black);
+ Assert.True(overlay.TryGetSinglePixelSpan(out Span overlaySpan));
+ overlaySpan.Fill(Color.Black);
background.Mutate(c => c.DrawImage(overlay, new Point(x, y), PixelColorBlendingMode.Normal, 1F));
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
index a8376f51b9..85cdf6d11a 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
@@ -394,10 +394,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
[Theory]
[WithFile(InvalidPaletteSize, PixelTypes.Rgba32)]
- public void BmpDecoder_ThrowsImageFormatException_OnInvalidPaletteSize(TestImageProvider provider)
+ public void BmpDecoder_ThrowsInvalidImageContentException_OnInvalidPaletteSize(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- Assert.Throws(() =>
+ Assert.Throws(() =>
{
using (provider.GetImage(BmpDecoder))
{
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
index fa28993720..b3a99aa1c0 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
@@ -164,7 +164,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
ImageFrame first = kumin1.Frames[i];
ImageFrame second = kumin2.Frames[i];
- first.ComparePixelBufferTo(second.GetPixelSpan());
+
+ Assert.True(second.TryGetSinglePixelSpan(out Span secondSpan));
+
+ first.ComparePixelBufferTo(secondSpan);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
index cf2e5c81b3..57051a9d7b 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[WithFileCollection(nameof(UnrecoverableTestJpegs), PixelTypes.Rgba32)]
- public void UnrecoverableImage_Throws_ImageFormatException(TestImageProvider provider)
- where TPixel : unmanaged, IPixel => Assert.Throws(provider.GetImage);
+ public void UnrecoverableImage_Throws_InvalidImageContentException(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => Assert.Throws(provider.GetImage);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index bb79abf54d..6cbdb83fcd 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -1,10 +1,14 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
+using SixLabors.ImageSharp.Metadata.Profiles.Icc;
+using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@@ -215,5 +219,70 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
}
+
+ [Fact]
+ public void Encode_PreservesIptcProfile()
+ {
+ // arrange
+ using var input = new Image(1, 1);
+ input.Metadata.IptcProfile = new IptcProfile();
+ input.Metadata.IptcProfile.SetValue(IptcTag.Byline, "unit_test");
+ var encoder = new JpegEncoder();
+
+ // act
+ using var memStream = new MemoryStream();
+ input.Save(memStream, encoder);
+
+ // assert
+ memStream.Position = 0;
+ using var output = Image.Load(memStream);
+ IptcProfile actual = output.Metadata.IptcProfile;
+ Assert.NotNull(actual);
+ IEnumerable values = input.Metadata.IptcProfile.Values;
+ Assert.Equal(values, actual.Values);
+ }
+
+ [Fact]
+ public void Encode_PreservesExifProfile()
+ {
+ // arrange
+ using var input = new Image(1, 1);
+ input.Metadata.ExifProfile = new ExifProfile();
+ input.Metadata.ExifProfile.SetValue(ExifTag.Software, "unit_test");
+ var encoder = new JpegEncoder();
+
+ // act
+ using var memStream = new MemoryStream();
+ input.Save(memStream, encoder);
+
+ // assert
+ memStream.Position = 0;
+ using var output = Image.Load(memStream);
+ ExifProfile actual = output.Metadata.ExifProfile;
+ Assert.NotNull(actual);
+ IReadOnlyList values = input.Metadata.ExifProfile.Values;
+ Assert.Equal(values, actual.Values);
+ }
+
+ [Fact]
+ public void Encode_PreservesIccProfile()
+ {
+ // arrange
+ using var input = new Image(1, 1);
+ input.Metadata.IccProfile = new IccProfile(IccTestDataProfiles.Profile_Random_Array);
+ var encoder = new JpegEncoder();
+
+ // act
+ using var memStream = new MemoryStream();
+ input.Save(memStream, encoder);
+
+ // assert
+ memStream.Position = 0;
+ using var output = Image.Load(memStream);
+ IccProfile actual = output.Metadata.IccProfile;
+ Assert.NotNull(actual);
+ IccProfile values = input.Metadata.IccProfile;
+ Assert.Equal(values.Entries, actual.Entries);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
index 2e8c0de272..3a207722bd 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs
@@ -13,15 +13,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Fact]
public void ChunkTypeIdsAreCorrect()
{
- Assert.Equal(PngChunkType.Header, GetType("IHDR"));
- Assert.Equal(PngChunkType.Palette, GetType("PLTE"));
- Assert.Equal(PngChunkType.Data, GetType("IDAT"));
- Assert.Equal(PngChunkType.End, GetType("IEND"));
+ Assert.Equal(PngChunkType.Header, GetType("IHDR"));
+ Assert.Equal(PngChunkType.Palette, GetType("PLTE"));
+ Assert.Equal(PngChunkType.Data, GetType("IDAT"));
+ Assert.Equal(PngChunkType.End, GetType("IEND"));
Assert.Equal(PngChunkType.Transparency, GetType("tRNS"));
- Assert.Equal(PngChunkType.Text, GetType("tEXt"));
- Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
- Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
- Assert.Equal(PngChunkType.Exif, GetType("eXIf"));
+ Assert.Equal(PngChunkType.Text, GetType("tEXt"));
+ Assert.Equal(PngChunkType.InternationalText, GetType("iTXt"));
+ Assert.Equal(PngChunkType.CompressedText, GetType("zTXt"));
+ Assert.Equal(PngChunkType.Chroma, GetType("cHRM"));
+ Assert.Equal(PngChunkType.Gamma, GetType("gAMA"));
+ Assert.Equal(PngChunkType.Physical, GetType("pHYs"));
+ Assert.Equal(PngChunkType.Exif, GetType("eXIf"));
+ Assert.Equal(PngChunkType.Time, GetType("tIME"));
+ Assert.Equal(PngChunkType.Background, GetType("bKGD"));
+ Assert.Equal(PngChunkType.EmbeddedColorProfile, GetType("iCCP"));
+ Assert.Equal(PngChunkType.StandardRgbColourSpace, GetType("sRGB"));
+ Assert.Equal(PngChunkType.SignificantBits, GetType("sBIT"));
+ Assert.Equal(PngChunkType.Histogram, GetType("hIST"));
+ Assert.Equal(PngChunkType.SuggestedPalette, GetType("sPLT"));
+ Assert.Equal(PngChunkType.ProprietaryApple, GetType("CgBI"));
}
private static PngChunkType GetType(string text)
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
index ee4001c203..6cefff95d4 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Text;
using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
@@ -72,12 +73,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
var decoder = new PngDecoder();
ImageFormatException exception =
- Assert.Throws(() => decoder.Decode(null, memStream));
+ Assert.Throws(() => decoder.Decode(null, memStream));
Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message);
}
}
+ [Fact]
+ public void CalculateCrc_Works()
+ {
+ // arrange
+ var data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
+ var crc = new Crc32();
+
+ // act
+ crc.Update(data);
+
+ // assert
+ Assert.Equal(0x88AA689F, crc.Value);
+ }
+
private static string GetChunkTypeName(uint value)
{
var data = new byte[4];
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 46112bdd81..6eb6e93db8 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -4,7 +4,6 @@
using System.IO;
using Microsoft.DotNet.RemoteExecutor;
-using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -26,67 +25,40 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
public static readonly string[] CommonTestImages =
{
TestImages.Png.Splash,
- TestImages.Png.Indexed,
TestImages.Png.FilterVar,
- TestImages.Png.Bad.ChunkLength1,
- TestImages.Png.Bad.CorruptedChunk,
TestImages.Png.VimImage1,
+ TestImages.Png.VimImage2,
TestImages.Png.VersioningImage1,
TestImages.Png.VersioningImage2,
TestImages.Png.SnakeGame,
- TestImages.Png.Banner7Adam7InterlaceMode,
- TestImages.Png.Banner8Index,
-
- TestImages.Png.Bad.ChunkLength2,
- TestImages.Png.VimImage2,
TestImages.Png.Rgb24BppTrans,
- TestImages.Png.GrayA8Bit,
- TestImages.Png.Gray1BitTrans,
- TestImages.Png.Bad.ZlibOverflow,
- TestImages.Png.Bad.ZlibOverflow2,
- TestImages.Png.Bad.ZlibZtxtBadHeader,
- TestImages.Png.Bad.Issue1047_BadEndChunk
- };
- public static readonly string[] TestImages48Bpp =
- {
- TestImages.Png.Rgb48Bpp,
- TestImages.Png.Rgb48BppInterlaced
- };
-
- public static readonly string[] TestImages64Bpp =
- {
- TestImages.Png.Rgba64Bpp,
- TestImages.Png.Rgb48BppTrans
+ TestImages.Png.Bad.ChunkLength1,
+ TestImages.Png.Bad.ChunkLength2,
};
- public static readonly string[] TestImagesL16Bit =
+ public static readonly string[] TestImagesIssue1014 =
{
- TestImages.Png.L16Bit,
+ TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2,
+ TestImages.Png.Issue1014_3, TestImages.Png.Issue1014_4,
+ TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6
};
- public static readonly string[] TestImagesGrayAlpha16Bit =
+ public static readonly string[] TestImagesIssue1177 =
{
- TestImages.Png.GrayAlpha16Bit,
- TestImages.Png.GrayTrns16BitInterlaced
+ TestImages.Png.Issue1177_1,
+ TestImages.Png.Issue1177_2
};
- public static readonly string[] TestImagesL8BitInterlaced =
- {
- TestImages.Png.GrayAlpha1BitInterlaced,
- TestImages.Png.GrayAlpha2BitInterlaced,
- TestImages.Png.Gray4BitInterlaced,
- TestImages.Png.GrayA8BitInterlaced
- };
-
- public static readonly string[] TestImagesIssue1014 =
+ public static readonly string[] CorruptedTestImages =
{
- TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2,
- TestImages.Png.Issue1014_3, TestImages.Png.Issue1014_4,
- TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6
+ TestImages.Png.Bad.CorruptedChunk,
+ TestImages.Png.Bad.ZlibOverflow,
+ TestImages.Png.Bad.ZlibOverflow2,
+ TestImages.Png.Bad.ZlibZtxtBadHeader,
};
[Theory]
@@ -97,25 +69,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
using (Image image = provider.GetImage(PngDecoder))
{
image.DebugSave(provider);
+ image.CompareToOriginal(provider, ImageComparer.Exact);
+ }
+ }
- // We don't have another x-plat reference decoder that can be compared for this image.
- if (provider.Utility.SourceFileOrDescription == TestImages.Png.Bad.Issue1047_BadEndChunk)
- {
- if (TestEnvironment.IsWindows)
- {
- image.CompareToOriginal(provider, ImageComparer.Exact, (IImageDecoder)SystemDrawingReferenceDecoder.Instance);
- }
- }
- else
- {
- image.CompareToOriginal(provider, ImageComparer.Exact);
- }
+ [Theory]
+ [WithFile(TestImages.Png.GrayA8Bit, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.Gray1BitTrans, PixelTypes.Rgba32)]
+ public void Decode_GrayWithAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image image = provider.GetImage(PngDecoder))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, ImageComparer.Exact);
}
}
[Theory]
[WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)]
- public void Decode_Interlaced_ImageIsCorrect(TestImageProvider provider)
+ [WithFile(TestImages.Png.Banner7Adam7InterlaceMode, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.Banner8Index, PixelTypes.Rgba32)]
+ public void Decode_Interlaced(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
using (Image image = provider.GetImage(PngDecoder))
@@ -126,7 +101,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
}
[Theory]
- [WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgb48)]
+ [WithFile(TestImages.Png.Indexed, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.Banner8Index, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.PalettedTwoColor, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.PalettedFourColor, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.PalettedSixteenColor, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.Paletted256Colors, PixelTypes.Rgba32)]
+ public void Decode_Indexed(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image image = provider.GetImage(PngDecoder))
+ {
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, ImageComparer.Exact);
+ }
+ }
+
+ [Theory]
+ [WithFile(TestImages.Png.Rgb48Bpp, PixelTypes.Rgb48)]
+ [WithFile(TestImages.Png.Rgb48BppInterlaced, PixelTypes.Rgb48)]
public void Decode_48Bpp