From 5f43656f8cd643b5f09e2acc7eaa6c56a1ce8861 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 14:57:03 +0100 Subject: [PATCH 1/7] Always read CRC. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 7 +----- .../Formats/Png/PngDecoderTests.cs | 23 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 4 ++++ .../Images/Input/Png/issues/Issue_1177_1.png | 3 +++ .../Images/Input/Png/issues/Issue_1177_2.png | 3 +++ 5 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/Images/Input/Png/issues/Issue_1177_1.png create mode 100644 tests/Images/Input/Png/issues/Issue_1177_2.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index bc7b9d815..e2cd80631 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1141,11 +1141,6 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . private void ValidateChunk(in PngChunk chunk) { - if (!chunk.IsCritical) - { - return; - } - Span chunkType = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); @@ -1155,7 +1150,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.crc.Update(chunk.Data.GetSpan()); uint crc = this.ReadChunkCrc(); - if (this.crc.Value != crc) + if (this.crc.Value != crc && chunk.IsCritical) { string chunkTypeName = Encoding.ASCII.GetString(chunkType); PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 46112bdd8..b08025f53 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -89,6 +89,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6 }; + public static readonly string[] TestImagesIssue1177 = + { + TestImages.Png.Issue1177_1, + TestImages.Png.Issue1177_2 + }; + [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) @@ -243,6 +249,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Null(ex); } + [Theory] + [WithFileCollection(nameof(TestImagesIssue1177), PixelTypes.Rgba32)] + public void Issue1177(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + System.Exception ex = Record.Exception( + () => + { + using (Image image = provider.GetImage(PngDecoder)) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + }); + Assert.Null(ex); + } + [Theory] [WithFile(TestImages.Png.Issue1127, PixelTypes.Rgba32)] public void Issue1127(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 65d975257..18fd84331 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -95,6 +95,10 @@ namespace SixLabors.ImageSharp.Tests public const string Issue1014_6 = "Png/issues/Issue_1014_6.png"; public const string Issue1127 = "Png/issues/Issue_1127.png"; + // Issue 1177: https://github.com/SixLabors/ImageSharp/issues/1177 + public const string Issue1177_1 = "Png/issues/Issue_1177_1.png"; + public const string Issue1177_2 = "Png/issues/Issue_1177_2.png"; + public static class Bad { // Odd chunk lengths diff --git a/tests/Images/Input/Png/issues/Issue_1177_1.png b/tests/Images/Input/Png/issues/Issue_1177_1.png new file mode 100644 index 000000000..2d851e31b --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1177_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cef2be6012f4604f9f30b51273661058df0201be4de508235f372eb2304b2132 +size 7023 diff --git a/tests/Images/Input/Png/issues/Issue_1177_2.png b/tests/Images/Input/Png/issues/Issue_1177_2.png new file mode 100644 index 000000000..efd043b38 --- /dev/null +++ b/tests/Images/Input/Png/issues/Issue_1177_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7067af724977e1ecd8fc761f50226eaaa9e9d4142be963b4edbbf0918b8eba1d +size 57125 From 842106ae3ae67482c099b96d5ee9cb06544052e1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 18:15:00 +0100 Subject: [PATCH 2/7] No need to calculate CRC for non-critical chunks. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index e2cd80631..272f93d10 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1141,19 +1141,22 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . private void ValidateChunk(in PngChunk chunk) { - Span chunkType = stackalloc byte[4]; + uint crc = this.ReadChunkCrc(); - BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); + if (chunk.IsCritical) + { + Span chunkType = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); - this.crc.Reset(); - this.crc.Update(chunkType); - this.crc.Update(chunk.Data.GetSpan()); + this.crc.Reset(); + this.crc.Update(chunkType); + this.crc.Update(chunk.Data.GetSpan()); - uint crc = this.ReadChunkCrc(); - if (this.crc.Value != crc && chunk.IsCritical) - { - string chunkTypeName = Encoding.ASCII.GetString(chunkType); - PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName); + if (this.crc.Value != crc) + { + string chunkTypeName = Encoding.ASCII.GetString(chunkType); + PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName); + } } } From bff2d52c65c073efac91dd63091f1198743c30cb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 19:50:31 +0100 Subject: [PATCH 3/7] Introduce InvalidImageContentException, document and guard all load methods --- .../InvalidImageContentException.cs | 22 +++ src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 10 +- src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs | 16 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 2 +- src/ImageSharp/Formats/Gif/GifThrowHelper.cs | 18 +++ .../JpegColorConverter.FromYCbCrSimd.cs | 4 +- .../Jpeg/Components/Decoder/JFifMarker.cs | 4 +- .../Formats/Jpeg/JpegDecoderCore.cs | 18 +-- .../Formats/Jpeg/JpegThrowHelper.cs | 27 ++-- src/ImageSharp/Formats/Png/PngThrowHelper.cs | 10 +- src/ImageSharp/Formats/Tga/TgaDecoderCore.cs | 6 +- src/ImageSharp/Formats/Tga/TgaThrowHelper.cs | 14 +- src/ImageSharp/Image.FromBytes.cs | 147 +++++++++++++++--- src/ImageSharp/Image.FromFile.cs | 119 ++++++++------ src/ImageSharp/Image.FromStream.cs | 131 +++++++++++----- src/ImageSharp/Image.LoadPixelData.cs | 43 +++-- src/ImageSharp/Image.WrapMemory.cs | 50 +++--- 18 files changed, 432 insertions(+), 211 deletions(-) create mode 100644 src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs create mode 100644 src/ImageSharp/Formats/Gif/GifThrowHelper.cs diff --git a/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs b/src/ImageSharp/Common/Exceptions/InvalidImageContentException.cs new file mode 100644 index 000000000..7069e8982 --- /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/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index dfdbb22fa..eeb1ae714 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -462,7 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { if (this.stream.Read(cmd, 0, cmd.Length) != 2) { - BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); + BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); } if (cmd[0] == RleCommand) @@ -569,7 +569,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { if (this.stream.Read(cmd, 0, cmd.Length) != 2) { - BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); + BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); } if (cmd[0] == RleCommand) @@ -648,7 +648,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { if (this.stream.Read(cmd, 0, cmd.Length) != 2) { - BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); + BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); } if (cmd[0] == RleCommand) @@ -1431,7 +1431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp // Make sure, that we will not read pass the bitmap offset (starting position of image data). if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset) { - BmpThrowHelper.ThrowImageFormatException( + BmpThrowHelper.ThrowInvalidImageContentException( $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); } @@ -1445,7 +1445,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; if ((skipAmount + (int)this.stream.Position) > this.stream.Length) { - BmpThrowHelper.ThrowImageFormatException("Invalid fileheader offset found. Offset is greater than the stream length."); + BmpThrowHelper.ThrowInvalidImageContentException("Invalid fileheader offset found. Offset is greater than the stream length."); } if (skipAmount > 0) diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index 9ede66070..3411de042 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -393,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp break; default: // Compression type 3 (1DHuffman) is not supported. - BmpThrowHelper.ThrowImageFormatException("Compression type is not supported. ImageSharp only supports uncompressed, RLE4, RLE8 and RLE24."); + BmpThrowHelper.ThrowInvalidImageContentException("Compression type is not supported. ImageSharp only supports uncompressed, RLE4, RLE8 and RLE24."); break; } diff --git a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs index 443471404..c48566f83 100644 --- a/src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs +++ b/src/ImageSharp/Formats/Bmp/BmpThrowHelper.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; @@ -9,23 +9,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp internal static class BmpThrowHelper { /// - /// 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/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 7919bc148..de5aa7884 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (length > GifConstants.MaxCommentSubBlockLength) { - throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block"); + GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block"); } if (this.IgnoreMetadata) diff --git a/src/ImageSharp/Formats/Gif/GifThrowHelper.cs b/src/ImageSharp/Formats/Gif/GifThrowHelper.cs new file mode 100644 index 000000000..1d81008a0 --- /dev/null +++ b/src/ImageSharp/Formats/Gif/GifThrowHelper.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Gif +{ + internal static class GifThrowHelper + { + /// + /// Cold path optimization for throwing 's + /// + /// The error message for the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowInvalidImageContentException(string errorMessage) + => throw new InvalidImageContentException(errorMessage); + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 09d6a4d1d..002f79f84 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.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; @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { // TODO: Run fallback scalar code here // However, no issues expected before someone implements this: https://github.com/dotnet/coreclr/issues/12007 - throw new NotImplementedException("Your CPU architecture is too modern!"); + JpegThrowHelper.ThrowNotImplementedException("Your CPU architecture is too modern!"); } // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index 7497c8a40..44878bd6c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { if (xDensity <= 0) { - JpegThrowHelper.ThrowImageFormatException($"X-Density {xDensity} must be greater than 0."); + JpegThrowHelper.ThrowInvalidImageContentException($"X-Density {xDensity} must be greater than 0."); } if (yDensity <= 0) { - JpegThrowHelper.ThrowImageFormatException($"Y-Density {yDensity} must be greater than 0."); + JpegThrowHelper.ThrowInvalidImageContentException($"Y-Density {yDensity} must be greater than 0."); } this.MajorVersion = majorVersion; diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 4000fa0f6..654610659 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { - JpegThrowHelper.ThrowImageFormatException("Missing SOI marker."); + JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); } this.InputStream.Read(this.markerBuffer, 0, 2); @@ -423,7 +423,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg : JpegColorSpace.Cmyk; } - JpegThrowHelper.ThrowImageFormatException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {this.ComponentCount}"); + JpegThrowHelper.ThrowInvalidImageContentException($"Unsupported color mode. Supported component counts 1, 3, and 4; found {this.ComponentCount}"); return default; } @@ -821,7 +821,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (this.Frame != null) { - JpegThrowHelper.ThrowImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); + JpegThrowHelper.ThrowInvalidImageContentException("Multiple SOF markers. Only single frame jpegs supported."); } // Read initial marker definitions. @@ -831,7 +831,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // We only support 8-bit and 12-bit precision. if (Array.IndexOf(this.supportedPrecisions, this.temp[0]) == -1) { - JpegThrowHelper.ThrowImageFormatException("Only 8-Bit and 12-Bit precision supported."); + JpegThrowHelper.ThrowInvalidImageContentException("Only 8-Bit and 12-Bit precision supported."); } this.Precision = this.temp[0]; @@ -928,13 +928,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Types 0..1 DC..AC if (tableType > 1) { - JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table type."); + JpegThrowHelper.ThrowInvalidImageContentException("Bad Huffman Table type."); } // Max tables of each type if (tableIndex > 3) { - JpegThrowHelper.ThrowImageFormatException("Bad Huffman Table index."); + JpegThrowHelper.ThrowInvalidImageContentException("Bad Huffman Table index."); } this.InputStream.Read(huffmanData.Array, 0, 16); @@ -953,7 +953,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (codeLengthSum > 256 || codeLengthSum > length) { - JpegThrowHelper.ThrowImageFormatException("Huffman table has excessive length."); + JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); } using (IManagedByteBuffer huffmanValues = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(256, AllocationOptions.Clean)) @@ -995,7 +995,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { if (this.Frame is null) { - JpegThrowHelper.ThrowImageFormatException("No readable SOFn (Start Of Frame) marker found."); + JpegThrowHelper.ThrowInvalidImageContentException("No readable SOFn (Start Of Frame) marker found."); } int selectorsCount = this.InputStream.ReadByte(); @@ -1016,7 +1016,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (componentIndex < 0) { - JpegThrowHelper.ThrowImageFormatException($"Unknown component selector {componentIndex}."); + JpegThrowHelper.ThrowInvalidImageContentException($"Unknown component selector {componentIndex}."); } ref JpegComponent component = ref this.Frame.Components[componentIndex]; diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index 7e8384dcf..dd44cb2d1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -1,6 +1,7 @@ -// 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.CompilerServices; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -8,25 +9,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg internal static class JpegThrowHelper { /// - /// Cold path optimization for throwing 's. + /// Cold path optimization for throwing 's. /// /// The error message for the exception. [MethodImpl(InliningOptions.ColdPath)] - 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 + /// + /// The error message for the exception. + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotImplementedException(string errorMessage) + => throw new NotImplementedException(errorMessage); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowBadMarker(string marker, int length) => throw new ImageFormatException($"Marker {marker} has bad length {length}."); + public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}."); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowBadQuantizationTable() => throw new ImageFormatException("Bad Quantization Table index."); + public static void ThrowBadQuantizationTable() => throw new InvalidImageContentException("Bad Quantization Table index."); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowBadSampling() => throw new ImageFormatException("Bad sampling factor."); + public static void ThrowBadSampling() => throw new InvalidImageContentException("Bad sampling factor."); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new ImageFormatException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}."); + public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) => throw new InvalidImageContentException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}."); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowInvalidImageDimensions(int width, int height) => throw new ImageFormatException($"Invalid image dimensions: {width}x{height}."); + public static void ThrowInvalidImageDimensions(int width, int height) => throw new InvalidImageContentException($"Invalid image dimensions: {width}x{height}."); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngThrowHelper.cs b/src/ImageSharp/Formats/Png/PngThrowHelper.cs index dd3a05464..dcb643d03 100644 --- a/src/ImageSharp/Formats/Png/PngThrowHelper.cs +++ b/src/ImageSharp/Formats/Png/PngThrowHelper.cs @@ -12,21 +12,21 @@ namespace SixLabors.ImageSharp.Formats.Png internal static class PngThrowHelper { [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowNoHeader() => throw new ImageFormatException("PNG Image does not contain a header chunk"); + public static void ThrowNoHeader() => throw new InvalidImageContentException("PNG Image does not contain a header chunk"); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowNoData() => throw new ImageFormatException("PNG Image does not contain a data chunk"); + public static void ThrowNoData() => throw new InvalidImageContentException("PNG Image does not contain a data chunk"); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowInvalidChunkType() => throw new ImageFormatException("Invalid PNG data."); + public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data."); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); + public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); [MethodImpl(InliningOptions.ColdPath)] public static void ThrowNotSupportedColor() => new NotSupportedException("Unsupported PNG color type"); [MethodImpl(InliningOptions.ColdPath)] - public static void ThrowUnknownFilter() => throw new ImageFormatException("Unknown filter type."); + public static void ThrowUnknownFilter() => throw new InvalidImageContentException("Unknown filter type."); } } diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs index 816a472fb..057ec1bfc 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 845d00922..1714a2025 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/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 06b05fe3c..a0e8097d8 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 1a9fa1546..8546dd270 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 52d71409b..332ca471e 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 f8f2e8485..f36243cc3 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 e5181a0db..0dd8c814d 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); } } From 6f2ed15bea650d59ba579f0f4b9010f0c156b243 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 20:18:26 +0100 Subject: [PATCH 4/7] Document Mutate and Clone --- .../Extensions/ProcessingExtensions.cs | 67 +++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index 59be5bf02..45cff9398 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, From 82ba16f63d688c881a3b609d7e044f17db4a6460 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 20:32:27 +0100 Subject: [PATCH 5/7] Document save, make params consistant --- src/ImageSharp/ImageExtensions.cs | 46 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 62ac449b7..e5b2a32a9 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()); @@ -113,9 +124,12 @@ namespace SixLabors.ImageSharp /// /// The source image /// The format. + /// The format is null. /// The public static string ToBase64String(this Image source, IImageFormat format) { + Guard.NotNull(format, nameof(format)); + using var stream = new MemoryStream(); source.Save(stream, format); From dc7d9b8e174cac70a8a1b84693f922c353bb5d0c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 20:53:40 +0100 Subject: [PATCH 6/7] Update tests --- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 4 ++-- .../ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs | 4 ++-- tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index a8376f51b..85cdf6d11 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/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index cf2e5c81b..57051a9d7 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/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index ee4001c20..297512fa6 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -72,7 +72,7 @@ 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); } From 0a16f7ebe6fe1fcb1d169c2bef03ba9900d3f7e6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 Apr 2020 23:39:26 +0100 Subject: [PATCH 7/7] Cast to long and add test/guard. Fix #1167 --- src/ImageSharp/Memory/Buffer2D{T}.cs | 4 ++-- .../Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs | 8 +++++++- .../Memory/DiscontiguousBuffers/MemoryGroupTests.cs | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index bf8630931..ada1d29b6 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/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs index 28da49263..295f9190a 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/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index 2a5dafb27..15b07265f 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -152,6 +152,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers public static TheoryData GetBoundedSlice_ErrorData = new TheoryData() { + { 300, 100, -1, 91 }, { 300, 100, 110, 91 }, { 42, 7, 0, 8 }, { 42, 7, 1, 7 },