Browse Source

Update editorconfig and fix all new warnings.

pull/2189/head
James Jackson-South 4 years ago
parent
commit
b22785ed33
  1. 30
      .editorconfig
  2. 2
      shared-infrastructure
  3. 21
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  4. 5
      src/ImageSharp/Advanced/PreserveAttribute.cs
  5. 11
      src/ImageSharp/Color/Color.cs
  6. 60
      src/ImageSharp/ColorSpaces/CieLab.cs
  7. 66
      src/ImageSharp/ColorSpaces/CieLch.cs
  8. 62
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  9. 58
      src/ImageSharp/ColorSpaces/CieLuv.cs
  10. 46
      src/ImageSharp/ColorSpaces/CieXyy.cs
  11. 48
      src/ImageSharp/ColorSpaces/CieXyz.cs
  12. 60
      src/ImageSharp/ColorSpaces/Cmyk.cs
  13. 4
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs
  14. 27
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  15. 7
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs
  16. 29
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs
  17. 24
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  18. 56
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs
  19. 30
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs
  20. 50
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs
  21. 50
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs
  22. 50
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs
  23. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs
  24. 61
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs
  25. 2
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs
  26. 60
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs
  27. 54
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs
  28. 46
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs
  29. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs
  30. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs
  31. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
  32. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs
  33. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs
  34. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
  35. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs
  36. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs
  37. 10
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs
  38. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs
  39. 6
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs
  40. 4
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
  41. 19
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs
  42. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs
  43. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs
  44. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs
  45. 8
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs
  46. 48
      src/ImageSharp/ColorSpaces/Hsl.cs
  47. 48
      src/ImageSharp/ColorSpaces/Hsv.cs
  48. 58
      src/ImageSharp/ColorSpaces/HunterLab.cs
  49. 56
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  50. 48
      src/ImageSharp/ColorSpaces/Lms.cs
  51. 60
      src/ImageSharp/ColorSpaces/Rgb.cs
  52. 46
      src/ImageSharp/ColorSpaces/YCbCr.cs
  53. 1
      src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
  54. 25
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  55. 47
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  56. 15
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  57. 4
      src/ImageSharp/Formats/DecoderOptions.cs
  58. 25
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  59. 50
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  60. 14
      src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs
  61. 25
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs
  62. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs
  63. 2
      src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs
  64. 20
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs
  65. 3
      src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs
  66. 95
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  67. 18
      src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs
  68. 8
      src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs
  69. 48
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  70. 281
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  71. 28
      src/ImageSharp/Formats/Png/PngTextData.cs
  72. 10
      src/ImageSharp/Formats/Png/PngThrowHelper.cs
  73. 20
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
  74. 8
      src/ImageSharp/Formats/Tga/TgaMetadata.cs
  75. 6
      src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs
  76. 26
      src/ImageSharp/Formats/Tiff/Compression/Compressors/T6BitCompressor.cs
  77. 34
      src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs
  78. 72
      src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs
  79. 27
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  80. 6
      src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs
  81. 6
      src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs
  82. 14
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  83. 8
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  84. 6
      src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs
  85. 50
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  86. 66
      src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs
  87. 97
      src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
  88. 83
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  89. 226
      src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs
  90. 50
      src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
  91. 12
      src/ImageSharp/Formats/Webp/WebpImageFormatDetector.cs
  92. 8
      src/ImageSharp/Formats/Webp/WebpImageInfo.cs
  93. 2
      src/ImageSharp/IO/BufferedReadStream.cs
  94. 4
      src/ImageSharp/IO/ChunkedMemoryStream.cs
  95. 17
      src/ImageSharp/Image.FromStream.cs
  96. 16
      src/ImageSharp/Image.WrapMemory.cs
  97. 11
      src/ImageSharp/ImageExtensions.cs
  98. 15
      src/ImageSharp/ImageSharp.csproj.DotSettings
  99. 5
      src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject
  100. 27
      src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs

30
.editorconfig

@ -1,5 +1,5 @@
# Version: 2.1.0 (Using https://semver.org/)
# Updated: 2021-03-03
# Version: 4.1.1 (Using https://semver.org/)
# Updated: 2022-05-23
# See https://github.com/RehanSaeed/EditorConfig/releases for release notes.
# See https://github.com/RehanSaeed/EditorConfig for updates to this file.
# See http://EditorConfig.org for more information about .editorconfig files.
@ -49,11 +49,11 @@ indent_size = 2
indent_size = 2
# Markdown Files
[*.md]
[*.{md,mdx}]
trim_trailing_whitespace = false
# Web Files
[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}]
[*.{htm,html,js,jsm,ts,tsx,cjs,cts,ctsx,mjs,mts,mtsx,css,sass,scss,less,pcss,svg,vue}]
indent_size = 2
# Batch Files
@ -75,7 +75,7 @@ indent_style = tab
[*.{cs,csx,cake,vb,vbx}]
# Default Severity for all .NET Code Style rules below
dotnet_analyzer_diagnostic.category-style.severity = warning
dotnet_analyzer_diagnostic.severity = warning
##########################################
# Language Rules
@ -128,14 +128,15 @@ file_header_template = Copyright (c) Six Labors.\nLicensed under the Six Labors
# dotnet_diagnostic.SA1636.severity = none
# Undocumented
dotnet_style_operator_placement_when_wrapping = end_of_line
dotnet_style_operator_placement_when_wrapping = end_of_line:warning
csharp_style_prefer_null_check_over_type_check = true:warning
# C# Style Rules
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules
[*.{cs,csx,cake}]
# 'var' preferences
csharp_style_var_for_built_in_types = never
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_elsewhere = false:warning
# Expression-bodied members
csharp_style_expression_bodied_methods = true:warning
@ -200,12 +201,15 @@ dotnet_diagnostic.IDE0059.severity = suggestion
# Organize using directives
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# Dotnet namespace options
dotnet_style_namespace_match_folder = true:suggestion
dotnet_diagnostic.IDE0130.severity = suggestion
# C# formatting rules
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules
[*.{cs,csx,cake}]
# Newline options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#new-line-options
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
@ -214,7 +218,7 @@ csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#indentation-options
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = no_change
@ -222,7 +226,7 @@ csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents_when_block = false
# Spacing options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#spacing-options
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_parentheses = false
@ -246,7 +250,7 @@ csharp_space_before_open_square_brackets = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_square_brackets = false
# Wrap options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#wrap-options
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
@ -448,4 +452,4 @@ dotnet_naming_rule.parameters_rule.severity = warning
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
##########################################
##########################################

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 86af5a82198c21ce6889370428eb97d1fa7b41ff
Subproject commit 5eb77e2d9eb4f0ece012c996941ab78db1af2a41

21
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
@ -34,11 +35,11 @@ namespace SixLabors.ImageSharp.Advanced
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format is null)
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:");
StringBuilder sb = new();
sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
sb.AppendFormat(" - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", fmt.Name, string.Join(", ", fmt.FileExtensions), Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@ -48,11 +49,11 @@ namespace SixLabors.ImageSharp.Advanced
if (encoder is null)
{
var sb = new StringBuilder();
sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
StringBuilder sb = new();
sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(" - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", enc.Key, enc.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@ -116,6 +117,7 @@ namespace SixLabors.ImageSharp.Advanced
/// Certain Image Processors may invalidate the returned <see cref="IMemoryGroup{T}"/> and all it's buffers,
/// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="source"/> in <see langword="null"/>.</exception>
public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this ImageFrame<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> source?.PixelBuffer.FastMemoryGroup.View ?? throw new ArgumentNullException(nameof(source));
@ -131,13 +133,14 @@ namespace SixLabors.ImageSharp.Advanced
/// Certain Image Processors may invalidate the returned <see cref="IMemoryGroup{T}"/> and all it's buffers,
/// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="source"/> in <see langword="null"/>.</exception>
public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this Image<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source));
/// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// at row <paramref name="rowIndex"/> beginning from the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
@ -154,8 +157,8 @@ namespace SixLabors.ImageSharp.Advanced
}
/// <summary>
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the the first pixel on that row.
/// Gets the representation of the pixels as <see cref="Span{T}"/> of contiguous memory
/// at row <paramref name="rowIndex"/> beginning from the first pixel on that row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>

5
src/ImageSharp/Advanced/PreserveAttribute.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Advanced
{
/// <summary>
@ -8,7 +10,8 @@ namespace SixLabors.ImageSharp.Advanced
/// The only thing that matters is the class name.
/// There is no need to use or inherit from the PreserveAttribute class in each environment.
/// </summary>
internal sealed class PreserveAttribute : System.Attribute
[AttributeUsage(AttributeTargets.Method)]
internal sealed class PreserveAttribute : Attribute
{
}
}

11
src/ImageSharp/Color/Color.cs

@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(InliningOptions.ShortMethod)]
public static Color ParseHex(string hex)
{
var rgba = Rgba32.ParseHex(hex);
Rgba32 rgba = Rgba32.ParseHex(hex);
return new Color(rgba);
}
@ -193,6 +193,7 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="Color"/>.
/// </returns>
/// <exception cref="ArgumentException">Input string is not in the correct format.</exception>
public static Color Parse(string input)
{
Guard.NotNull(input, nameof(input));
@ -241,7 +242,7 @@ namespace SixLabors.ImageSharp
/// <returns>The color having it's alpha channel altered.</returns>
public Color WithAlpha(float alpha)
{
var v = (Vector4)this;
Vector4 v = (Vector4)this;
v.W = alpha;
return new Color(v);
}
@ -286,14 +287,10 @@ namespace SixLabors.ImageSharp
/// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type.
/// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void ToPixel<TPixel>(
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));

60
src/ImageSharp/ColorSpaces/CieLab.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -19,29 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct.
/// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A { get; }
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B { get; }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint { get; }
/// <summary>
/// Compares two <see cref="CieLab"/> objects for equality.
/// </summary>
@ -128,12 +128,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLab other)
{
return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
public bool Equals(CieLab other) =>
this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
}

66
src/ImageSharp/ColorSpaces/CieLch.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -19,31 +19,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
private static readonly Vector3 Min = new(0, -200, 0);
private static readonly Vector3 Max = new(100, 200, 360);
/// <summary>
/// Initializes a new instance of the <see cref="CieLch"/> struct.
@ -97,6 +74,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C { get; }
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H { get; }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint { get; }
/// <summary>
/// Compares two <see cref="CieLch"/> objects for equality.
/// </summary>
@ -121,9 +121,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
public override int GetHashCode()
{
return HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
}
=> HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");
@ -135,12 +133,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLch other)
{
return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
}
=> this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
/// <summary>
/// Computes the saturation of the color (chroma normalized by lightness)

62
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -13,8 +13,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public readonly struct CieLchuv : IEquatable<CieLchuv>
{
private static readonly Vector3 Min = new Vector3(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360);
private static readonly Vector3 Min = new(0, -200, 0);
private static readonly Vector3 Max = new(100, 200, 360);
/// <summary>
/// D50 standard illuminant.
@ -22,29 +22,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLchuv"/> struct.
/// </summary>
@ -98,6 +75,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the a chroma component.
/// <remarks>A value ranging from 0 to 200.</remarks>
/// </summary>
public readonly float C { get; }
/// <summary>
/// Gets the h° hue component in degrees.
/// <remarks>A value ranging from 0 to 360.</remarks>
/// </summary>
public readonly float H { get; }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint { get; }
/// <summary>
/// Compares two <see cref="CieLchuv"/> objects for equality.
/// </summary>
@ -130,12 +130,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLchuv other)
{
return this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
}
=> this.L.Equals(other.L)
&& this.C.Equals(other.C)
&& this.H.Equals(other.H)
&& this.WhitePoint.Equals(other.WhitePoint);
/// <summary>
/// Computes the saturation of the color (chroma normalized by lightness)

58
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -21,29 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65;
/// <summary>
/// Gets the lightness dimension
/// <remarks>A value usually ranging between 0 and 100.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float U;
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float V;
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="CieLuv"/> struct.
/// </summary>
@ -96,6 +73,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the lightness dimension
/// <remarks>A value usually ranging between 0 and 100.</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the blue-yellow chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float U { get; }
/// <summary>
/// Gets the red-green chromaticity coordinate of the given whitepoint.
/// <remarks>A value usually ranging between -100 and 100.</remarks>
/// </summary>
public readonly float V { get; }
/// <summary>
/// Gets the reference white point of this color
/// </summary>
public readonly CieXyz WhitePoint { get; }
/// <summary>
/// Compares two <see cref="CieLuv"/> objects for equality.
/// </summary>
@ -130,11 +130,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLuv other)
{
return this.L.Equals(other.L)
&& this.U.Equals(other.U)
&& this.V.Equals(other.V)
&& this.WhitePoint.Equals(other.WhitePoint);
}
=> this.L.Equals(other.L)
&& this.U.Equals(other.U)
&& this.V.Equals(other.V)
&& this.WhitePoint.Equals(other.WhitePoint);
}
}

46
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -13,24 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public readonly struct CieXyy : IEquatable<CieXyy>
{
/// <summary>
/// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Yl;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct.
/// </summary>
@ -60,6 +42,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Yl = vector.Z;
}
/// <summary>
/// Gets the X chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X { get; }
/// <summary>
/// Gets the Y chrominance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y { get; }
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Yl { get; }
/// <summary>
/// Compares two <see cref="CieXyy"/> objects for equality.
/// </summary>
@ -94,10 +94,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyy other)
{
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Yl.Equals(other.Yl);
}
=> this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Yl.Equals(other.Yl);
}
}

48
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -13,24 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public readonly struct CieXyz : IEquatable<CieXyz>
{
/// <summary>
/// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X;
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Z;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct.
/// </summary>
@ -56,6 +38,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Z = vector.Z;
}
/// <summary>
/// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float X { get; }
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y { get; }
/// <summary>
/// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Z { get; }
/// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary>
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
public Vector3 ToVector3() => new(this.X, this.Y, this.Z);
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
@ -97,10 +97,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyz other)
{
return this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Z.Equals(other.Z);
}
=> this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Z.Equals(other.Z);
}
}

60
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -15,30 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector4 Min = Vector4.Zero;
private static readonly Vector4 Max = Vector4.One;
/// <summary>
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float C;
/// <summary>
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float M;
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float K;
/// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// </summary>
@ -66,6 +42,30 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.K = vector.W;
}
/// <summary>
/// Gets the cyan color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float C { get; }
/// <summary>
/// Gets the magenta color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float M { get; }
/// <summary>
/// Gets the yellow color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float Y { get; }
/// <summary>
/// Gets the keyline black color component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float K { get; }
/// <summary>
/// Compares two <see cref="Cmyk"/> objects for equality.
/// </summary>
@ -101,11 +101,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Cmyk other)
{
return this.C.Equals(other.C)
&& this.M.Equals(other.M)
&& this.Y.Equals(other.Y)
&& this.K.Equals(other.K);
}
=> this.C.Equals(other.C)
&& this.M.Equals(other.M)
&& this.Y.Equals(other.Y)
&& this.K.Equals(other.K);
}
}

4
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs

@ -150,9 +150,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
return color;
}
var linearInput = this.ToLinearRgb(color);
var linearInput = ToLinearRgb(color);
LinearRgb linearOutput = this.Adapt(linearInput);
return this.ToRgb(linearOutput);
return ToRgb(linearOutput);
}
}
}

27
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -12,11 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLch to CieLab.
/// </summary>
private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter();
/// <summary>
/// Converts a <see cref="CieLch"/> into a <see cref="CieLab"/>.
/// </summary>
@ -58,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -91,7 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -124,7 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -190,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -222,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -255,7 +250,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -287,7 +282,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -320,7 +315,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -353,7 +348,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -386,7 +381,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}
@ -419,7 +414,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLab(xyzColor);
}

7
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs

@ -12,11 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLab to CieLch.
/// </summary>
private static readonly CieLabToCieLchConverter CieLabToCieLchConverter = new CieLabToCieLchConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="CieLch"/>
/// </summary>
@ -123,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLch"/></returns>
public CieLch ToCieLch(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
var xyzColor = ToCieXyz(color);
return this.ToCieLch(xyzColor);
}

29
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs

@ -12,11 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
/// <summary>
/// The converter for converting between CieLab to CieLchuv.
/// </summary>
private static readonly CieLuvToCieLchuvConverter CieLuvToCieLchuvConverter = new CieLuvToCieLchuvConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="CieLchuv"/>
/// </summary>
@ -24,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -57,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -123,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -156,7 +151,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in CieXyz color)
{
var luvColor = this.ToCieLuv(color);
CieLuv luvColor = this.ToCieLuv(color);
return this.ToCieLchuv(luvColor);
}
@ -189,7 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -222,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -255,7 +250,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -288,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -321,7 +316,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -354,7 +349,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -387,7 +382,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}
@ -420,7 +415,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLchuv"/></returns>
public CieLchuv ToCieLchuv(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLchuv(xyzColor);
}

24
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="CieLuv"/>.
/// </summary>
@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -53,7 +51,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -120,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -219,7 +217,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in Hsl color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -251,7 +249,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -283,7 +281,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -315,7 +313,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -347,7 +345,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -379,7 +377,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}
@ -411,7 +409,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLuv"/></returns>
public CieLuv ToCieLuv(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieLuv(xyzColor);
}

56
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="CieXyy"/>
/// </summary>
@ -21,9 +19,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -54,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -87,9 +85,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -120,9 +118,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -151,14 +149,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color);
public static CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyz"/> into <see cref="CieXyy"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination)
public static void Convert(ReadOnlySpan<CieXyz> source, Span<CieXyy> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -170,7 +168,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyy dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyy(sp);
dp = ToCieXyy(sp);
}
}
@ -181,9 +179,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in Cmyk color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -214,9 +212,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(Hsl color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -247,9 +245,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in Hsv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -280,9 +278,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -313,9 +311,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in LinearRgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -346,9 +344,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -379,9 +377,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in Rgb color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>
@ -412,9 +410,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyy"/></returns>
public CieXyy ToCieXyy(in YCbCr color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCieXyy(xyzColor);
return ToCieXyy(xyzColor);
}
/// <summary>

30
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs

@ -12,12 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter();
private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter();
private static readonly HunterLabToCieXyzConverter
HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter();
private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new();
private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter;
@ -166,18 +161,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(in CieXyy color)
{
public static CieXyz ToCieXyz(in CieXyy color)
// Conversion
return CieXyzAndCieXyyConverter.Convert(color);
}
=> CieXyzAndCieXyyConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="CieXyy"/> into <see cref="CieXyz"/>.
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination)
public static void Convert(ReadOnlySpan<CieXyy> source, Span<CieXyz> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -189,7 +183,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i);
ref CieXyz dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCieXyz(sp);
dp = ToCieXyz(sp);
}
}
@ -200,7 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(in Cmyk color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return this.ToCieXyz(rgb);
}
@ -233,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(in Hsl color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return this.ToCieXyz(rgb);
}
@ -267,7 +261,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public CieXyz ToCieXyz(in Hsv color)
{
// Conversion
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return this.ToCieXyz(rgb);
}
@ -367,9 +361,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(in Lms color)
{
return this.cieXyzAndLmsConverter.Convert(color);
}
=> this.cieXyzAndLmsConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Lms"/> into <see cref="CieXyz"/>.
@ -432,7 +424,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieXyz"/></returns>
public CieXyz ToCieXyz(in YCbCr color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return this.ToCieXyz(rgb);
}

50
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Cmyk"/>.
/// </summary>
@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -54,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -87,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -120,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -153,7 +151,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -186,7 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in CieXyz color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
}
@ -217,9 +215,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Hsl color)
public static Cmyk ToCmyk(in Hsl color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
}
@ -229,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination)
public static void Convert(ReadOnlySpan<Hsl> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -241,7 +239,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
dp = ToCmyk(sp);
}
}
@ -250,9 +248,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Hsv color)
public static Cmyk ToCmyk(in Hsv color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
}
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination)
public static void Convert(ReadOnlySpan<Hsv> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -274,7 +272,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
dp = ToCmyk(sp);
}
}
@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -316,9 +314,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in LinearRgb color)
public static Cmyk ToCmyk(in LinearRgb color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
}
@ -328,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination)
public static void Convert(ReadOnlySpan<LinearRgb> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -340,7 +338,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
dp = ToCmyk(sp);
}
}
@ -351,7 +349,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToCmyk(xyzColor);
}
@ -382,14 +380,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color);
public static Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Cmyk"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination)
public static void Convert(ReadOnlySpan<Rgb> source, Span<Cmyk> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -401,7 +399,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Cmyk dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToCmyk(sp);
dp = ToCmyk(sp);
}
}
@ -412,7 +410,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Cmyk"/></returns>
public Cmyk ToCmyk(in YCbCr color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return CmykAndRgbConverter.Convert(rgb);
}

50
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Hsl"/>.
/// </summary>
@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -54,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -87,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -120,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -153,7 +151,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -186,7 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in CieXyz color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
}
@ -217,9 +215,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in Cmyk color)
public static Hsl ToHsl(in Cmyk color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
}
@ -229,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination)
public static void Convert(ReadOnlySpan<Cmyk> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -241,7 +239,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
dp = ToHsl(sp);
}
}
@ -250,9 +248,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in Hsv color)
public static Hsl ToHsl(in Hsv color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
}
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination)
public static void Convert(ReadOnlySpan<Hsv> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -274,7 +272,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
dp = ToHsl(sp);
}
}
@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -316,9 +314,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in LinearRgb color)
public static Hsl ToHsl(in LinearRgb color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
}
@ -328,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination)
public static void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -340,7 +338,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
dp = ToHsl(sp);
}
}
@ -351,7 +349,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsl(xyzColor);
}
@ -382,14 +380,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color);
public static Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsl"/>.
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination)
public static void Convert(ReadOnlySpan<Rgb> source, Span<Hsl> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -401,7 +399,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsl dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsl(sp);
dp = ToHsl(sp);
}
}
@ -412,7 +410,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsl"/></returns>
public Hsl ToHsl(in YCbCr color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return HslAndRgbConverter.Convert(rgb);
}

50
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter();
/// <summary>
/// Converts a <see cref="CieLab"/> into a <see cref="Hsv"/>
/// </summary>
@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -54,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -87,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -120,7 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -153,7 +151,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -186,7 +184,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in CieXyz color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb);
}
@ -217,9 +215,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in Cmyk color)
public static Hsv ToHsv(in Cmyk color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HsvAndRgbConverter.Convert(rgb);
}
@ -229,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination)
public static void Convert(ReadOnlySpan<Cmyk> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -241,7 +239,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
dp = ToHsv(sp);
}
}
@ -250,9 +248,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in Hsl color)
public static Hsv ToHsv(in Hsl color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HsvAndRgbConverter.Convert(rgb);
}
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination)
public static void Convert(ReadOnlySpan<Hsl> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -274,7 +272,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
dp = ToHsv(sp);
}
}
@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -316,9 +314,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in LinearRgb color)
public static Hsv ToHsv(in LinearRgb color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return HsvAndRgbConverter.Convert(rgb);
}
@ -328,7 +326,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination)
public static void Convert(ReadOnlySpan<LinearRgb> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -340,7 +338,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
dp = ToHsv(sp);
}
}
@ -351,7 +349,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToHsv(xyzColor);
}
@ -382,14 +380,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color);
public static Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color);
/// <summary>
/// Performs the bulk conversion from <see cref="Rgb"/> into <see cref="Hsv"/>
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination)
public static void Convert(ReadOnlySpan<Rgb> source, Span<Hsv> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -401,7 +399,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Hsv dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToHsv(sp);
dp = ToHsv(sp);
}
}
@ -412,7 +410,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Hsv"/></returns>
public Hsv ToHsv(in YCbCr color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return HsvAndRgbConverter.Convert(rgb);
}

2
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs

@ -336,7 +336,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="HunterLab"/></returns>
public HunterLab ToHunterLab(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
var xyzColor = ToCieXyz(color);
return this.ToHunterLab(xyzColor);
}

61
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter();
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="LinearRgb"/>.
/// </summary>
@ -145,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination)
public static void Convert(ReadOnlySpan<Cmyk> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -157,7 +155,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
dp = ToLinearRgb(sp);
}
}
@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination)
public static void Convert(ReadOnlySpan<Hsl> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -178,7 +176,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
dp = ToLinearRgb(sp);
}
}
@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors.</param>
/// <param name="destination">The span to the destination colors.</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination)
public static void Convert(ReadOnlySpan<Hsv> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
dp = ToLinearRgb(sp);
}
}
@ -250,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination)
public static void Convert(ReadOnlySpan<Rgb> source, Span<LinearRgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref LinearRgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToLinearRgb(sp);
dp = ToLinearRgb(sp);
}
}
@ -294,7 +292,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -305,7 +303,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -316,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -327,7 +325,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -338,7 +336,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -361,10 +359,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Cmyk color)
public static LinearRgb ToLinearRgb(in Cmyk color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
Rgb rgb = ToRgb(color);
return ToLinearRgb(rgb);
}
/// <summary>
@ -372,10 +370,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsl color)
public static LinearRgb ToLinearRgb(in Hsl color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
Rgb rgb = ToRgb(color);
return ToLinearRgb(rgb);
}
/// <summary>
@ -383,10 +381,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Hsv color)
public static LinearRgb ToLinearRgb(in Hsv color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
Rgb rgb = ToRgb(color);
return ToLinearRgb(rgb);
}
/// <summary>
@ -396,7 +394,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -407,7 +405,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToLinearRgb(xyzColor);
}
@ -416,11 +414,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in Rgb color)
{
// Conversion
return RgbToLinearRgbConverter.Convert(color);
}
public static LinearRgb ToLinearRgb(in Rgb color)
=> RgbToLinearRgbConverter.Convert(color);
/// <summary>
/// Converts a <see cref="YCbCr"/> into a <see cref="LinearRgb"/>.
@ -429,8 +424,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="LinearRgb"/></returns>
public LinearRgb ToLinearRgb(in YCbCr color)
{
var rgb = this.ToRgb(color);
return this.ToLinearRgb(rgb);
Rgb rgb = this.ToRgb(color);
return ToLinearRgb(rgb);
}
}
}

2
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs

@ -336,7 +336,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Lms"/></returns>
public Lms ToLms(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
var xyzColor = ToCieXyz(color);
return this.ToLms(xyzColor);
}

60
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter();
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="Rgb"/>
/// </summary>
@ -145,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination)
public static void Convert(ReadOnlySpan<Cmyk> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -157,7 +155,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
dp = ToRgb(sp);
}
}
@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination)
public static void Convert(ReadOnlySpan<Hsv> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -178,7 +176,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
dp = ToRgb(sp);
}
}
@ -187,7 +185,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination)
public static void Convert(ReadOnlySpan<Hsl> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -199,7 +197,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
dp = ToRgb(sp);
}
}
@ -229,7 +227,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination)
public static void Convert(ReadOnlySpan<LinearRgb> source, Span<Rgb> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -241,7 +239,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref Rgb dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToRgb(sp);
dp = ToRgb(sp);
}
}
@ -294,7 +292,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -305,7 +303,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -316,7 +314,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLchuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -327,7 +325,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -338,7 +336,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -350,10 +348,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
public Rgb ToRgb(in CieXyz color)
{
// Conversion
var linear = this.ToLinearRgb(color);
LinearRgb linear = this.ToLinearRgb(color);
// Compand
return this.ToRgb(linear);
return ToRgb(linear);
}
/// <summary>
@ -361,33 +359,21 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Cmyk color)
{
// Conversion
return CmykAndRgbConverter.Convert(color);
}
public static Rgb ToRgb(in Cmyk color) => CmykAndRgbConverter.Convert(color);
/// <summary>
/// Converts a <see cref="Hsv"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsv color)
{
// Conversion
return HsvAndRgbConverter.Convert(color);
}
public static Rgb ToRgb(in Hsv color) => HsvAndRgbConverter.Convert(color);
/// <summary>
/// Converts a <see cref="Hsl"/> into a <see cref="Rgb"/>
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Hsl color)
{
// Conversion
return HslAndRgbConverter.Convert(color);
}
public static Rgb ToRgb(in Hsl color) => HslAndRgbConverter.Convert(color);
/// <summary>
/// Converts a <see cref="HunterLab"/> into a <see cref="Rgb"/>
@ -396,7 +382,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}
@ -405,11 +391,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in LinearRgb color)
{
// Conversion
return LinearRgbToRgbConverter.Convert(color);
}
public static Rgb ToRgb(in LinearRgb color) => LinearRgbToRgbConverter.Convert(color);
/// <summary>
/// Converts a <see cref="Lms"/> into a <see cref="Rgb"/>
@ -418,7 +400,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="Rgb"/></returns>
public Rgb ToRgb(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToRgb(xyzColor);
}

54
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content>
public partial class ColorSpaceConverter
{
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
/// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>.
/// </summary>
@ -124,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination)
public static void Convert(ReadOnlySpan<Cmyk> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -136,7 +134,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
dp = ToYCbCr(sp);
}
}
@ -145,7 +143,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination)
public static void Convert(ReadOnlySpan<Hsl> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -157,7 +155,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
dp = ToYCbCr(sp);
}
}
@ -166,7 +164,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination)
public static void Convert(ReadOnlySpan<Hsv> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -178,7 +176,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
dp = ToYCbCr(sp);
}
}
@ -208,7 +206,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination)
public static void Convert(ReadOnlySpan<LinearRgb> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -220,7 +218,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
dp = ToYCbCr(sp);
}
}
@ -250,7 +248,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination colors</param>
public void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination)
public static void Convert(ReadOnlySpan<Rgb> source, Span<YCbCr> destination)
{
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length;
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, i);
dp = this.ToYCbCr(sp);
dp = ToYCbCr(sp);
}
}
@ -273,7 +271,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLch color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -297,7 +295,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLuv color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -309,7 +307,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyy color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -321,7 +319,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyz color)
{
var rgb = this.ToRgb(color);
Rgb rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
@ -331,9 +329,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Cmyk color)
public static YCbCr ToYCbCr(in Cmyk color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
@ -343,9 +341,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsl color)
public static YCbCr ToYCbCr(in Hsl color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
@ -355,9 +353,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Hsv color)
public static YCbCr ToYCbCr(in Hsv color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
@ -369,7 +367,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in HunterLab color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -379,9 +377,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in LinearRgb color)
public static YCbCr ToYCbCr(in LinearRgb color)
{
var rgb = this.ToRgb(color);
Rgb rgb = ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb);
}
@ -393,7 +391,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Lms color)
{
var xyzColor = this.ToCieXyz(color);
CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor);
}
@ -403,6 +401,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color);
public static YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color);
}
}

46
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs

@ -12,13 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary>
public readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates>
{
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyChromaticityCoordinates(float x, float y)
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Gets the chromaticity X-coordinate.
/// </summary>
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public readonly float X;
public readonly float X { get; }
/// <summary>
/// Gets the chromaticity Y-coordinate
@ -26,19 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <remarks>
/// Ranges usually from 0 to 1.
/// </remarks>
public readonly float Y;
/// <summary>
/// Initializes a new instance of the <see cref="CieXyChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="x">Chromaticity coordinate x (usually from 0 to 1)</param>
/// <param name="y">Chromaticity coordinate y (usually from 0 to 1)</param>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyChromaticityCoordinates(float x, float y)
{
this.X = x;
this.Y = y;
}
public readonly float Y { get; }
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality.
@ -49,7 +49,8 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => left.Equals(right);
public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
=> left.Equals(right);
/// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality
@ -60,20 +61,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right);
public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right)
=> !left.Equals(right);
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
public override int GetHashCode()
=> HashCode.Combine(this.X, this.Y);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
public override string ToString()
=> FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");
/// <inheritdoc/>
public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other);
public override bool Equals(object obj)
=> obj is CieXyChromaticityCoordinates other && this.Equals(other);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
public bool Equals(CieXyChromaticityCoordinates other)
=> this.X.Equals(other.X) && this.Y.Equals(other.Y);
}
}

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal sealed class CieLchToCieLabConverter
internal static class CieLchToCieLabConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLch"/> input to an instance of <see cref="CieLab"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieLch input)
public static CieLab Convert(in CieLch input)
{
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal sealed class CieLabToCieLchConverter
internal static class CieLabToCieLchConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieLch"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLch Convert(in CieLab input)
public static CieLch Convert(in CieLab input)
{
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
/// </summary>
internal sealed class CieLabToCieXyzConverter
internal static class CieLabToCieXyzConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieXyz"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieLab input)
public static CieXyz Convert(in CieLab input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
float l = input.L, a = input.A, b = input.B;
@ -32,10 +32,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
Vector3 wxyz = new(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
// Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white)
var xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One);
Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One);
Vector3 xyz = xyzr * wxyz;
return new CieXyz(xyz);

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary>
internal sealed class CieLchuvToCieLuvConverter
internal static class CieLchuvToCieLuvConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLchuv"/> input to an instance of <see cref="CieLuv"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLuv Convert(in CieLchuv input)
public static CieLuv Convert(in CieLchuv input)
{
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary>
internal sealed class CieLuvToCieLchuvConverter
internal static class CieLuvToCieLchuvConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieLchuv"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv Convert(in CieLuv input)
public static CieLchuv Convert(in CieLuv input)
{
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs

@ -8,14 +8,14 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
/// </summary>
internal sealed class CieLuvToCieXyzConverter
internal static class CieLuvToCieXyzConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary>
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
public CieXyz Convert(in CieLuv input)
public static CieXyz Convert(in CieLuv input)
{
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
float l = input.L, u = input.U, v = input.V;

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Color converter between CIE XYZ and CIE xyY.
/// <see href="http://www.brucelindbloom.com/"/> for formulas.
/// </summary>
internal sealed class CieXyzAndCieXyyConverter
internal static class CieXyzAndCieXyyConverter
{
/// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieXyy"/> type.
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyy Convert(in CieXyz input)
public static CieXyy Convert(in CieXyz input)
{
float x = input.X / (input.X + input.Y + input.Z);
float y = input.Y / (input.X + input.Y + input.Z);
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieXyy input)
public static CieXyz Convert(in CieXyy input)
{
if (MathF.Abs(input.Y) < Constants.Epsilon)
{

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
[MethodImpl(InliningOptions.ShortMethod)]
public Lms Convert(in CieXyz input)
{
var vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix);
Vector3 vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix);
return new Lms(vector);
}
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in Lms input)
{
var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
Vector3 vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix);
return new CieXyz(vector);
}

10
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Color converter between <see cref="Cmyk"/> and <see cref="Rgb"/>.
/// </summary>
internal sealed class CmykAndRgbConverter
internal static class CmykAndRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Cmyk"/> input to an instance of <see cref="Rgb"/> type.
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Cmyk input)
public static Rgb Convert(in Cmyk input)
{
Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K));
return new Rgb(rgb);
@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk Convert(in Rgb input)
public static Cmyk Convert(in Rgb input)
{
// To CMY
Vector3 cmy = Vector3.One - input.ToVector3();
// To CMYK
var k = new Vector3(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z)));
Vector3 k = new(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z)));
if (MathF.Abs(k.X - 1F) < Constants.Epsilon)
{

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Color converter between HSL and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal sealed class HslAndRgbConverter
internal static class HslAndRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Hsl"/> input to an instance of <see cref="Rgb"/> type.
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsl input)
public static Rgb Convert(in Hsl input)
{
float rangedH = input.H / 360F;
float r = 0;
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsl Convert(in Rgb input)
public static Hsl Convert(in Rgb input)
{
float r = input.R;
float g = input.G;

6
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Color converter between HSV and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary>
internal sealed class HsvAndRgbConverter
internal static class HsvAndRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Hsv"/> input to an instance of <see cref="Rgb"/> type.
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsv input)
public static Rgb Convert(in Hsv input)
{
float s = input.S;
float v = input.V;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Hsv Convert(in Rgb input)
public static Hsv Convert(in Rgb input)
{
float r = input.R;
float g = input.G;

4
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary>
internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{
/// <summary>
/// Performs the conversion from the <see cref="HunterLab"/> input to an instance of <see cref="CieXyz"/> type.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in HunterLab input)
public static CieXyz Convert(in HunterLab input)
{
// Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float l = input.L, a = input.A, b = input.B;

19
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs

@ -28,25 +28,22 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float yb = chromaticity.B.Y;
float mXr = xr / yr;
const float Yr = 1;
float mZr = (1 - xr - yr) / yr;
float mXg = xg / yg;
const float Yg = 1;
float mZg = (1 - xg - yg) / yg;
float mXb = xb / yb;
const float Yb = 1;
float mZb = (1 - xb - yb) / yb;
var xyzMatrix = new Matrix4x4
Matrix4x4 xyzMatrix = new()
{
M11 = mXr,
M21 = mXg,
M31 = mXb,
M12 = Yr,
M22 = Yg,
M32 = Yb,
M12 = 1F,
M22 = 1F,
M32 = 1F,
M13 = mZr,
M23 = mZg,
M33 = mZb,
@ -55,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix);
var vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix);
Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix);
// Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication?
@ -64,9 +61,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
M11 = vector.X * mXr,
M21 = vector.Y * mXg,
M31 = vector.Z * mXb,
M12 = vector.X * Yr,
M22 = vector.Y * Yg,
M32 = vector.Z * Yb,
M12 = vector.X * 1,
M22 = vector.Y * 1,
M32 = vector.Z * 1,
M13 = vector.X * mZr,
M23 = vector.Y * mZg,
M33 = vector.Z * mZb,

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{
DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal.");
var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
Vector3 vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix);
return new CieXyz(vector);
}
}

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Color converter between <see cref="LinearRgb"/> and <see cref="Rgb"/>.
/// </summary>
internal sealed class LinearRgbToRgbConverter
internal static class LinearRgbToRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="LinearRgb"/> input to an instance of <see cref="Rgb"/> type.
@ -16,13 +16,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in LinearRgb input)
{
return new Rgb(
public static Rgb Convert(in LinearRgb input) =>
new(
r: input.WorkingSpace.Compress(input.R),
g: input.WorkingSpace.Compress(input.G),
b: input.WorkingSpace.Compress(input.B),
workingSpace: input.WorkingSpace);
}
}
}

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary>
/// Color converter between Rgb and LinearRgb.
/// </summary>
internal class RgbToLinearRgbConverter
internal static class RgbToLinearRgbConverter
{
/// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="LinearRgb"/> type.
@ -16,13 +16,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in Rgb input)
{
return new LinearRgb(
public static LinearRgb Convert(in Rgb input)
=> new(
r: input.WorkingSpace.Expand(input.R),
g: input.WorkingSpace.Expand(input.G),
b: input.WorkingSpace.Expand(input.B),
workingSpace: input.WorkingSpace);
}
}
}

8
src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs

@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Color converter between <see cref="YCbCr"/> and <see cref="Rgb"/>
/// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas.
/// </summary>
internal sealed class YCbCrAndRgbConverter
internal static class YCbCrAndRgbConverter
{
private static readonly Vector3 MaxBytes = new Vector3(255F);
private static readonly Vector3 MaxBytes = new(255F);
/// <summary>
/// Performs the conversion from the <see cref="YCbCr"/> input to an instance of <see cref="Rgb"/> type.
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in YCbCr input)
public static Rgb Convert(in YCbCr input)
{
float y = input.Y;
float cb = input.Cb - 128F;
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public YCbCr Convert(in Rgb input)
public static YCbCr Convert(in Rgb input)
{
Vector3 rgb = input.ToVector3() * MaxBytes;
float r = rgb.X;

48
src/ImageSharp/ColorSpaces/Hsl.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -13,25 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly struct Hsl : IEquatable<Hsl>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float L;
private static readonly Vector3 Max = new(360, 1, 1);
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct.
@ -58,6 +40,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.L = vector.Z;
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H { get; }
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float S { get; }
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Compares two <see cref="Hsl"/> objects for equality.
/// </summary>
@ -95,10 +95,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsl other)
{
return this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.L.Equals(other.L);
}
=> this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.L.Equals(other.L);
}
}

48
src/ImageSharp/ColorSpaces/Hsv.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -13,25 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly struct Hsv : IEquatable<Hsv>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1);
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H;
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float V;
private static readonly Vector3 Max = new(360, 1, 1);
/// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct.
@ -58,6 +40,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.V = vector.Z;
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public readonly float H { get; }
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float S { get; }
/// <summary>
/// Gets the value (brightness) component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public readonly float V { get; }
/// <summary>
/// Compares two <see cref="Hsv"/> objects for equality.
/// </summary>
@ -93,10 +93,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsv other)
{
return this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.V.Equals(other.V);
}
=> this.H.Equals(other.H)
&& this.S.Equals(other.S)
&& this.V.Equals(other.V);
}
}

58
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -19,29 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.C;
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A;
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the reference white point of this color.
/// </summary>
public readonly CieXyz WhitePoint;
/// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary>
@ -94,6 +71,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint;
}
/// <summary>
/// Gets the lightness dimension.
/// <remarks>A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white).</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the a color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is green, positive magenta.</remarks>
/// </summary>
public readonly float A { get; }
/// <summary>
/// Gets the b color component.
/// <remarks>A value usually ranging from -100 to 100. Negative is blue, positive is yellow</remarks>
/// </summary>
public readonly float B { get; }
/// <summary>
/// Gets the reference white point of this color.
/// </summary>
public readonly CieXyz WhitePoint { get; }
/// <summary>
/// Compares two <see cref="HunterLab"/> objects for equality.
/// </summary>
@ -128,11 +128,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(HunterLab other)
{
return this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
=> this.L.Equals(other.L)
&& this.A.Equals(other.A)
&& this.B.Equals(other.B)
&& this.WhitePoint.Equals(other.WhitePoint);
}
}

56
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -21,29 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb;
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WorkingSpace = workingSpace;
}
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R { get; }
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G { get; }
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B { get; }
/// <summary>
/// Gets the LinearRgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace { get; }
/// <summary>
/// Compares two <see cref="LinearRgb"/> objects for equality.
/// </summary>
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
public Vector3 ToVector3() => new(this.R, this.G, this.B);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
@ -137,10 +137,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(LinearRgb other)
{
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
=> this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}

48
src/ImageSharp/ColorSpaces/Lms.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -14,24 +14,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
public readonly struct Lms : IEquatable<Lms>
{
/// <summary>
/// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float L;
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float M;
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float S;
/// <summary>
/// Initializes a new instance of the <see cref="Lms"/> struct.
/// </summary>
@ -57,6 +39,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.S = vector.Z;
}
/// <summary>
/// Gets the L long component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float L { get; }
/// <summary>
/// Gets the M medium component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float M { get; }
/// <summary>
/// Gets the S short component.
/// <remarks>A value usually ranging between -1 and 1.</remarks>
/// </summary>
public readonly float S { get; }
/// <summary>
/// Compares two <see cref="Lms"/> objects for equality.
/// </summary>
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
public Vector3 ToVector3() => new(this.L, this.M, this.S);
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S);
@ -98,10 +98,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Lms other)
{
return this.L.Equals(other.L)
&& this.M.Equals(other.M)
&& this.S.Equals(other.S);
}
=> this.L.Equals(other.L)
&& this.M.Equals(other.M)
&& this.S.Equals(other.S);
}
}

60
src/ImageSharp/ColorSpaces/Rgb.cs

@ -22,29 +22,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One;
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B;
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace;
/// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WorkingSpace = workingSpace;
}
/// <summary>
/// Gets the red component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float R { get; }
/// <summary>
/// Gets the green component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float G { get; }
/// <summary>
/// Gets the blue component.
/// <remarks>A value usually ranging between 0 and 1.</remarks>
/// </summary>
public readonly float B { get; }
/// <summary>
/// Gets the Rgb color space <seealso cref="RgbWorkingSpaces"/>
/// </summary>
public readonly RgbWorkingSpace WorkingSpace { get; }
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a
/// <see cref="Rgb"/>.
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgb24 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
public static implicit operator Rgb(Rgb24 color) => new(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static implicit operator Rgb(Rgba32 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F);
public static implicit operator Rgb(Rgba32 color) => new(color.R / 255F, color.G / 255F, color.B / 255F);
/// <summary>
/// Compares two <see cref="Rgb"/> objects for equality.
@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
public Vector3 ToVector3() => new(this.R, this.G, this.B);
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
@ -158,10 +158,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Rgb other)
{
return this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
=> this.R.Equals(other.R)
&& this.G.Equals(other.G)
&& this.B.Equals(other.B);
}
}

46
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -15,25 +15,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly struct YCbCr : IEquatable<YCbCr>
{
private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(255);
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Y;
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Cb;
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Cr;
private static readonly Vector3 Max = new(255);
/// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct.
@ -60,6 +42,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Cr = vector.Z;
}
/// <summary>
/// Gets the Y luminance component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Y { get; }
/// <summary>
/// Gets the Cb chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Cb { get; }
/// <summary>
/// Gets the Cr chroma component.
/// <remarks>A value ranging between 0 and 255.</remarks>
/// </summary>
public readonly float Cr { get; }
/// <summary>
/// Compares two <see cref="YCbCr"/> objects for equality.
/// </summary>
@ -94,10 +94,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(YCbCr other)
{
return this.Y.Equals(other.Y)
&& this.Cb.Equals(other.Cb)
&& this.Cr.Equals(other.Cr);
}
=> this.Y.Equals(other.Y)
&& this.Cb.Equals(other.Cb)
&& this.Cr.Equals(other.Cr);
}
}

1
src/ImageSharp/Diagnostics/MemoryDiagnostics.cs

@ -9,6 +9,7 @@ namespace SixLabors.ImageSharp.Diagnostics
/// <summary>
/// Represents the method to handle <see cref="MemoryDiagnostics.UndisposedAllocation"/>.
/// </summary>
/// <param name="allocationStackTrace">The allocation stack trace.</param>
public delegate void UndisposedAllocationDelegate(string allocationStackTrace);
/// <summary>

25
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -491,7 +491,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int max = cmd[1];
int bytesToRead = (max + 1) / 2;
var run = new byte[bytesToRead];
byte[] run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length);
@ -501,13 +501,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
byte twoPixels = run[idx];
if (i % 2 == 0)
{
byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
buffer[count++] = leftPixel;
buffer[count++] = (byte)((twoPixels >> 4) & 0xF);
}
else
{
byte rightPixel = (byte)(twoPixels & 0xF);
buffer[count++] = rightPixel;
buffer[count++] = (byte)(twoPixels & 0xF);
idx++;
}
}
@ -597,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// Take this number of bytes from the stream as uncompressed data.
int length = cmd[1];
var run = new byte[length];
byte[] run = new byte[length];
this.stream.Read(run, 0, run.Length);
@ -676,7 +674,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// Take this number of bytes from the stream as uncompressed data.
int length = cmd[1];
var run = new byte[length * 3];
byte[] run = new byte[length * 3];
this.stream.Read(run, 0, run.Length);
@ -909,7 +907,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask);
int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask);
int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask);
var rgb = new Rgb24((byte)r, (byte)g, (byte)b);
Rgb24 rgb = new((byte)r, (byte)g, (byte)b);
color.FromRgb24(rgb);
pixelRow[x] = color;
@ -1164,7 +1162,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
uint g = (uint)(temp & greenMask) >> rightShiftGreenMask;
uint b = (uint)(temp & blueMask) >> rightShiftBlueMask;
float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f;
var vector4 = new Vector4(
Vector4 vector4 = new(
r * invMaxValueRed,
g * invMaxValueGreen,
b * invMaxValueBlue,
@ -1246,7 +1244,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize || headerSize > BmpInfoHeader.MaxHeaderSize)
if (headerSize is < BmpInfoHeader.CoreSize or > BmpInfoHeader.MaxHeaderSize)
{
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'.");
}
@ -1277,7 +1275,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// color masks for each color channel follow the info header.
if (this.infoHeader.Compression == BmpCompression.BitFields)
{
var bitfieldsBuffer = new byte[12];
byte[] bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
@ -1286,7 +1284,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
{
var bitfieldsBuffer = new byte[16];
byte[] bitfieldsBuffer = new byte[16];
this.stream.Read(bitfieldsBuffer, 0, 16);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
@ -1396,6 +1394,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="inverted">Whether the image orientation is inverted.</param>
/// <param name="palette">The color palette.</param>
/// <returns>Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</returns>
private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette)

47
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -160,10 +160,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Span<byte> buffer = stackalloc byte[infoHeaderSize];
this.WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame);
this.WriteColorProfile(stream, iccProfileData, buffer);
WriteColorProfile(stream, iccProfileData, buffer);
stream.Flush();
}
@ -184,34 +184,33 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int hResolution = 0;
int vResolution = 0;
if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio)
if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio
&& metadata.HorizontalResolution > 0
&& metadata.VerticalResolution > 0)
{
if (metadata.HorizontalResolution > 0 && metadata.VerticalResolution > 0)
switch (metadata.ResolutionUnits)
{
switch (metadata.ResolutionUnits)
{
case PixelResolutionUnit.PixelsPerInch:
case PixelResolutionUnit.PixelsPerInch:
hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution));
break;
hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerCentimeter:
case PixelResolutionUnit.PixelsPerCentimeter:
hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution));
break;
hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerMeter:
hResolution = (int)Math.Round(metadata.HorizontalResolution);
vResolution = (int)Math.Round(metadata.VerticalResolution);
case PixelResolutionUnit.PixelsPerMeter:
hResolution = (int)Math.Round(metadata.HorizontalResolution);
vResolution = (int)Math.Round(metadata.VerticalResolution);
break;
}
break;
}
}
var infoHeader = new BmpInfoHeader(
BmpInfoHeader infoHeader = new(
headerSize: infoHeaderSize,
height: height,
width: width,
@ -248,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="stream">The stream to write to.</param>
/// <param name="iccProfileData">The color profile data.</param>
/// <param name="buffer">The buffer.</param>
private void WriteColorProfile(Stream stream, byte[] iccProfileData, Span<byte> buffer)
private static void WriteColorProfile(Stream stream, byte[] iccProfileData, Span<byte> buffer)
{
if (iccProfileData != null)
{
@ -270,9 +269,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="iccProfileSize">The size in bytes of the color profile.</param>
/// <param name="infoHeader">The information header to write.</param>
/// <param name="buffer">The buffer to write to.</param>
private void WriteBitmapFileHeader(Stream stream, int infoHeaderSize, int colorPaletteSize, int iccProfileSize, BmpInfoHeader infoHeader, Span<byte> buffer)
private static void WriteBitmapFileHeader(Stream stream, int infoHeaderSize, int colorPaletteSize, int iccProfileSize, BmpInfoHeader infoHeader, Span<byte> buffer)
{
var fileHeader = new BmpFileHeader(
BmpFileHeader fileHeader = new(
type: BmpConstants.TypeMarkers.Bitmap,
fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + iccProfileSize + infoHeader.ImageSize,
reserved: 0,
@ -555,7 +554,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
if (pixelRowSpan.Length % 2 != 0)
{
stream.WriteByte((byte)((pixelRowSpan[pixelRowSpan.Length - 1] << 4) | 0));
stream.WriteByte((byte)((pixelRowSpan[^1] << 4) | 0));
}
for (int i = 0; i < rowPadding; i++)

15
src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs

@ -382,18 +382,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan<byte> data)
{
var infoHeader = new BmpInfoHeader(
BmpInfoHeader infoHeader = new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data[..4]),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4));
// The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
// Map the OS/2 value to the windows values.
switch (compression)
switch (BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)))
{
case 0:
infoHeader.Compression = BmpCompression.RGB;
@ -465,11 +463,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://docs.microsoft.com/de-de/windows/win32/api/wingdi/ns-wingdi-bitmapv5header?redirectedfrom=MSDN"/>
/// <exception cref="ArgumentException">Invalid size.</exception>
public static BmpInfoHeader ParseV5(ReadOnlySpan<byte> data)
{
if (data.Length < SizeV5)
{
throw new ArgumentException(nameof(data), $"Must be {SizeV5} bytes. Was {data.Length} bytes.");
throw new ArgumentException($"Must be {SizeV5} bytes. Was {data.Length} bytes.", nameof(data));
}
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
@ -545,13 +544,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal void VerifyDimensions()
{
const int MaximumBmpDimension = 65535;
const int maximumBmpDimension = 65535;
if (this.Width > MaximumBmpDimension || this.Height > MaximumBmpDimension)
if (this.Width > maximumBmpDimension || this.Height > maximumBmpDimension)
{
throw new InvalidOperationException(
$"The input bmp '{this.Width}x{this.Height}' is "
+ $"bigger then the max allowed size '{MaximumBmpDimension}x{MaximumBmpDimension}'");
+ $"bigger then the max allowed size '{maximumBmpDimension}x{maximumBmpDimension}'");
}
}
}

4
src/ImageSharp/Formats/DecoderOptions.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats
/// <summary>
/// Gets or sets the target size to decode the image into.
/// </summary>
public Size? TargetSize { get; set; } = null;
public Size? TargetSize { get; set; }
/// <summary>
/// Gets or sets the sampler to use when resizing during decoding.
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats
/// <summary>
/// Gets or sets a value indicating whether to ignore encoded metadata when decoding.
/// </summary>
public bool SkipMetadata { get; set; } = false;
public bool SkipMetadata { get; set; }
/// <summary>
/// Gets or sets the maximum number of image frames to decode, inclusive.

25
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -108,10 +108,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
// Write the header.
this.WriteHeader(stream);
WriteHeader(stream);
// Write the LSD.
int index = this.GetTransparentIndex(quantized);
int index = GetTransparentIndex(quantized);
this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream);
if (useGlobalTable)
@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength
&& frameMetadata.ColorTableLength > 0)
{
var options = new QuantizerOptions
QuantizerOptions options = new()
{
Dither = this.quantizer.Options.Dither,
DitherScale = this.quantizer.Options.DitherScale,
@ -211,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream);
this.WriteGraphicalControlExtension(frameMetadata, GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <returns>
/// The <see cref="int"/>.
/// </returns>
private int GetTransparentIndex<TPixel>(IndexedImageFrame<TPixel> quantized)
private static int GetTransparentIndex<TPixel>(IndexedImageFrame<TPixel> quantized)
where TPixel : unmanaged, IPixel<TPixel>
{
// Transparent pixels are much more likely to be found at the end of a palette.
@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber);
private static void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber);
/// <summary>
/// Writes the logical screen descriptor to the stream.
@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
}
var descriptor = new GifLogicalScreenDescriptor(
GifLogicalScreenDescriptor descriptor = new(
width: (ushort)width,
height: (ushort)height,
packed: packedValue,
@ -332,14 +332,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Application Extension: Loop repeat count.
if (frameCount > 1 && repeatCount != 1)
{
var loopingExtension = new GifNetscapeLoopingApplicationExtension(repeatCount);
GifNetscapeLoopingApplicationExtension loopingExtension = new(repeatCount);
this.WriteExtension(loopingExtension, stream);
}
// Application Extension: XMP Profile.
if (xmpProfile != null)
{
var xmpExtension = new GifXmpApplicationExtension(xmpProfile.Data);
GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data);
this.WriteExtension(xmpExtension, stream);
}
}
@ -411,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
disposalMethod: metadata.DisposalMethod,
transparencyFlag: transparencyIndex > -1);
var extension = new GifGraphicControlExtension(
GifGraphicControlExtension extension = new(
packed: packedValue,
delayTime: (ushort)metadata.FrameDelay,
transparencyIndex: unchecked((byte)transparencyIndex));
@ -422,6 +422,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Writes the provided extension to the stream.
/// </summary>
/// <typeparam name="TGifExtension">The type of gif extension.</typeparam>
/// <param name="extension">The extension to write to the stream.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteExtension<TGifExtension>(TGifExtension extension, Stream stream)
@ -472,7 +473,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
sortFlag: false,
localColorTableSize: this.bitDepth - 1);
var descriptor = new GifImageDescriptor(
GifImageDescriptor descriptor = new(
left: 0,
top: 0,
width: (ushort)image.Width,
@ -517,7 +518,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteImageData<TPixel>(IndexedImageFrame<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
using var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth);
using LzwEncoder encoder = new(this.memoryAllocator, (byte)this.bitDepth);
encoder.Encode(((IPixelSource)image).PixelBuffer, stream);
}
}

50
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
if (Avx.IsSupported)
{
var valueVec = Vector256.Create(value);
Vector256<float> valueVec = Vector256.Create(value);
this.V0 = Avx.Multiply(this.V0, valueVec);
this.V1 = Avx.Multiply(this.V1, valueVec);
this.V2 = Avx.Multiply(this.V2, valueVec);
@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
else
{
var valueVec = new Vector4(value);
Vector4 valueVec = new(value);
this.V0L *= valueVec;
this.V0R *= valueVec;
this.V1L *= valueVec;
@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Multiply all elements of the block by the corresponding elements of 'other'.
/// </summary>
/// <param name="other">The other block.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public unsafe void MultiplyInPlace(ref Block8x8F other)
{
@ -240,7 +241,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
if (Avx.IsSupported)
{
var valueVec = Vector256.Create(value);
Vector256<float> valueVec = Vector256.Create(value);
this.V0 = Avx.Add(this.V0, valueVec);
this.V1 = Avx.Add(this.V1, valueVec);
this.V2 = Avx.Add(this.V2, valueVec);
@ -252,7 +253,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
else
{
var valueVec = new Vector4(value);
Vector4 valueVec = new(value);
this.V0L += valueVec;
this.V0R += valueVec;
this.V1L += valueVec;
@ -330,6 +331,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
/// </summary>
/// <param name="maximum">The maximum value.</param>
public void NormalizeColorsAndRoundInPlace(float maximum)
{
if (SimdUtils.HasVector8)
@ -421,7 +423,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111);
var targetVector = Vector256.Create(value);
Vector256<int> targetVector = Vector256.Create(value);
ref Vector256<float> blockStride = ref this.V0;
for (int i = 0; i < RowCount; i++)
@ -468,20 +470,46 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
&& this.V7L == other.V7L
&& this.V7R == other.V7R;
/// <inheritdoc />
public override bool Equals(object obj) => this.Equals((Block8x8F)obj);
/// <inheritdoc />
public override int GetHashCode()
{
int left = HashCode.Combine(
this.V0L,
this.V1L,
this.V2L,
this.V3L,
this.V4L,
this.V5L,
this.V6L,
this.V7L);
int right = HashCode.Combine(
this.V0R,
this.V1R,
this.V2R,
this.V3R,
this.V4R,
this.V5R,
this.V6R,
this.V7R);
return HashCode.Combine(left, right);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
StringBuilder sb = new();
sb.Append('[');
for (int i = 0; i < Size - 1; i++)
{
sb.Append(this[i]);
sb.Append(',');
sb.Append(this[i]).Append(',');
}
sb.Append(this[Size - 1]);
sb.Append(']');
sb.Append(this[Size - 1]).Append(']');
return sb.ToString();
}

14
src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverterBase.cs

@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverterBase"/> class.
/// </summary>
/// <param name="colorSpace">The color space.</param>
/// <param name="precision">The precision in bits.</param>
protected JpegColorConverterBase(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
@ -66,6 +68,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary>
/// <param name="colorSpace">The color space.</param>
/// <param name="precision">The precision in bits.</param>
/// <exception cref="InvalidImageContentException">Invalid colorspace.</exception>
public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision)
{
JpegColorConverterBase converter = Array.Find(
@ -75,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
if (converter is null)
{
throw new Exception($"Could not find any converter for JpegColorSpace {colorSpace}!");
throw new InvalidImageContentException($"Could not find any converter for JpegColorSpace {colorSpace}!");
}
return converter;
@ -104,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// 5 color types with 2 supported precisions: 8 bit & 12 bit
const int colorConvertersCount = 5 * 2;
var converters = new JpegColorConverterBase[colorConvertersCount];
JpegColorConverterBase[] converters = new JpegColorConverterBase[colorConvertersCount];
// 8-bit converters
converters[0] = GetYCbCrConverter(8);
@ -126,6 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace.
/// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYCbCrConverter(int precision)
{
if (JpegColorConverterAvx.IsSupported)
@ -144,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the YccK colorspace.
/// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYccKConverter(int precision)
{
if (JpegColorConverterAvx.IsSupported)
@ -162,6 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the CMYK colorspace.
/// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetCmykConverter(int precision)
{
if (JpegColorConverterAvx.IsSupported)
@ -180,6 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the gray scale colorspace.
/// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetGrayScaleConverter(int precision)
{
if (JpegColorConverterAvx.IsSupported)
@ -198,6 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the RGB colorspace.
/// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetRgbConverter(int precision)
{
if (JpegColorConverterAvx.IsSupported)

25
src/ImageSharp/Formats/Jpeg/Components/Decoder/ArithmeticScanDecoder.cs

@ -225,10 +225,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
for (int i = 0; i < this.components.Length; i++)
{
var component = this.components[i] as ArithmeticDecodingComponent;
this.dcDecodingTables[i] = this.GetArithmeticTable(arithmeticDecodingTables, true, component.DcTableId);
ArithmeticDecodingComponent component = this.components[i] as ArithmeticDecodingComponent;
this.dcDecodingTables[i] = GetArithmeticTable(arithmeticDecodingTables, true, component.DcTableId);
component.DcStatistics = this.CreateOrGetStatisticsBin(true, component.DcTableId);
this.acDecodingTables[i] = this.GetArithmeticTable(arithmeticDecodingTables, false, component.AcTableId);
this.acDecodingTables[i] = GetArithmeticTable(arithmeticDecodingTables, false, component.AcTableId);
component.AcStatistics = this.CreateOrGetStatisticsBin(false, component.AcTableId);
}
}
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.spectralConverter.InjectFrameData(frame, jpegData);
}
private ArithmeticDecodingTable GetArithmeticTable(List<ArithmeticDecodingTable> arithmeticDecodingTables, bool isDcTable, int identifier)
private static ArithmeticDecodingTable GetArithmeticTable(List<ArithmeticDecodingTable> arithmeticDecodingTables, bool isDcTable, int identifier)
{
int tableClass = isDcTable ? 0 : 1;
@ -306,15 +306,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
var statistic = new ArithmeticStatistics(dc, identifier);
ArithmeticStatistics statistic = new(dc, identifier);
this.statistics.Add(statistic);
return statistic;
}
private void ParseBaselineData()
{
foreach (ArithmeticDecodingComponent component in this.components)
for (int i = 0; i < this.components.Length; i++)
{
ArithmeticDecodingComponent component = (ArithmeticDecodingComponent)this.components[i];
component.DcPredictor = 0;
component.DcContext = 0;
component.DcStatistics?.Reset();
@ -441,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++)
{
int order = this.frame.ComponentOrder[k];
var component = this.components[order] as ArithmeticDecodingComponent;
ArithmeticDecodingComponent component = this.components[order] as ArithmeticDecodingComponent;
ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
@ -491,7 +492,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataSingleComponent()
{
var component = this.frame.Components[0] as ArithmeticDecodingComponent;
ArithmeticDecodingComponent component = this.frame.Components[0] as ArithmeticDecodingComponent;
int mcuLines = this.frame.McusPerColumn;
int w = component.WidthInBlocks;
int h = component.SamplingFactors.Height;
@ -537,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataNonInterleaved()
{
var component = (ArithmeticDecodingComponent)this.components[this.frame.ComponentOrder[0]];
ArithmeticDecodingComponent component = (ArithmeticDecodingComponent)this.components[this.frame.ComponentOrder[0]];
ref JpegBitReader reader = ref this.scanBuffer;
int w = component.WidthInBlocks;
@ -586,7 +587,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++)
{
int order = this.frame.ComponentOrder[k];
var component = this.components[order] as ArithmeticDecodingComponent;
ArithmeticDecodingComponent component = this.components[order] as ArithmeticDecodingComponent;
ref ArithmeticDecodingTable dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
int h = component.HorizontalSamplingFactor;
@ -628,7 +629,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseProgressiveDataNonInterleaved()
{
var component = this.components[this.frame.ComponentOrder[0]] as ArithmeticDecodingComponent;
ArithmeticDecodingComponent component = this.components[this.frame.ComponentOrder[0]] as ArithmeticDecodingComponent;
ref JpegBitReader reader = ref this.scanBuffer;
int w = component.WidthInBlocks;
@ -1140,7 +1141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
for (int i = 0; i < this.components.Length; i++)
{
var component = this.components[i] as ArithmeticDecodingComponent;
ArithmeticDecodingComponent component = this.components[i] as ArithmeticDecodingComponent;
component.DcPredictor = 0;
}

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Globalization;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -62,8 +63,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <inheritdoc/>
public override string ToString()
{
return this.Marker.ToString("X");
}
=> this.Marker.ToString("X", CultureInfo.InvariantCulture);
}
}

2
src/ImageSharp/Formats/Jpeg/Components/Encoder/EncodingConfigs/JpegFrameConfig.cs

@ -39,6 +39,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
public int MaxVerticalSamplingFactor { get; }
public byte? AdobeColorTransformMarkerFlag { get; set; } = null;
public byte? AdobeColorTransformMarkerFlag { get; set; }
}
}

20
src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs

@ -122,16 +122,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
0xf9, 0xfa
});
/// <summary>
/// Gets count[i] - The number of codes of length i bits.
/// </summary>
public readonly byte[] Count;
/// <summary>
/// Gets value[i] - The decoded value of the codeword at the given index.
/// </summary>
public readonly byte[] Values;
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanSpec"/> struct.
/// </summary>
@ -146,5 +136,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
this.Count = count;
this.Values = values;
}
/// <summary>
/// Gets the count[i] - The number of codes of length i bits.
/// </summary>
public readonly byte[] Count { get; }
/// <summary>
/// Gets the value[i] - The decoded value of the codeword at the given index.
/// </summary>
public readonly byte[] Values { get; }
}
}

3
src/ImageSharp/Formats/Jpeg/Components/RowOctet.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>.
/// </summary>
/// <typeparam name="T">The type of element in each row.</typeparam>
[StructLayout(LayoutKind.Sequential)]
internal ref struct RowOctet<T>
where T : struct
@ -92,6 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(MethodImplOptions.NoInlining)]
private static Span<T> ThrowIndexOutOfRangeException()
#pragma warning disable CA2201 // Do not raise reserved exception types
=> throw new IndexOutOfRangeException();
#pragma warning restore CA2201 // Do not raise reserved exception types
}
}

95
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -213,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
using var spectralConverter = new SpectralConverter<TPixel>(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize);
using SpectralConverter<TPixel> spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize);
this.ParseStream(stream, spectralConverter, cancellationToken);
this.InitExifProfile();
this.InitIccProfile();
@ -257,12 +257,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
}
using var ms = new MemoryStream(tableBytes);
using var stream = new BufferedReadStream(this.configuration, ms);
using MemoryStream ms = new(tableBytes);
using BufferedReadStream stream = new(this.configuration, ms);
// Check for the Start Of Image marker.
int bytesRead = stream.Read(this.markerBuffer, 0, 2);
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
JpegFileMarker fileMarker = new(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
@ -290,7 +290,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
switch (fileMarker.Marker)
{
case JpegConstants.Markers.SOI:
break;
case JpegConstants.Markers.RST0:
case JpegConstants.Markers.RST7:
break;
@ -335,7 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Check for the Start Of Image marker.
stream.Read(this.markerBuffer, 0, 2);
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
JpegFileMarker fileMarker = new(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
@ -409,12 +408,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.ProcessStartOfScanMarker(stream, markerContentByteSize);
break;
}
else
{
// It's highly unlikely that APPn related data will be found after the SOS marker
// We should have gathered everything we need by now.
return;
}
// It's highly unlikely that APPn related data will be found after the SOS marker
// We should have gathered everything we need by now.
return;
case JpegConstants.Markers.DHT:
@ -654,7 +651,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.hasIcc)
{
var profile = new IccProfile(this.iccData);
IccProfile profile = new(this.iccData);
if (profile.CheckIsValid())
{
this.Metadata.IccProfile = profile;
@ -669,8 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.hasIptc)
{
var profile = new IptcProfile(this.iptcData);
this.Metadata.IptcProfile = profile;
this.Metadata.IptcProfile = new IptcProfile(this.iptcData);
}
}
@ -681,8 +677,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
if (this.hasXmp)
{
var profile = new XmpProfile(this.xmpData);
this.Metadata.XmpProfile = profile;
this.Metadata.XmpProfile = new XmpProfile(this.xmpData);
}
}
@ -723,7 +718,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
/// <param name="profile">The profile data array.</param>
/// <param name="extension">The array containing addition profile data.</param>
private void ExtendProfile(ref byte[] profile, byte[] extension)
private static void ExtendProfile(ref byte[] profile, byte[] extension)
{
int currentLength = profile.Length;
@ -751,7 +746,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
stream.Read(this.temp, 0, JFifMarker.Length);
remaining -= JFifMarker.Length;
JFifMarker.TryParse(this.temp, out this.jFif);
_ = JFifMarker.TryParse(this.temp, out this.jFif);
// TODO: thumbnail
if (remaining > 0)
@ -772,9 +767,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApp1Marker(BufferedReadStream stream, int remaining)
{
const int ExifMarkerLength = 6;
const int XmpMarkerLength = 29;
if (remaining < ExifMarkerLength || this.skipMetadata)
const int exifMarkerLength = 6;
const int xmpMarkerLength = 29;
if (remaining < exifMarkerLength || this.skipMetadata)
{
// Skip the application header length.
stream.Skip(remaining);
@ -787,8 +782,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
// XMP marker is the longer then the EXIF marker, so first try read the EXIF marker bytes.
stream.Read(this.temp, 0, ExifMarkerLength);
remaining -= ExifMarkerLength;
stream.Read(this.temp, 0, exifMarkerLength);
remaining -= exifMarkerLength;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.ExifMarker))
{
@ -803,15 +798,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else
{
// If the EXIF information exceeds 64K, it will be split over multiple APP1 markers.
this.ExtendProfile(ref this.exifData, profile);
ExtendProfile(ref this.exifData, profile);
}
remaining = 0;
}
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker[..ExifMarkerLength]))
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker[..exifMarkerLength]))
{
const int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
const int remainingXmpMarkerBytes = xmpMarkerLength - exifMarkerLength;
if (remaining < remainingXmpMarkerBytes || this.skipMetadata)
{
// Skip the application header length.
@ -819,7 +814,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return;
}
stream.Read(this.temp, ExifMarkerLength, remainingXmpMarkerBytes);
stream.Read(this.temp, exifMarkerLength, remainingXmpMarkerBytes);
remaining -= remainingXmpMarkerBytes;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker))
{
@ -834,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else
{
// If the XMP information exceeds 64K, it will be split over multiple APP1 markers.
this.ExtendProfile(ref this.xmpData, profile);
ExtendProfile(ref this.xmpData, profile);
}
remaining = 0;
@ -853,16 +848,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void ProcessApp2Marker(BufferedReadStream stream, int remaining)
{
// Length is 14 though we only need to check 12.
const int Icclength = 14;
if (remaining < Icclength || this.skipMetadata)
const int icclength = 14;
if (remaining < icclength || this.skipMetadata)
{
stream.Skip(remaining);
return;
}
byte[] identifier = new byte[Icclength];
stream.Read(identifier, 0, Icclength);
remaining -= Icclength; // We have read it by this point
byte[] identifier = new byte[icclength];
stream.Read(identifier, 0, icclength);
remaining -= icclength; // We have read it by this point
if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker))
{
@ -877,7 +872,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else
{
// If the ICC information exceeds 64K, it will be split over multiple APP2 markers
this.ExtendProfile(ref this.iccData, profile);
ExtendProfile(ref this.iccData, profile);
}
}
else
@ -971,7 +966,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
byte conditioningTableValue = (byte)stream.ReadByte();
remaining--;
var arithmeticTable = new ArithmeticDecodingTable(tableClass, identifier);
ArithmeticDecodingTable arithmeticTable = new(tableClass, identifier);
arithmeticTable.Configure(conditioningTableValue);
bool tableEntryReplaced = false;
@ -1029,16 +1024,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApp14Marker(BufferedReadStream stream, int remaining)
{
const int MarkerLength = AdobeMarker.Length;
if (remaining < MarkerLength)
const int markerLength = AdobeMarker.Length;
if (remaining < markerLength)
{
// Skip the application header length
stream.Skip(remaining);
return;
}
stream.Read(this.temp, 0, MarkerLength);
remaining -= MarkerLength;
stream.Read(this.temp, 0, markerLength);
remaining -= markerLength;
if (AdobeMarker.TryParse(this.temp, out this.adobe))
{
@ -1086,7 +1081,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
// 8 bit values
case 0:
{
// Validate: 8 bit table needs exactly 64 bytes
if (remaining < 64)
{
@ -1103,11 +1097,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
break;
}
// 16 bit values
case 1:
{
// Validate: 16 bit table needs exactly 128 bytes
if (remaining < 128)
{
@ -1124,14 +1116,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
break;
}
// Unknown precision - error
default:
{
JpegThrowHelper.ThrowBadQuantizationTablePrecision(tablePrecision);
break;
}
}
// Estimating quality
@ -1139,17 +1128,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{
// luminance table
case 0:
{
jpegMetadata.LuminanceQuality = Quantization.EstimateLuminanceQuality(ref table);
break;
}
// chrominance table
case 1:
{
jpegMetadata.ChrominanceQuality = Quantization.EstimateChrominanceQuality(ref table);
break;
}
}
}
}
@ -1312,7 +1297,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int codeValuesMaxByteSize = 256;
const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize;
var huffmanScanDecoder = this.scanDecoder as HuffmanScanDecoder;
HuffmanScanDecoder huffmanScanDecoder = this.scanDecoder as HuffmanScanDecoder;
if (huffmanScanDecoder is null)
{
JpegThrowHelper.ThrowInvalidImageContentException("missing huffman table data");
@ -1399,6 +1384,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Processes the SOS (Start of scan marker).
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
{
if (this.Frame is null)
@ -1479,11 +1466,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data");
}
int spectralStart = this.temp[0];
this.scanDecoder.SpectralStart = spectralStart;
this.scanDecoder.SpectralStart = this.temp[0];
int spectralEnd = this.temp[1];
this.scanDecoder.SpectralEnd = spectralEnd;
this.scanDecoder.SpectralEnd = this.temp[1];
int successiveApproximation = this.temp[2];
this.scanDecoder.SuccessiveHigh = successiveApproximation >> 4;

18
src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -15,22 +15,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{
return this.IsSupportedFileFormat(header) ? JpegFormat.Instance : null;
}
=> this.IsSupportedFileFormat(header) ? JpegFormat.Instance : null;
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
return header.Length >= this.HeaderSize &&
(this.IsJfif(header) || this.IsExif(header) || this.IsJpeg(header));
}
=> header.Length >= this.HeaderSize
&& (IsJfif(header) || IsExif(header) || IsJpeg(header));
/// <summary>
/// Returns a value indicating whether the given bytes identify Jfif data.
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJfif(ReadOnlySpan<byte> header) =>
private static bool IsJfif(ReadOnlySpan<byte> header) =>
header[6] == 0x4A && // J
header[7] == 0x46 && // F
header[8] == 0x49 && // I
@ -42,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsExif(ReadOnlySpan<byte> header) =>
private static bool IsExif(ReadOnlySpan<byte> header) =>
header[6] == 0x45 && // E
header[7] == 0x78 && // X
header[8] == 0x69 && // I
@ -55,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
/// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns>
private bool IsJpeg(ReadOnlySpan<byte> header) =>
private static bool IsJpeg(ReadOnlySpan<byte> header) =>
header[0] == 0xFF && // 255
header[1] == 0xD8; // 216
}

8
src/ImageSharp/Formats/Pbm/PbmImageFormatDetector.cs

@ -18,13 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Pbm
public int HeaderSize => 2;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? PbmFormat.Instance : null;
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => IsSupportedFileFormat(header) ? PbmFormat.Instance : null;
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
#pragma warning disable SA1131 // Use readable conditions
if (1 < (uint)header.Length)
#pragma warning restore SA1131 // Use readable conditions
if ((uint)header.Length > 1)
{
// Signature should be between P1 and P6.
return header[0] == P && (uint)(header[1] - Zero - 1) < (Seven - Zero - 1);

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

@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
var metadata = new ImageMetadata();
ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream;
this.currentStream.Skip(8);
@ -250,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
var metadata = new ImageMetadata();
ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream;
this.currentStream.Skip(8);
@ -434,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The data containing physical data.</param>
private static void ReadPhysicalChunk(ImageMetadata metadata, ReadOnlySpan<byte> data)
{
var physicalChunk = PhysicalChunkData.Parse(data);
PhysicalChunkData physicalChunk = PhysicalChunkData.Parse(data);
metadata.ResolutionUnits = physicalChunk.UnitSpecifier == byte.MinValue
? PixelResolutionUnit.AspectRatio
@ -560,7 +560,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel<TPixel>
{
using var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk);
using ZlibInflateStream deframeStream = new(this.currentStream, this.ReadNextDataChunk);
deframeStream.AllocateNewBytes(chunk.Length, true);
DeflateStream dataStream = deframeStream.CompressedStream;
@ -1018,7 +1018,7 @@ namespace SixLabors.ImageSharp.Formats.Png
string value = PngConstants.Encoding.GetString(data[(zeroIndex + 1)..]);
if (!this.TryReadTextChunkMetadata(baseMetadata, name, value))
if (!TryReadTextChunkMetadata(baseMetadata, name, value))
{
metadata.TextData.Add(new PngTextData(name, value, string.Empty, string.Empty));
}
@ -1058,8 +1058,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan<byte> compressedData = data[(zeroIndex + 2)..];
if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed) &&
!this.TryReadTextChunkMetadata(baseMetadata, name, uncompressed))
if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed)
&& !TryReadTextChunkMetadata(baseMetadata, name, uncompressed))
{
metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty));
}
@ -1074,10 +1074,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>True if metadata was successfully parsed from the text chunk. False if the
/// text chunk was not identified as metadata, and should be stored in the metadata
/// object unmodified.</returns>
private bool TryReadTextChunkMetadata(ImageMetadata baseMetadata, string chunkName, string chunkText)
private static bool TryReadTextChunkMetadata(ImageMetadata baseMetadata, string chunkName, string chunkText)
{
if (chunkName.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase) &&
this.TryReadLegacyExifTextChunk(baseMetadata, chunkText))
TryReadLegacyExifTextChunk(baseMetadata, chunkText))
{
// Successfully parsed legacy exif data from text
return true;
@ -1096,7 +1096,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="metadata">The <see cref="ImageMetadata"/> to store the decoded exif tags into.</param>
/// <param name="data">The contents of the "raw profile type exif" text chunk.</param>
private bool TryReadLegacyExifTextChunk(ImageMetadata metadata, string data)
private static bool TryReadLegacyExifTextChunk(ImageMetadata metadata, string data)
{
ReadOnlySpan<char> dataSpan = data.AsSpan();
dataSpan = dataSpan.TrimStart();
@ -1219,10 +1219,10 @@ namespace SixLabors.ImageSharp.Formats.Png
fixed (byte* compressedDataBase = compressedData)
{
using IMemoryOwner<byte> destBuffer = this.memoryAllocator.Allocate<byte>(this.configuration.StreamProcessingBufferSize);
using var memoryStreamOutput = new MemoryStream(compressedData.Length);
using var memoryStreamInput = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length);
using var bufferedStream = new BufferedReadStream(this.configuration, memoryStreamInput);
using var inflateStream = new ZlibInflateStream(bufferedStream);
using MemoryStream memoryStreamOutput = new(compressedData.Length);
using UnmanagedMemoryStream memoryStreamInput = new(compressedDataBase, compressedData.Length);
using BufferedReadStream bufferedStream = new(this.configuration, memoryStreamInput);
using ZlibInflateStream inflateStream = new(bufferedStream);
Span<byte> destUncompressedData = destBuffer.GetSpan();
if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
@ -1358,7 +1358,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else if (IsXmpTextData(keywordBytes))
{
var xmpProfile = new XmpProfile(data[dataStartIdx..].ToArray());
XmpProfile xmpProfile = new(data[dataStartIdx..].ToArray());
metadata.XmpProfile = xmpProfile;
}
else
@ -1561,18 +1561,17 @@ namespace SixLabors.ImageSharp.Formats.Png
{
return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
}
else
{
PngThrowHelper.ThrowInvalidChunkType();
// The IDE cannot detect the throw here.
return default;
}
PngThrowHelper.ThrowInvalidChunkType();
// The IDE cannot detect the throw here.
return default;
}
/// <summary>
/// Attempts to read the length of the next chunk.
/// </summary>
/// <param name="result">The result length. If the return type is <see langword="false"/> this parameter is passed uninitialized.</param>
/// <returns>
/// Whether the length was read.
/// </returns>
@ -1613,10 +1612,13 @@ namespace SixLabors.ImageSharp.Formats.Png
// Keywords should not be empty or have leading or trailing whitespace.
name = PngConstants.Encoding.GetString(keywordBytes);
return !string.IsNullOrWhiteSpace(name) && !name.StartsWith(" ") && !name.EndsWith(" ");
return !string.IsNullOrWhiteSpace(name)
&& !name.StartsWith(" ", StringComparison.Ordinal)
&& !name.EndsWith(" ", StringComparison.Ordinal);
}
private static bool IsXmpTextData(ReadOnlySpan<byte> keywordBytes) => keywordBytes.SequenceEqual(PngConstants.XmpKeyword);
private static bool IsXmpTextData(ReadOnlySpan<byte> keywordBytes)
=> keywordBytes.SequenceEqual(PngConstants.XmpKeyword);
private void SwapScanlineBuffers()
=> (this.scanline, this.previousScanline) = (this.previousScanline, this.scanline);

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

@ -245,62 +245,56 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
}
else
{
if (this.bitDepth == 8)
{
// 8 bit grayscale
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
rowSpan.Length);
}
else
{
// 1, 2, and 4 bit grayscale
using IMemoryOwner<byte> temp = this.memoryAllocator.Allocate<byte>(rowSpan.Length, AllocationOptions.Clean);
int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan();
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
tempSpan,
rowSpan.Length);
PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
}
else
{
if (this.use16Bit)
else if (this.bitDepth == 8)
{
// 16 bit grayscale + alpha
using IMemoryOwner<La32> laBuffer = this.memoryAllocator.Allocate<La32>(rowSpan.Length);
Span<La32> laSpan = laBuffer.GetSpan();
ref La32 laRef = ref MemoryMarshal.GetReference(laSpan);
PixelOperations<TPixel>.Instance.ToLa32(this.configuration, rowSpan, laSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < laSpan.Length; x++, o += 4)
{
La32 la = Unsafe.Add(ref laRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), la.L);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), la.A);
}
// 8 bit grayscale
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
rowSpan.Length);
}
else
{
// 8 bit grayscale + alpha
PixelOperations<TPixel>.Instance.ToLa16Bytes(
// 1, 2, and 4 bit grayscale
using IMemoryOwner<byte> temp = this.memoryAllocator.Allocate<byte>(rowSpan.Length, AllocationOptions.Clean);
int scaleFactor = 255 / (ColorNumerics.GetColorCountForBitDepth(this.bitDepth) - 1);
Span<byte> tempSpan = temp.GetSpan();
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
PixelOperations<TPixel>.Instance.ToL8Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
tempSpan,
rowSpan.Length);
PngEncoderHelpers.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor);
}
}
else if (this.use16Bit)
{
// 16 bit grayscale + alpha
using IMemoryOwner<La32> laBuffer = this.memoryAllocator.Allocate<La32>(rowSpan.Length);
Span<La32> laSpan = laBuffer.GetSpan();
ref La32 laRef = ref MemoryMarshal.GetReference(laSpan);
PixelOperations<TPixel>.Instance.ToLa32(this.configuration, rowSpan, laSpan);
// Can't map directly to byte array as it's big endian.
for (int x = 0, o = 0; x < laSpan.Length; x++, o += 4)
{
La32 la = Unsafe.Add(ref laRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), la.L);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), la.A);
}
}
else
{
// 8 bit grayscale + alpha
PixelOperations<TPixel>.Instance.ToLa16Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
rowSpan.Length);
}
}
/// <summary>
@ -316,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.bytesPerPixel)
{
case 4:
{
// 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes(
this.configuration,
@ -324,10 +318,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rawScanlineSpan,
rowSpan.Length);
break;
}
case 3:
{
// 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration,
@ -335,10 +328,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rawScanlineSpan,
rowSpan.Length);
break;
}
case 8:
{
// 16 bit Rgba
using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
{
@ -358,10 +350,9 @@ namespace SixLabors.ImageSharp.Formats.Png
}
break;
}
default:
{
// 16 bit Rgb
using (IMemoryOwner<Rgb48> rgbBuffer = this.memoryAllocator.Allocate<Rgb48>(rowSpan.Length))
{
@ -380,7 +371,6 @@ namespace SixLabors.ImageSharp.Formats.Png
}
break;
}
}
}
@ -413,8 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(rowSpan);
break;
case PngColorType.Rgb:
case PngColorType.RgbWithAlpha:
default:
this.CollectTPixelBytes(rowSpan);
break;
@ -424,6 +412,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Apply the line filter for the raw scanline to enable better compression.
/// </summary>
/// <param name="filter">The filtered buffer.</param>
/// <param name="attempt">Used for attempting optimized filtering.</param>
private void FilterPixelBytes(ref Span<byte> filter, ref Span<byte> attempt)
{
switch (this.options.FilterMethod)
@ -446,7 +436,6 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngFilterMethod.Paeth:
PaethFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _);
break;
case PngFilterMethod.Adaptive:
default:
this.ApplyOptimalFilteredScanline(ref filter, ref attempt);
break;
@ -503,6 +492,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
/// to be most compressible, using lowest total variation as proxy for compressibility.
/// </summary>
/// <param name="filter">The filtered buffer.</param>
/// <param name="attempt">Used for attempting optimized filtering.</param>
private void ApplyOptimalFilteredScanline(ref Span<byte> filter, ref Span<byte> attempt)
{
// Palette images don't compress well with adaptive filtering.
@ -551,7 +542,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteHeaderChunk(Stream stream)
{
var header = new PngHeader(
PngHeader header = new(
width: this.width,
height: this.height,
bitDepth: this.bitDepth,
@ -688,25 +679,24 @@ namespace SixLabors.ImageSharp.Formats.Png
}
int payloadLength = xmpData.Length + PngConstants.XmpKeyword.Length + iTxtHeaderSize;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{
Span<byte> payload = owner.GetSpan();
PngConstants.XmpKeyword.CopyTo(payload);
int bytesWritten = PngConstants.XmpKeyword.Length;
// Write the iTxt header (all zeros in this case).
Span<byte> iTxtHeader = payload[bytesWritten..];
iTxtHeader[4] = 0;
iTxtHeader[3] = 0;
iTxtHeader[2] = 0;
iTxtHeader[1] = 0;
iTxtHeader[0] = 0;
bytesWritten += 5;
// And the XMP data itself.
xmpData.CopyTo(payload[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> payload = owner.GetSpan();
PngConstants.XmpKeyword.CopyTo(payload);
int bytesWritten = PngConstants.XmpKeyword.Length;
// Write the iTxt header (all zeros in this case).
Span<byte> iTxtHeader = payload[bytesWritten..];
iTxtHeader[4] = 0;
iTxtHeader[3] = 0;
iTxtHeader[2] = 0;
iTxtHeader[1] = 0;
iTxtHeader[0] = 0;
bytesWritten += 5;
// And the XMP data itself.
xmpData.CopyTo(payload[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
/// <summary>
@ -725,16 +715,15 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] compressedData = this.GetZlibCompressedBytes(iccProfileBytes);
int payloadLength = ColorProfileName.Length + compressedData.Length + 2;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(ColorProfileName).CopyTo(outputBytes);
int bytesWritten = ColorProfileName.Length;
outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.EmbeddedColorProfile, outputBytes);
}
using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(ColorProfileName).CopyTo(outputBytes);
int bytesWritten = ColorProfileName.Length;
outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.EmbeddedColorProfile, outputBytes);
}
/// <summary>
@ -776,65 +765,59 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] languageTag = PngConstants.LanguageEncoding.GetBytes(textData.LanguageTag);
int payloadLength = keywordBytes.Length + textBytes.Length + translatedKeyword.Length + languageTag.Length + 5;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{
Span<byte> outputBytes = owner.GetSpan();
keywordBytes.CopyTo(outputBytes);
int bytesWritten = keywordBytes.Length;
outputBytes[bytesWritten++] = 0;
if (textData.Value.Length > this.options.TextCompressionThreshold)
{
// Indicate that the text is compressed.
outputBytes[bytesWritten++] = 1;
}
else
{
outputBytes[bytesWritten++] = 0;
}
outputBytes[bytesWritten++] = 0;
languageTag.CopyTo(outputBytes[bytesWritten..]);
bytesWritten += languageTag.Length;
outputBytes[bytesWritten++] = 0;
translatedKeyword.CopyTo(outputBytes[bytesWritten..]);
bytesWritten += translatedKeyword.Length;
outputBytes[bytesWritten++] = 0;
textBytes.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, outputBytes);
}
}
else
{
using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> outputBytes = owner.GetSpan();
keywordBytes.CopyTo(outputBytes);
int bytesWritten = keywordBytes.Length;
outputBytes[bytesWritten++] = 0;
if (textData.Value.Length > this.options.TextCompressionThreshold)
{
// Write zTXt chunk.
byte[] compressedData = this.GetZlibCompressedBytes(PngConstants.Encoding.GetBytes(textData.Value));
int payloadLength = textData.Keyword.Length + compressedData.Length + 2;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.CompressedText, outputBytes);
}
// Indicate that the text is compressed.
outputBytes[bytesWritten++] = 1;
}
else
{
// Write tEXt chunk.
int payloadLength = textData.Keyword.Length + textData.Value.Length + 1;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
outputBytes[bytesWritten++] = 0;
PngConstants.Encoding.GetBytes(textData.Value).CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.Text, outputBytes);
}
outputBytes[bytesWritten++] = 0;
}
outputBytes[bytesWritten++] = 0;
languageTag.CopyTo(outputBytes[bytesWritten..]);
bytesWritten += languageTag.Length;
outputBytes[bytesWritten++] = 0;
translatedKeyword.CopyTo(outputBytes[bytesWritten..]);
bytesWritten += translatedKeyword.Length;
outputBytes[bytesWritten++] = 0;
textBytes.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, outputBytes);
}
else if (textData.Value.Length > this.options.TextCompressionThreshold)
{
// Write zTXt chunk.
byte[] compressedData = this.GetZlibCompressedBytes(PngConstants.Encoding.GetBytes(textData.Value));
int payloadLength = textData.Keyword.Length + compressedData.Length + 2;
using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.CompressedText, outputBytes);
}
else
{
// Write tEXt chunk.
int payloadLength = textData.Keyword.Length + textData.Value.Length + 1;
using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(textData.Keyword).CopyTo(outputBytes);
int bytesWritten = textData.Keyword.Length;
outputBytes[bytesWritten++] = 0;
PngConstants.Encoding.GetBytes(textData.Value).CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.Text, outputBytes);
}
}
}
@ -846,15 +829,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>The compressed byte array.</returns>
private byte[] GetZlibCompressedBytes(byte[] dataBytes)
{
using (var memoryStream = new MemoryStream())
using MemoryStream memoryStream = new();
using (ZlibDeflateStream deflateStream = new(this.memoryAllocator, memoryStream, this.options.CompressionLevel))
{
using (var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, this.options.CompressionLevel))
{
deflateStream.Write(dataBytes);
}
return memoryStream.ToArray();
deflateStream.Write(dataBytes);
}
return memoryStream.ToArray();
}
/// <summary>
@ -944,9 +925,9 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] buffer;
int bufferLength;
using (var memoryStream = new MemoryStream())
using (MemoryStream memoryStream = new())
{
using (var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, this.options.CompressionLevel))
using (ZlibDeflateStream deflateStream = new(this.memoryAllocator, memoryStream, this.options.CompressionLevel))
{
if (this.options.InterlaceMethod == PngInterlaceMode.Adam7)
{
@ -1174,7 +1155,7 @@ namespace SixLabors.ImageSharp.Formats.Png
uint crc = Crc32.Calculate(this.buffer.AsSpan(4, 4)); // Write the type buffer
if (data != null && length > 0)
if (data.Length > 0 && length > 0)
{
stream.Write(data, offset, length);

28
src/ImageSharp/Formats/Png/PngTextData.cs

@ -72,9 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(PngTextData left, PngTextData right)
{
return left.Equals(right);
}
=> left.Equals(right);
/// <summary>
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values
@ -90,9 +88,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(PngTextData left, PngTextData right)
{
return !(left == right);
}
=> !(left == right);
/// <summary>
/// Indicates whether this instance and a specified object are equal.
@ -105,9 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// same value; otherwise, false.
/// </returns>
public override bool Equals(object obj)
{
return obj is PngTextData other && this.Equals(other);
}
=> obj is PngTextData other && this.Equals(other);
/// <summary>
/// Returns the hash code for this instance.
@ -115,7 +109,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
public override int GetHashCode() => HashCode.Combine(this.Keyword, this.Value, this.LanguageTag, this.TranslatedKeyword);
public override int GetHashCode()
=> HashCode.Combine(this.Keyword, this.Value, this.LanguageTag, this.TranslatedKeyword);
/// <summary>
/// Returns the fully qualified type name of this instance.
@ -123,7 +118,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// A <see cref="T:System.String"/> containing a fully qualified type name.
/// </returns>
public override string ToString() => $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]";
public override string ToString()
=> $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]";
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
@ -133,11 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(PngTextData other)
{
return this.Keyword.Equals(other.Keyword)
&& this.Value.Equals(other.Value)
&& this.LanguageTag.Equals(other.LanguageTag)
&& this.TranslatedKeyword.Equals(other.TranslatedKeyword);
}
=> this.Keyword.Equals(other.Keyword, StringComparison.OrdinalIgnoreCase)
&& this.Value.Equals(other.Value, StringComparison.OrdinalIgnoreCase)
&& this.LanguageTag.Equals(other.LanguageTag, StringComparison.OrdinalIgnoreCase)
&& this.TranslatedKeyword.Equals(other.TranslatedKeyword, StringComparison.OrdinalIgnoreCase);
}
}

10
src/ImageSharp/Formats/Png/PngThrowHelper.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Png
@ -11,31 +12,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal static class PngThrowHelper
{
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException)
=> throw new InvalidImageContentException(errorMessage, innerException);
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoHeader() => throw new InvalidImageContentException("PNG Image does not contain a header chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoData() => throw new InvalidImageContentException("PNG Image does not contain a data chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowMissingPalette() => throw new InvalidImageContentException("PNG Image does not contain a palette chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data.");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType(string message) => throw new InvalidImageContentException(message);
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotSupportedColor() => throw new NotSupportedException("Unsupported PNG color type");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowUnknownFilter() => throw new InvalidImageContentException("Unknown filter type.");
}

20
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -5,6 +5,7 @@ using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using SixLabors.ImageSharp.Advanced;
@ -99,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
imageDescriptor |= 0x1;
}
var fileHeader = new TgaFileHeader(
TgaFileHeader fileHeader = new(
idLength: 0,
colorMapType: 0,
imageType: imageType,
@ -158,8 +159,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
case TgaBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels);
break;
default:
break;
}
}
@ -181,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
TPixel currentPixel = pixelRow[x];
currentPixel.ToRgba32(ref color);
byte equalPixelCount = this.FindEqualPixels(pixelRow, x);
byte equalPixelCount = FindEqualPixels(pixelRow, x);
if (equalPixelCount > 0)
{
@ -193,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
else
{
// Write Raw Packet (i.e., Non-Run-Length Encoded):
byte unEqualPixelCount = this.FindUnEqualPixels(pixelRow, x);
byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x);
stream.WriteByte(unEqualPixelCount);
this.WritePixel(stream, currentPixel, color);
x++;
@ -227,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
break;
case TgaBitsPerPixel.Pixel16:
var bgra5551 = new Bgra5551(color.ToVector4());
Bgra5551 bgra5551 = new(color.ToVector4());
BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
stream.WriteByte(this.buffer[0]);
stream.WriteByte(this.buffer[1]);
@ -246,8 +245,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
stream.WriteByte(color.R);
stream.WriteByte(color.A);
break;
default:
break;
}
}
@ -258,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <param name="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the same pixels.</param>
/// <returns>The number of equal pixels.</returns>
private byte FindEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart)
private static byte FindEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart)
where TPixel : unmanaged, IPixel<TPixel>
{
byte equalPixelCount = 0;
@ -291,7 +288,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <param name="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the unequal pixels.</param>
/// <returns>The number of equal pixels.</returns>
private byte FindUnEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart)
private static byte FindUnEqualPixels<TPixel>(Span<TPixel> pixelRow, int xStart)
where TPixel : unmanaged, IPixel<TPixel>
{
byte unEqualPixelCount = 0;
@ -419,12 +416,13 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
/// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
/// <param name="sourcePixel">The pixel to get the luminance from.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetLuminance<TPixel>(TPixel sourcePixel)
where TPixel : unmanaged, IPixel<TPixel>
{
var vector = sourcePixel.ToVector4();
Vector4 vector = sourcePixel.ToVector4();
return ColorNumerics.GetBT709Luminance(ref vector, 256);
}
}

8
src/ImageSharp/Formats/Tga/TgaMetadata.cs

@ -20,9 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private TgaMetadata(TgaMetadata other)
{
this.BitsPerPixel = other.BitsPerPixel;
}
=> this.BitsPerPixel = other.BitsPerPixel;
/// <summary>
/// Gets or sets the number of bits per pixel.
@ -30,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Tga
public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24;
/// <summary>
/// Gets or sets the the number of alpha bits per pixel.
/// Gets or sets the number of alpha bits per pixel.
/// </summary>
public byte AlphaChannelBits { get; set; } = 0;
public byte AlphaChannelBits { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new TgaMetadata(this);

6
src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitCompressor.cs

@ -93,14 +93,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
uint codeLength;
if (runLength <= 63)
{
code = this.GetTermCode(runLength, out codeLength, isWhiteRun);
code = GetTermCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
x += (int)runLength;
}
else
{
runLength = this.GetBestFittingMakeupRunLength(runLength);
code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun);
runLength = GetBestFittingMakeupRunLength(runLength);
code = GetMakeupCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
x += (int)runLength;

26
src/ImageSharp/Formats/Tiff/Compression/Compressors/T6BitCompressor.cs

@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
{
Span<byte> row = pixelsAsGray.Slice(y * this.Width, this.Width);
uint a0 = 0;
uint a1 = row[0] == 0 ? 0 : this.FindRunEnd(row, 0);
uint b1 = referenceLine[0] == 0 ? 0 : this.FindRunEnd(referenceLine, 0);
uint a1 = row[0] == 0 ? 0 : FindRunEnd(row, 0);
uint b1 = referenceLine[0] == 0 ? 0 : FindRunEnd(referenceLine, 0);
while (true)
{
uint b2 = this.FindRunEnd(referenceLine, b1);
uint b2 = FindRunEnd(referenceLine, b1);
if (b2 < a1)
{
// Pass mode.
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
d = -(int)(a1 - b1);
}
if ((d >= -3) && (d <= 3))
if (d is >= -3 and <= 3)
{
// Vertical mode.
(uint length, uint code) = VerticalCodes[d + 3];
@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
// Horizontal mode.
this.WriteCode(3, 1, compressedData);
uint a2 = this.FindRunEnd(row, a1);
uint a2 = FindRunEnd(row, a1);
if ((a0 + a1 == 0) || (row[(int)a0] != 0))
{
this.WriteRun(a1 - a0, true, compressedData);
@ -119,9 +119,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
}
byte thisPixel = row[(int)a0];
a1 = this.FindRunEnd(row, a0, thisPixel);
b1 = this.FindRunEnd(referenceLine, a0, (byte)~thisPixel);
b1 = this.FindRunEnd(referenceLine, b1, thisPixel);
a1 = FindRunEnd(row, a0, thisPixel);
b1 = FindRunEnd(referenceLine, a0, (byte)~thisPixel);
b1 = FindRunEnd(referenceLine, b1, thisPixel);
}
// This row is now the reference line.
@ -149,14 +149,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// <returns>The index of the first pixel at or after <paramref name="startIndex"/>
/// that does not match <paramref name="color"/>, or the length of <paramref name="row"/>,
/// whichever comes first.</returns>
private uint FindRunEnd(Span<byte> row, uint startIndex, byte? color = null)
private static uint FindRunEnd(Span<byte> row, uint startIndex, byte? color = null)
{
if (startIndex >= row.Length)
{
return (uint)row.Length;
}
byte colorValue = color.GetValueOrDefault(row[(int)startIndex]);
byte colorValue = color ?? row[(int)startIndex];
for (int i = (int)startIndex; i < row.Length; i++)
{
if (row[i] != colorValue)
@ -188,13 +188,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
uint codeLength;
while (runLength > 63)
{
uint makeupLength = this.GetBestFittingMakeupRunLength(runLength);
code = this.GetMakeupCode(makeupLength, out codeLength, isWhiteRun);
uint makeupLength = GetBestFittingMakeupRunLength(runLength);
code = GetMakeupCode(makeupLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
runLength -= makeupLength;
}
code = this.GetTermCode(runLength, out codeLength, isWhiteRun);
code = GetTermCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
}
}

34
src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs

@ -195,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
this.bitPosition = 0;
}
private uint GetWhiteMakeupCode(uint runLength, out uint codeLength)
private static uint GetWhiteMakeupCode(uint runLength, out uint codeLength)
{
codeLength = 0;
@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0;
}
private uint GetBlackMakeupCode(uint runLength, out uint codeLength)
private static uint GetBlackMakeupCode(uint runLength, out uint codeLength)
{
codeLength = 0;
@ -275,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0;
}
private uint GetWhiteTermCode(uint runLength, out uint codeLength)
private static uint GetWhiteTermCode(uint runLength, out uint codeLength)
{
codeLength = 0;
@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0;
}
private uint GetBlackTermCode(uint runLength, out uint codeLength)
private static uint GetBlackTermCode(uint runLength, out uint codeLength)
{
codeLength = 0;
@ -390,7 +390,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// </summary>
/// <param name="runLength">A run length needing a makeup code</param>
/// <returns>The makeup length for <paramref name="runLength"/>.</returns>
protected uint GetBestFittingMakeupRunLength(uint runLength)
protected static uint GetBestFittingMakeupRunLength(uint runLength)
{
DebugGuard.MustBeGreaterThanOrEqualTo(runLength, MakeupRunLength[0], nameof(runLength));
@ -402,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
}
}
return MakeupRunLength[MakeupRunLength.Length - 1];
return MakeupRunLength[^1];
}
/// <summary>
@ -413,14 +413,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
/// If <c>false</c> the run is of black pixels</param>
/// <returns>The terminating code for a run of length <paramref name="runLength"/></returns>
protected uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun)
protected static uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun)
{
if (isWhiteRun)
{
return this.GetWhiteTermCode(runLength, out codeLength);
return GetWhiteTermCode(runLength, out codeLength);
}
return this.GetBlackTermCode(runLength, out codeLength);
return GetBlackTermCode(runLength, out codeLength);
}
/// <summary>
@ -431,14 +431,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
/// If <c>false</c> the run is of black pixels</param>
/// <returns>The makeup code for a run of length <paramref name="runLength"/></returns>
protected uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun)
protected static uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun)
{
if (isWhiteRun)
{
return this.GetWhiteMakeupCode(runLength, out codeLength);
return GetWhiteMakeupCode(runLength, out codeLength);
}
return this.GetBlackMakeupCode(runLength, out codeLength);
return GetBlackMakeupCode(runLength, out codeLength);
}
/// <summary>
@ -494,12 +494,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// <summary>
/// Writes a image compressed with CCITT T6 to the stream.
/// </summary>
/// <param name="pixelsAsGray">The pixels as 8-bit gray array.</param>
/// <param name="rows">The pixels as 8-bit gray array.</param>
/// <param name="height">The strip height.</param>
public override void CompressStrip(Span<byte> pixelsAsGray, int height)
public override void CompressStrip(Span<byte> rows, int height)
{
DebugGuard.IsTrue(pixelsAsGray.Length / height == this.Width, "Values must be equals");
DebugGuard.IsTrue(pixelsAsGray.Length % height == 0, "Values must be equals");
DebugGuard.IsTrue(rows.Length / height == this.Width, "Values must be equals");
DebugGuard.IsTrue(rows.Length % height == 0, "Values must be equals");
this.compressedDataBuffer.Clear();
Span<byte> compressedData = this.compressedDataBuffer.GetSpan();
@ -507,7 +507,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
this.bytePosition = 0;
this.bitPosition = 0;
this.CompressStrip(pixelsAsGray, height, compressedData);
this.CompressStrip(rows, height, compressedData);
// Write the compressed data to the stream.
int bytesToWrite = this.bitPosition != 0 ? this.bytePosition + 1 : this.bytePosition;

72
src/ImageSharp/Formats/Tiff/TiffBitsPerSample.cs

@ -10,31 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
public readonly struct TiffBitsPerSample : IEquatable<TiffBitsPerSample>
{
/// <summary>
/// The bits for the channel 0.
/// </summary>
public readonly ushort Channel0;
/// <summary>
/// The bits for the channel 1.
/// </summary>
public readonly ushort Channel1;
/// <summary>
/// The bits for the channel 2.
/// </summary>
public readonly ushort Channel2;
/// <summary>
/// The bits for the alpha channel.
/// </summary>
public readonly ushort Channel3;
/// <summary>
/// The number of channels.
/// </summary>
public readonly byte Channels;
/// <summary>
/// Initializes a new instance of the <see cref="TiffBitsPerSample"/> struct.
/// </summary>
@ -56,6 +31,53 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.Channels += (byte)(this.Channel3 != 0 ? 1 : 0);
}
/// <summary>
/// Gets the bits for the channel 0.
/// </summary>
public readonly ushort Channel0 { get; }
/// <summary>
/// Gets the bits for the channel 1.
/// </summary>
public readonly ushort Channel1 { get; }
/// <summary>
/// Gets the bits for the channel 2.
/// </summary>
public readonly ushort Channel2 { get; }
/// <summary>
/// Gets the bits for the alpha channel.
/// </summary>
public readonly ushort Channel3 { get; }
/// <summary>
/// Gets the number of channels.
/// </summary>
public readonly byte Channels { get; }
/// <summary>
/// Checks whether two <see cref="TiffBitsPerSample"/> structures are equal.
/// </summary>
/// <param name="left">The left hand <see cref="TiffBitsPerSample"/> operand.</param>
/// <param name="right">The right hand <see cref="TiffBitsPerSample"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator ==(TiffBitsPerSample left, TiffBitsPerSample right) => left.Equals(right);
/// <summary>
/// Checks whether two <see cref="TiffBitsPerSample"/> structures are not equal.
/// </summary>
/// <param name="left">The left hand <see cref="TiffBitsPerSample"/> operand.</param>
/// <param name="right">The right hand <see cref="TiffBitsPerSample"/> operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter;
/// otherwise, false.
/// </returns>
public static bool operator !=(TiffBitsPerSample left, TiffBitsPerSample right) => !(left == right);
/// <summary>
/// Tries to parse a ushort array and convert it into a TiffBitsPerSample struct.
/// </summary>

27
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
private readonly List<(long, uint)> frameMarkers = new List<(long, uint)>();
private readonly List<(long, uint)> frameMarkers = new();
/// <summary>
/// Initializes a new instance of the <see cref="TiffEncoderCore"/> class.
@ -148,15 +148,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// Make sure, the Encoder options makes sense in combination with each other.
this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor);
using var writer = new TiffStreamWriter(stream);
long ifdMarker = this.WriteHeader(writer);
using TiffStreamWriter writer = new(stream);
long ifdMarker = WriteHeader(writer);
Image<TPixel> metadataImage = image;
foreach (ImageFrame<TPixel> frame in image.Frames)
{
cancellationToken.ThrowIfCancellationRequested();
var subfileType = (TiffNewSubfileType)(frame.Metadata.ExifProfile?.GetValue(ExifTag.SubfileType)?.Value ?? (int)TiffNewSubfileType.FullImage);
TiffNewSubfileType subfileType = (TiffNewSubfileType)(frame.Metadata.ExifProfile?.GetValue(ExifTag.SubfileType)?.Value ?? (int)TiffNewSubfileType.FullImage);
ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker);
metadataImage = null;
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <returns>
/// The marker to write the first IFD offset.
/// </returns>
public long WriteHeader(TiffStreamWriter writer)
public static long WriteHeader(TiffStreamWriter writer)
{
writer.Write(ByteOrderMarker);
writer.Write(TiffConstants.HeaderMagicNumber);
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.compressionLevel,
this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None);
var entriesCollector = new TiffEncoderEntriesCollector();
TiffEncoderEntriesCollector entriesCollector = new();
using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create(
this.PhotometricInterpretation,
frame,
@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
entriesCollector,
(int)this.BitsPerPixel);
int rowsPerStrip = this.CalcRowsPerStrip(frame.Height, colorWriter.BytesPerRow, this.CompressionType);
int rowsPerStrip = CalcRowsPerStrip(frame.Height, colorWriter.BytesPerRow, this.CompressionType);
colorWriter.Write(compressor, rowsPerStrip);
@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <param name="bytesPerRow">The number of bytes per row.</param>
/// <param name="compression">The compression used.</param>
/// <returns>Number of rows per strip.</returns>
private int CalcRowsPerStrip(int height, int bytesPerRow, TiffCompression? compression)
private static int CalcRowsPerStrip(int height, int bytesPerRow, TiffCompression? compression)
{
DebugGuard.MustBeGreaterThan(height, 0, nameof(height));
DebugGuard.MustBeGreaterThan(bytesPerRow, 0, nameof(bytesPerRow));
@ -290,7 +290,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
uint dataOffset = (uint)writer.Position + (uint)(6 + (entries.Count * 12));
var largeDataBlocks = new List<byte[]>();
List<byte[]> largeDataBlocks = new();
entries.Sort((a, b) => (ushort)a.Tag - (ushort)b.Tag);
@ -440,13 +440,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
public static bool IsOneBitCompression(TiffCompression? compression)
{
if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax)
{
return true;
}
return false;
}
=> compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax;
}
}

6
src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
/// <summary>
/// Gets a value indicating whether the architecture is little-endian.
/// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian;
public static bool IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary>
/// Gets the current position within the stream.
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
/// <param name="value">The two-byte unsigned integer to write.</param>
public void Write(ushort value)
{
if (this.IsLittleEndian)
if (IsLittleEndian)
{
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value);
}
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
/// <param name="value">The four-byte unsigned integer to write.</param>
public void Write(uint value)
{
if (this.IsLittleEndian)
if (IsLittleEndian)
{
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value);
}

6
src/ImageSharp/Formats/Webp/BitReader/Vp8BitReader.cs

@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitReader
uint split = this.range >> 1;
ulong value = this.value >> pos;
ulong mask = (split - value) >> 31; // -1 or 0
this.bits -= 1;
this.bits--;
this.range = (this.range + (uint)mask) | 1;
this.value -= ((split + 1) & mask) << pos;
@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitReader
{
ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.Memory.Span.Slice((int)this.pos, 8));
this.pos += BitsCount >> 3;
ulong bits = this.ByteSwap64(inBits);
ulong bits = ByteSwap64(inBits);
bits >>= 64 - BitsCount;
this.value = bits | (this.value << BitsCount);
this.bits += BitsCount;
@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitReader
}
[MethodImpl(InliningOptions.ShortMethod)]
private ulong ByteSwap64(ulong x)
private static ulong ByteSwap64(ulong x)
{
x = ((x & 0xffffffff00000000ul) >> 32) | ((x & 0x00000000fffffffful) << 32);
x = ((x & 0xffff0000ffff0000ul) >> 16) | ((x & 0x0000ffff0000fffful) << 16);

14
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers.Binary;
using System.IO;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
@ -38,6 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// Initializes a new instance of the <see cref="BitWriterBase"/> class.
/// Used internally for cloning.
/// </summary>
/// <param name="buffer">The byte buffer.</param>
private protected BitWriterBase(byte[] buffer) => this.buffer = buffer;
public byte[] Buffer => this.buffer;
@ -102,12 +102,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// </summary>
/// <param name="metadataBytes">The metadata profile bytes.</param>
/// <returns>The metadata chunk size in bytes.</returns>
protected uint MetadataChunkSize(byte[] metadataBytes)
protected static uint MetadataChunkSize(byte[] metadataBytes)
{
uint metaSize = (uint)metadataBytes.Length;
uint metaChunkSize = WebpConstants.ChunkHeaderSize + metaSize + (metaSize & 1);
return metaChunkSize;
return WebpConstants.ChunkHeaderSize + metaSize + (metaSize & 1);
}
/// <summary>
@ -115,12 +113,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// </summary>
/// <param name="alphaBytes">The alpha chunk bytes.</param>
/// <returns>The alpha data chunk size in bytes.</returns>
protected uint AlphaChunkSize(Span<byte> alphaBytes)
protected static uint AlphaChunkSize(Span<byte> alphaBytes)
{
uint alphaSize = (uint)alphaBytes.Length + 1;
uint alphaChunkSize = WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
return alphaChunkSize;
return WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
}
/// <summary>

8
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -433,27 +433,27 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
{
isVp8X = true;
exifBytes = exifProfile.ToByteArray();
riffSize += this.MetadataChunkSize(exifBytes);
riffSize += MetadataChunkSize(exifBytes);
}
if (xmpProfile != null)
{
isVp8X = true;
xmpBytes = xmpProfile.Data;
riffSize += this.MetadataChunkSize(xmpBytes);
riffSize += MetadataChunkSize(xmpBytes);
}
if (iccProfile != null)
{
isVp8X = true;
iccProfileBytes = iccProfile.ToByteArray();
riffSize += this.MetadataChunkSize(iccProfileBytes);
riffSize += MetadataChunkSize(iccProfileBytes);
}
if (hasAlpha)
{
isVp8X = true;
riffSize += this.AlphaChunkSize(alphaData);
riffSize += AlphaChunkSize(alphaData);
}
if (isVp8X)

6
src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs

@ -150,21 +150,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
{
isVp8X = true;
exifBytes = exifProfile.ToByteArray();
riffSize += this.MetadataChunkSize(exifBytes);
riffSize += MetadataChunkSize(exifBytes);
}
if (xmpProfile != null)
{
isVp8X = true;
xmpBytes = xmpProfile.Data;
riffSize += this.MetadataChunkSize(xmpBytes);
riffSize += MetadataChunkSize(xmpBytes);
}
if (iccProfile != null)
{
isVp8X = true;
iccBytes = iccProfile.ToByteArray();
riffSize += this.MetadataChunkSize(iccBytes);
riffSize += MetadataChunkSize(iccBytes);
}
if (isVp8X)

50
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -344,7 +344,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
foreach (CrunchConfig crunchConfig in crunchConfigs)
{
bgra.CopyTo(encodedData);
bool useCache = true;
const bool useCache = true;
this.UsePalette = crunchConfig.EntropyIdx is EntropyIx.Palette or EntropyIx.PaletteAndSpatial;
this.UseSubtractGreenTransform = crunchConfig.EntropyIdx is EntropyIx.SubGreen or EntropyIx.SpatialSubGreen;
this.UsePredictorTransform = crunchConfig.EntropyIdx is EntropyIx.Spatial or EntropyIx.SpatialSubGreen;
@ -418,7 +418,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (isFirstConfig || this.bitWriter.NumBytes() < bestSize)
{
bestSize = this.bitWriter.NumBytes();
this.BitWriterSwap(ref this.bitWriter, ref bitWriterBest);
BitWriterSwap(ref this.bitWriter, ref bitWriterBest);
}
// Reset the bit writer for the following iteration if any.
@ -430,7 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
isFirstConfig = false;
}
this.BitWriterSwap(ref bitWriterBest, ref this.bitWriter);
BitWriterSwap(ref bitWriterBest, ref this.bitWriter);
}
/// <summary>
@ -485,7 +485,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
EntropyIx entropyIdx = this.AnalyzeEntropy(bgra, width, height, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero);
bool doNotCache = false;
var crunchConfigs = new List<CrunchConfig>();
List<CrunchConfig> crunchConfigs = new();
if (this.method == WebpEncodingMethod.BestQuality && this.quality == 100)
{
@ -540,7 +540,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Span<uint> bgra = this.EncodedData.GetSpan();
int histogramImageXySize = LosslessUtils.SubSampleSize(width, this.HistoBits) * LosslessUtils.SubSampleSize(height, this.HistoBits);
ushort[] histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes];
HuffmanTree[] huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++)
{
huffTree[i] = default;
@ -583,8 +583,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
this.bitWriter.Reset(bwInit);
var tmpHisto = new Vp8LHistogram(cacheBits);
var histogramImage = new List<Vp8LHistogram>(histogramImageXySize);
Vp8LHistogram tmpHisto = new(cacheBits);
List<Vp8LHistogram> histogramImage = new(histogramImageXySize);
for (int i = 0; i < histogramImageXySize; i++)
{
histogramImage.Add(new Vp8LHistogram(cacheBits));
@ -596,7 +596,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Create Huffman bit lengths and codes for each histogram image.
int histogramImageSize = histogramImage.Count;
int bitArraySize = 5 * histogramImageSize;
var huffmanCodes = new HuffmanTreeCode[bitArraySize];
HuffmanTreeCode[] huffmanCodes = new HuffmanTreeCode[bitArraySize];
for (int i = 0; i < huffmanCodes.Length; i++)
{
huffmanCodes[i] = default;
@ -657,7 +657,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
var tokens = new HuffmanTreeToken[maxTokens];
HuffmanTreeToken[] tokens = new HuffmanTreeToken[maxTokens];
for (int i = 0; i < tokens.Length; i++)
{
tokens[i] = new HuffmanTreeToken();
@ -767,13 +767,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int cacheBits = 0;
ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol.
var huffmanCodes = new HuffmanTreeCode[5];
HuffmanTreeCode[] huffmanCodes = new HuffmanTreeCode[5];
for (int i = 0; i < huffmanCodes.Length; i++)
{
huffmanCodes[i] = default;
}
var huffTree = new HuffmanTree[3UL * WebpConstants.CodeLengthCodes];
HuffmanTree[] huffTree = new HuffmanTree[3UL * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++)
{
huffTree[i] = default;
@ -794,7 +794,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
refsTmp1,
refsTmp2);
var histogramImage = new List<Vp8LHistogram>()
List<Vp8LHistogram> histogramImage = new()
{
new(cacheBits)
};
@ -819,7 +819,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
var tokens = new HuffmanTreeToken[maxTokens];
HuffmanTreeToken[] tokens = new HuffmanTreeToken[maxTokens];
for (int i = 0; i < tokens.Length; i++)
{
tokens[i] = new HuffmanTreeToken();
@ -842,8 +842,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int count = 0;
Span<int> symbols = this.scratch.AsSpan(0, 2);
symbols.Clear();
int maxBits = 8;
int maxSymbol = 1 << maxBits;
const int maxBits = 8;
const int maxSymbol = 1 << maxBits;
// Check whether it's a small tree.
for (int i = 0; i < huffmanCode.NumSymbols && count < 3; i++)
@ -896,7 +896,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i;
byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes];
short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes];
var huffmanCode = new HuffmanTreeCode
HuffmanTreeCode huffmanCode = new()
{
NumSymbols = WebpConstants.CodeLengthCodes,
CodeLengths = codeLengthBitDepth,
@ -1099,7 +1099,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint pix = currentRow[x];
uint pixDiff = LosslessUtils.SubPixels(pix, pixPrev);
pixPrev = pix;
if (pixDiff == 0 || (prevRow != null && pix == prevRow[x]))
if (pixDiff == 0 || (prevRow.Length > 0 && pix == prevRow[x]))
{
continue;
}
@ -1147,7 +1147,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
histo[(int)HistoIx.HistoBluePred * 256]++;
histo[(int)HistoIx.HistoAlphaPred * 256]++;
var bitEntropy = new Vp8LBitEntropy();
Vp8LBitEntropy bitEntropy = new();
for (int j = 0; j < (int)HistoIx.HistoTotal; j++)
{
bitEntropy.Init();
@ -1239,7 +1239,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private bool AnalyzeAndCreatePalette(ReadOnlySpan<uint> bgra, int width, int height)
{
Span<uint> palette = this.Palette.Memory.Span;
this.PaletteSize = this.GetColorPalette(bgra, width, height, palette);
this.PaletteSize = GetColorPalette(bgra, width, height, palette);
if (this.PaletteSize > WebpConstants.MaxPaletteSize)
{
this.PaletteSize = 0;
@ -1265,9 +1265,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="height">The image height.</param>
/// <param name="palette">The span to store the palette into.</param>
/// <returns>The number of palette entries.</returns>
private int GetColorPalette(ReadOnlySpan<uint> bgra, int width, int height, Span<uint> palette)
private static int GetColorPalette(ReadOnlySpan<uint> bgra, int width, int height, Span<uint> palette)
{
var colors = new HashSet<uint>();
HashSet<uint> colors = new();
for (int y = 0; y < height; y++)
{
ReadOnlySpan<uint> bgraRow = bgra.Slice(y * width, width);
@ -1547,7 +1547,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <returns>True, if the palette has no monotonous deltas.</returns>
private static bool PaletteHasNonMonotonousDeltas(Span<uint> palette, int numColors)
{
uint predict = 0x000000;
const uint predict = 0x000000;
byte signFound = 0x00;
for (int i = 0; i < numColors; i++)
{
@ -1637,7 +1637,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Create Huffman trees.
bool[] bufRle = new bool[maxNumSymbols];
var huffTree = new HuffmanTree[3 * maxNumSymbols];
HuffmanTree[] huffTree = new HuffmanTree[3 * maxNumSymbols];
for (int i = 0; i < huffTree.Length; i++)
{
huffTree[i] = default;
@ -1665,7 +1665,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint PaletteColorDistance(uint col1, uint col2)
{
uint diff = LosslessUtils.SubPixels(col1, col2);
uint moreWeightForRGBThanForAlpha = 9;
const uint moreWeightForRGBThanForAlpha = 9;
uint score = PaletteComponentDistance((diff >> 0) & 0xff);
score += PaletteComponentDistance((diff >> 8) & 0xff);
score += PaletteComponentDistance((diff >> 16) & 0xff);
@ -1730,7 +1730,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
[MethodImpl(InliningOptions.ShortMethod)]
private void BitWriterSwap(ref Vp8LBitWriter src, ref Vp8LBitWriter dst)
private static void BitWriterSwap(ref Vp8LBitWriter src, ref Vp8LBitWriter dst)
{
Vp8LBitWriter tmp = src;
src = dst;

66
src/ImageSharp/Formats/Webp/Lossless/WebpLosslessDecoder.cs

@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : unmanaged, IPixel<TPixel>
{
using (var decoder = new Vp8LDecoder(width, height, this.memoryAllocator))
using (Vp8LDecoder decoder = new(width, height, this.memoryAllocator))
{
this.DecodeImageStream(decoder, width, height, true);
this.DecodeImageData(decoder, decoder.Pixels.Memory.Span);
@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
decoder.Metadata.ColorCacheSize = 0;
}
this.UpdateDecoder(decoder, transformXSize, transformYSize);
UpdateDecoder(decoder, transformXSize, transformYSize);
if (isLevel0)
{
// level 0 complete.
@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void DecodeImageData(Vp8LDecoder decoder, Span<uint> pixelData)
{
int lastPixel = 0;
const int lastPixel = 0;
int width = decoder.Width;
int height = decoder.Height;
int row = lastPixel / width;
@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (hTreeGroup[0].IsTrivialCode)
{
pixelData[decodedPixels] = hTreeGroup[0].LiteralArb;
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
continue;
}
@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (code == PackedNonLiteralCode)
{
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
continue;
}
}
@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
pixelData[decodedPixels] = (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
}
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
}
else if (code < lenCodeLimit)
{
@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
pixelData[decodedPixels] = colorCache.Lookup(key);
this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
}
else
{
@ -342,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached)
private static void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span<uint> pixelData, ref int lastCached)
{
col++;
decodedPixels++;
@ -414,8 +414,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
int tableSize = TableSize[colorCacheBits];
var huffmanTables = new HuffmanCode[numHTreeGroups * tableSize];
var hTreeGroups = new HTreeGroup[numHTreeGroups];
HuffmanCode[] huffmanTables = new HuffmanCode[numHTreeGroups * tableSize];
HTreeGroup[] hTreeGroups = new HTreeGroup[numHTreeGroups];
Span<HuffmanCode> huffmanTable = huffmanTables.AsSpan();
int[] codeLengths = new int[maxAlphabetSize];
for (int i = 0; i < numHTreeGroupsMax; i++)
@ -488,7 +488,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
if (hTreeGroup.UsePackedTable)
{
this.BuildPackedTable(hTreeGroup);
BuildPackedTable(hTreeGroup);
}
}
@ -546,9 +546,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.ReadHuffmanCodeLengths(table, codeLengthCodeLengths, alphabetSize, codeLengths);
}
int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);
return size;
return HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);
}
private void ReadHuffmanCodeLengths(Span<HuffmanCode> table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths)
@ -622,8 +620,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
{
var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
var transform = new Vp8LTransform(transformType, xSize, ySize);
Vp8LTransformType transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
Vp8LTransform transform = new(transformType, xSize, ySize);
// Each transform is allowed to be used only once.
foreach (Vp8LTransform decoderTransform in decoder.Transforms)
@ -643,11 +641,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// The transform data contains color table size and the entries in the color table.
// 8 bit value for color table size.
uint numColors = this.bitReader.ReadValue(8) + 1;
int bits = numColors > 16 ? 0
: numColors > 4 ? 1
: numColors > 2 ? 2
: 3;
transform.Bits = bits;
if (numColors > 16)
{
transform.Bits = 0;
}
else if (numColors > 4)
{
transform.Bits = 1;
}
else if (numColors > 2)
{
transform.Bits = 2;
}
else
{
transform.Bits = 3;
}
using (IMemoryOwner<uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
{
int finalNumColors = 1 << (8 >> transform.Bits);
@ -660,15 +670,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform:
{
// The first 3 bits of prediction data define the block width and height in number of bits.
transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
transform.Data = transformData;
transform.Data = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
break;
}
}
decoder.Transforms.Add(transform);
@ -687,8 +695,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
for (int i = transforms.Count - 1; i >= 0; i--)
{
Vp8LTransform transform = transforms[i];
Vp8LTransformType transformType = transform.TransformType;
switch (transformType)
switch (transform.TransformType)
{
case Vp8LTransformType.PredictorTransform:
using (IMemoryOwner<uint> output = memoryAllocator.Allocate<uint>(pixelData.Length, AllocationOptions.Clean))
@ -806,7 +813,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
dec.ExtractPalettedAlphaRows(row > lastRow ? lastRow : row);
}
private void UpdateDecoder(Vp8LDecoder decoder, int width, int height)
private static void UpdateDecoder(Vp8LDecoder decoder, int width, int height)
{
int numBits = decoder.Metadata.HuffmanSubSampleBits;
decoder.Width = width;
@ -831,7 +838,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return code.Value;
}
private void BuildPackedTable(HTreeGroup hTreeGroup)
private static void BuildPackedTable(HTreeGroup hTreeGroup)
{
for (uint code = 0; code < HuffmanUtils.HuffmanPackedTableSize; code++)
{
@ -859,6 +866,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// Decodes the next Huffman code from the bit-stream.
/// FillBitWindow() needs to be called at minimum every second call to ReadSymbol, in order to pre-fetch enough bits.
/// </summary>
/// <param name="table">The Huffman table.</param>
private uint ReadSymbol(Span<HuffmanCode> table)
{
uint val = (uint)this.bitReader.PrefetchBits();

97
src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs

@ -34,7 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
private readonly int predsWidth;
// Array to record the position of the top sample to pass to the prediction functions.
/// <summary>
/// Array to record the position of the top sample to pass to the prediction functions.
/// </summary>
private readonly byte[] vp8TopLeftI4 =
{
17, 21, 25, 29,
@ -46,9 +48,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private int currentMbIdx;
private int nzIdx;
private int predIdx;
private int yTopIdx;
private int uvTopIdx;
@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.yTopIdx = 0;
this.uvTopIdx = 0;
this.predsWidth = (4 * mbw) + 1;
this.predIdx = this.predsWidth;
this.PredIdx = this.predsWidth;
this.YuvIn = new byte[WebpConstants.Bps * 16];
this.YuvOut = new byte[WebpConstants.Bps * 16];
this.YuvOut2 = new byte[WebpConstants.Bps * 16];
@ -85,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Scratch3 = new int[16];
// To match the C initial values of the reference implementation, initialize all with 204.
byte defaultInitVal = 204;
const byte defaultInitVal = 204;
this.YuvIn.AsSpan().Fill(defaultInitVal);
this.YuvOut.AsSpan().Fill(defaultInitVal);
this.YuvOut2.AsSpan().Fill(defaultInitVal);
@ -160,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary>
/// Gets the current start index of the intra mode predictors.
/// </summary>
public int PredIdx => this.predIdx;
public int PredIdx { get; private set; }
/// <summary>
/// Gets the non-zero pattern.
@ -238,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void Init() => this.Reset();
public void InitFilter()
public static void InitFilter()
{
// TODO: add support for autofilter
}
@ -299,9 +298,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> yuvIn = this.YuvIn.AsSpan(YOffEnc);
Span<byte> uIn = this.YuvIn.AsSpan(UOffEnc);
Span<byte> vIn = this.YuvIn.AsSpan(VOffEnc);
this.ImportBlock(ySrc, yStride, yuvIn, w, h, 16);
this.ImportBlock(uSrc, uvStride, uIn, uvw, uvh, 8);
this.ImportBlock(vSrc, uvStride, vIn, uvw, uvh, 8);
ImportBlock(ySrc, yStride, yuvIn, w, h, 16);
ImportBlock(uSrc, uvStride, uIn, uvw, uvh, 8);
ImportBlock(vSrc, uvStride, vIn, uvw, uvh, 8);
if (!importBoundarySamples)
{
@ -331,9 +330,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
vLeft[0] = v[uvStartIdx - 1 - uvStride];
}
this.ImportLine(y[(yStartIdx - 1)..], yStride, yLeft[1..], h, 16);
this.ImportLine(u[(uvStartIdx - 1)..], uvStride, uLeft[1..], uvh, 8);
this.ImportLine(v[(uvStartIdx - 1)..], uvStride, vLeft[1..], uvh, 8);
ImportLine(y[(yStartIdx - 1)..], yStride, yLeft[1..], h, 16);
ImportLine(u[(uvStartIdx - 1)..], uvStride, uLeft[1..], uvh, 8);
ImportLine(v[(uvStartIdx - 1)..], uvStride, vLeft[1..], uvh, 8);
}
Span<byte> yTop = this.YTop.AsSpan(this.yTopIdx, 16);
@ -344,9 +343,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
else
{
this.ImportLine(y[(yStartIdx - yStride)..], 1, yTop, w, 16);
this.ImportLine(u[(uvStartIdx - uvStride)..], 1, this.UvTop.AsSpan(this.uvTopIdx, 8), uvw, 8);
this.ImportLine(v[(uvStartIdx - uvStride)..], 1, this.UvTop.AsSpan(this.uvTopIdx + 8, 8), uvw, 8);
ImportLine(y[(yStartIdx - yStride)..], 1, yTop, w, 16);
ImportLine(u[(uvStartIdx - uvStride)..], 1, this.UvTop.AsSpan(this.uvTopIdx, 8), uvw, 8);
ImportLine(v[(uvStartIdx - uvStride)..], 1, this.UvTop.AsSpan(this.uvTopIdx + 8, 8), uvw, 8);
}
}
@ -386,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int MbAnalyzeBestIntra16Mode()
{
int maxMode = MaxIntra16Mode;
const int maxMode = MaxIntra16Mode;
int mode;
int bestAlpha = DefaultAlpha;
int bestMode = 0;
@ -394,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.MakeLuma16Preds();
for (mode = 0; mode < maxMode; mode++)
{
var histo = new Vp8Histogram();
Vp8Histogram histo = new();
histo.CollectHistogram(this.YuvIn.AsSpan(YOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]), 0, 16);
int alpha = histo.GetAlpha();
if (alpha > bestAlpha)
@ -411,15 +410,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int MbAnalyzeBestIntra4Mode(int bestAlpha)
{
byte[] modes = new byte[16];
int maxMode = MaxIntra4Mode;
var totalHisto = new Vp8Histogram();
const int maxMode = MaxIntra4Mode;
Vp8Histogram totalHisto = new();
int curHisto = 0;
this.StartI4();
do
{
int mode;
int bestModeAlpha = DefaultAlpha;
var histos = new Vp8Histogram[2];
Vp8Histogram[] histos = new Vp8Histogram[2];
Span<byte> src = this.YuvIn.AsSpan(YOffEnc + WebpLookupTables.Vp8Scan[this.I4]);
this.MakeIntra4Preds();
@ -459,13 +458,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int bestAlpha = DefaultAlpha;
int smallestAlpha = 0;
int bestMode = 0;
int maxMode = MaxUvMode;
const int maxMode = MaxUvMode;
int mode;
this.MakeChroma8Preds();
for (mode = 0; mode < maxMode; ++mode)
{
var histo = new Vp8Histogram();
Vp8Histogram histo = new();
histo.CollectHistogram(this.YuvIn.AsSpan(UOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]), 16, 16 + 4 + 4);
int alpha = histo.GetAlpha();
if (alpha > bestAlpha)
@ -487,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetIntra16Mode(int mode)
{
Span<byte> preds = this.Preds.AsSpan(this.predIdx);
Span<byte> preds = this.Preds.AsSpan(this.PredIdx);
for (int y = 0; y < 4; y++)
{
preds[..4].Fill((byte)mode);
@ -500,7 +499,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetIntra4Mode(byte[] modes)
{
int modesIdx = 0;
int predIdx = this.predIdx;
int predIdx = this.PredIdx;
for (int y = 4; y > 0; y--)
{
modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx));
@ -542,7 +541,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public short[] GetCostModeI4(byte[] modes)
{
int predsWidth = this.predsWidth;
int predIdx = this.predIdx;
int predIdx = this.PredIdx;
int x = this.I4 & 3;
int y = this.I4 >> 2;
int left = x == 0 ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1];
@ -635,7 +634,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
this.currentMbIdx++;
this.nzIdx++;
this.predIdx += 4;
this.PredIdx += 4;
this.yTopIdx += 16;
this.uvTopIdx += 16;
}
@ -772,35 +771,35 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<int> leftNz = this.LeftNz;
// Top-Y
topNz[0] = this.Bit(tnz, 12);
topNz[1] = this.Bit(tnz, 13);
topNz[2] = this.Bit(tnz, 14);
topNz[3] = this.Bit(tnz, 15);
topNz[0] = Bit(tnz, 12);
topNz[1] = Bit(tnz, 13);
topNz[2] = Bit(tnz, 14);
topNz[3] = Bit(tnz, 15);
// Top-U
topNz[4] = this.Bit(tnz, 18);
topNz[5] = this.Bit(tnz, 19);
topNz[4] = Bit(tnz, 18);
topNz[5] = Bit(tnz, 19);
// Top-V
topNz[6] = this.Bit(tnz, 22);
topNz[7] = this.Bit(tnz, 23);
topNz[6] = Bit(tnz, 22);
topNz[7] = Bit(tnz, 23);
// DC
topNz[8] = this.Bit(tnz, 24);
topNz[8] = Bit(tnz, 24);
// left-Y
leftNz[0] = this.Bit(lnz, 3);
leftNz[1] = this.Bit(lnz, 7);
leftNz[2] = this.Bit(lnz, 11);
leftNz[3] = this.Bit(lnz, 15);
leftNz[0] = Bit(lnz, 3);
leftNz[1] = Bit(lnz, 7);
leftNz[2] = Bit(lnz, 11);
leftNz[3] = Bit(lnz, 15);
// left-U
leftNz[4] = this.Bit(lnz, 17);
leftNz[5] = this.Bit(lnz, 19);
leftNz[4] = Bit(lnz, 17);
leftNz[5] = Bit(lnz, 19);
// left-V
leftNz[6] = this.Bit(lnz, 21);
leftNz[7] = this.Bit(lnz, 23);
leftNz[6] = Bit(lnz, 21);
leftNz[7] = Bit(lnz, 23);
// left-DC is special, iterated separately.
}
@ -826,7 +825,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Nz[this.nzIdx] = nz;
}
private void ImportBlock(Span<byte> src, int srcStride, Span<byte> dst, int w, int h, int size)
private static void ImportBlock(Span<byte> src, int srcStride, Span<byte> dst, int w, int h, int size)
{
int dstIdx = 0;
int srcIdx = 0;
@ -852,7 +851,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private void ImportLine(Span<byte> src, int srcStride, Span<byte> dst, int len, int totalLen)
private static void ImportLine(Span<byte> src, int srcStride, Span<byte> dst, int len, int totalLen)
{
int i;
int srcIdx = 0;
@ -892,7 +891,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.nzIdx = 1; // note: in reference source nz starts at -1.
this.yTopIdx = 0;
this.uvTopIdx = 0;
this.predIdx = this.predsWidth + (y * 4 * this.predsWidth);
this.PredIdx = this.predsWidth + (y * 4 * this.predsWidth);
this.InitLeft();
}
@ -931,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.TopDerr.AsSpan().Clear();
}
private int Bit(uint nz, int n) => (nz & (1 << n)) != 0 ? 1 : 0;
private static int Bit(uint nz, int n) => (nz & (1 << n)) != 0 ? 1 : 0;
/// <summary>
/// Set count down.

83
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -123,10 +123,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
this.alphaCompression = alphaCompression;
this.rdOptLevel = method is WebpEncodingMethod.BestQuality ? Vp8RdLevel.RdOptTrellisAll
: method >= WebpEncodingMethod.Level5 ? Vp8RdLevel.RdOptTrellis
: method >= WebpEncodingMethod.Level3 ? Vp8RdLevel.RdOptBasic
: Vp8RdLevel.RdOptNone;
if (method is WebpEncodingMethod.BestQuality)
{
this.rdOptLevel = Vp8RdLevel.RdOptTrellisAll;
}
else if (method >= WebpEncodingMethod.Level5)
{
this.rdOptLevel = Vp8RdLevel.RdOptTrellis;
}
else if (method >= WebpEncodingMethod.Level3)
{
this.rdOptLevel = Vp8RdLevel.RdOptBasic;
}
else
{
this.rdOptLevel = Vp8RdLevel.RdOptNone;
}
int pixelCount = width * height;
this.Mbw = (width + 15) >> 4;
@ -142,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.TopDerr = new sbyte[this.Mbw * 4];
// TODO: make partition_limit configurable?
int limit = 100; // original code: limit = 100 - config->partition_limit;
const int limit = 100; // original code: limit = 100 - config->partition_limit;
this.maxI4HeaderBits =
256 * 16 * 16 * limit * limit / (100 * 100); // ... modulated with a quadratic curve.
@ -307,7 +319,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int yStride = width;
int uvStride = (yStride + 1) >> 1;
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
Vp8EncIterator it = new(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
int[] alphas = new int[WebpConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
int totalMb = this.Mbw * this.Mbw;
@ -327,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Extract and encode alpha channel data, if present.
int alphaDataSize = 0;
bool alphaCompressionSucceeded = false;
using var alphaEncoder = new AlphaEncoder();
using AlphaEncoder alphaEncoder = new();
Span<byte> alphaData = Span<byte>.Empty;
if (hasAlpha)
{
@ -344,9 +356,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Stats-collection loop.
this.StatLoop(width, height, yStride, uvStride);
it.Init();
it.InitFilter();
var info = new Vp8ModeScore();
var residual = new Vp8Residual();
Vp8EncIterator.InitFilter();
Vp8ModeScore info = new();
Vp8Residual residual = new();
do
{
bool dontUseSkip = !this.Proba.UseSkipProba;
@ -399,17 +411,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// This is used for deciding optimal probabilities. It also modifies the
/// quantizer value if some target (size, PSNR) was specified.
/// </summary>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="yStride">The y-luminance stride.</param>
/// <param name="uvStride">The uv stride.</param>
private void StatLoop(int width, int height, int yStride, int uvStride)
{
int targetSize = 0; // TODO: target size is hardcoded.
float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded.
bool doSearch = targetSize > 0 || targetPsnr > 0;
const int targetSize = 0; // TODO: target size is hardcoded.
const float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded.
const bool doSearch = targetSize > 0 || targetPsnr > 0;
bool fastProbe = (this.method == 0 || this.method == WebpEncodingMethod.Level3) && !doSearch;
int numPassLeft = this.entropyPasses;
Vp8RdLevel rdOpt = this.method >= WebpEncodingMethod.Level3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
int nbMbs = this.Mbw * this.Mbh;
var stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality);
PassStats stats = new(targetSize, targetPsnr, QMin, QMax, this.quality);
this.Proba.ResetTokenStats();
// Fast mode: quick analysis pass over few mbs. Better than nothing.
@ -450,7 +466,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// If no target size: just do several pass without changing 'q'
if (doSearch)
{
// Unreachable due to hardcoding above.
#pragma warning disable CS0162 // Unreachable code detected
stats.ComputeNextQ();
#pragma warning restore CS0162 // Unreachable code detected
if (MathF.Abs(stats.Dq) <= DqLimit)
{
break;
@ -474,7 +493,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.GetSpan();
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
Vp8EncIterator it = new(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
long size = 0;
long sizeP0 = 0;
long distortion = 0;
@ -482,7 +501,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
it.Init();
this.SetLoopParams(stats.Q);
var info = new Vp8ModeScore();
Vp8ModeScore info = new();
do
{
info.Clear();
@ -540,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// this '>> 3' accounts for some inverse WHT scaling
int delta = (dqm.MaxEdge * dqm.Y2.Q[1]) >> 3;
int level = this.FilterStrengthFromDelta(this.FilterHeader.Sharpness, delta);
int level = FilterStrengthFromDelta(this.FilterHeader.Sharpness, delta);
if (level > dqm.FStrength)
{
dqm.FStrength = level;
@ -757,8 +776,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void SetupFilterStrength()
{
int filterSharpness = 0; // TODO: filterSharpness is hardcoded
int filterType = 1; // TODO: filterType is hardcoded
const int filterSharpness = 0; // TODO: filterSharpness is hardcoded
const int filterType = 1; // TODO: filterType is hardcoded
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
int level0 = 5 * this.filterStrength;
@ -768,11 +787,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// We focus on the quantization of AC coeffs.
int qstep = WebpLookupTables.AcTable[Numerics.Clamp(m.Quant, 0, 127)] >> 2;
int baseStrength = this.FilterStrengthFromDelta(this.FilterHeader.Sharpness, qstep);
int baseStrength = FilterStrengthFromDelta(this.FilterHeader.Sharpness, qstep);
// Segments with lower complexity ('beta') will be less filtered.
int f = baseStrength * level0 / (256 + m.Beta);
m.FStrength = f < WebpConstants.FilterStrengthCutoff ? 0 : f > 63 ? 63 : f;
if (f < WebpConstants.FilterStrengthCutoff)
{
m.FStrength = 0;
}
else if (f > 63)
{
m.FStrength = 63;
}
else
{
m.FStrength = f;
}
}
// We record the initial strength (mainly for the case of 1-segment only).
@ -1027,10 +1057,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// Same as CodeResiduals, but doesn't actually write anything.
/// Instead, it just records the event distribution.
/// </summary>
/// <param name="it">The iterator.</param>
/// <param name="rd">The score accumulator.</param>
private void RecordResiduals(Vp8EncIterator it, Vp8ModeScore rd)
{
int x, y, ch;
var residual = new Vp8Residual();
Vp8Residual residual = new();
bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
it.NzToBytes();
@ -1096,6 +1128,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// is around q=75. Internally, our "good" middle is around c=50. So we
/// map accordingly using linear piece-wise function
/// </summary>
/// <param name="c">The compression level.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static double QualityToCompression(double c)
{
@ -1107,13 +1140,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// this power-law: quant ~= compression ^ 1/3. This law holds well for
// low quant. Finer modeling for high-quant would make use of AcTable[]
// more explicitly.
double v = Math.Pow(linearC, 1 / 3.0d);
return v;
return (double)Math.Pow(linearC, 1 / 3.0d);
}
[MethodImpl(InliningOptions.ShortMethod)]
private int FilterStrengthFromDelta(int sharpness, int delta)
private static int FilterStrengthFromDelta(int sharpness, int delta)
{
int pos = delta < WebpConstants.MaxDelzaSize ? delta : WebpConstants.MaxDelzaSize - 1;
return WebpLookupTables.LevelsFromDelta[sharpness, pos];

226
src/ImageSharp/Formats/Webp/Lossy/WebpLossyDecoder.cs

@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Paragraph 9.2: color space and clamp type follow.
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1);
sbyte clampType = (sbyte)this.bitReader.ReadValue(1);
var pictureHeader = new Vp8PictureHeader()
Vp8PictureHeader pictureHeader = new()
{
Width = (uint)width,
Height = (uint)height,
@ -83,10 +83,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
};
// Paragraph 9.3: Parse the segment header.
var proba = new Vp8Proba();
Vp8Proba proba = new();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
using (var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba, this.memoryAllocator))
using (Vp8Decoder decoder = new(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba, this.memoryAllocator))
{
Vp8Io io = InitializeVp8Io(decoder, pictureHeader);
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (info.Features?.Alpha == true)
{
using (var alphaDecoder = new AlphaDecoder(
using (AlphaDecoder alphaDecoder = new(
width,
height,
alphaData,
@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.configuration))
{
alphaDecoder.Decode();
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha);
DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha);
}
}
else
@ -146,7 +146,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> decodedPixels, IMemoryOwner<byte> alpha)
private static void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> decodedPixels, IMemoryOwner<byte> alpha)
where TPixel : unmanaged, IPixel<TPixel>
{
TPixel color = default;
@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
// Prepare for next scanline.
this.InitScanline(dec);
InitScanline(dec);
// Reconstruct, filter and emit the row.
this.ProcessRow(dec, io);
@ -222,9 +222,27 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (!block.IsI4x4)
{
// Hardcoded 16x16 intra-mode decision tree.
int yMode = this.bitReader.GetBit(156) != 0 ?
this.bitReader.GetBit(128) != 0 ? (int)IntraPredictionMode.TrueMotion : (int)IntraPredictionMode.HPrediction :
this.bitReader.GetBit(163) != 0 ? (int)IntraPredictionMode.VPrediction : (int)IntraPredictionMode.DcPrediction;
int yMode;
if (this.bitReader.GetBit(156) != 0)
{
if (this.bitReader.GetBit(128) != 0)
{
yMode = (int)IntraPredictionMode.TrueMotion;
}
else
{
yMode = (int)IntraPredictionMode.HPrediction;
}
}
else if (this.bitReader.GetBit(163) != 0)
{
yMode = (int)IntraPredictionMode.VPrediction;
}
else
{
yMode = (int)IntraPredictionMode.DcPrediction;
}
block.Modes[0] = (byte)yMode;
for (int i = 0; i < left.Length; i++)
{
@ -258,12 +276,29 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
// Hardcoded UVMode decision tree.
block.UvMode = (byte)(this.bitReader.GetBit(142) == 0 ? 0 :
this.bitReader.GetBit(114) == 0 ? 2 :
this.bitReader.GetBit(183) != 0 ? 1 : 3);
if (this.bitReader.GetBit(142) == 0)
{
// Hardcoded UVMode decision tree.
block.UvMode = 0;
}
else if (this.bitReader.GetBit(114) == 0)
{
// Hardcoded UVMode decision tree.
block.UvMode = 2;
}
else if (this.bitReader.GetBit(183) != 0)
{
// Hardcoded UVMode decision tree.
block.UvMode = 1;
}
else
{
// Hardcoded UVMode decision tree.
block.UvMode = 3;
}
}
private void InitScanline(Vp8Decoder dec)
private static void InitScanline(Vp8Decoder dec)
{
Vp8MacroBlock left = dec.LeftMacroBlock;
left.NoneZeroAcDcCoeffs = 0;
@ -279,7 +314,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void ProcessRow(Vp8Decoder dec, Vp8Io io)
{
this.ReconstructRow(dec);
this.FinishRow(dec, io);
FinishRow(dec, io);
}
private void ReconstructRow(Vp8Decoder dec)
@ -404,8 +439,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
int offset = yOff + WebpConstants.Scan[n];
Span<byte> dst = yuv[offset..];
byte lumaMode = block.Modes[n];
switch (lumaMode)
switch (block.Modes[n])
{
case 0:
LossyUtils.DC4(dst, yuv, offset);
@ -439,14 +473,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
break;
}
this.DoTransform(bits, coeffs.AsSpan(n * 16), dst, this.scratch);
DoTransform(bits, coeffs.AsSpan(n * 16), dst, this.scratch);
}
}
else
{
// 16x16
int mode = CheckMode(mbx, mby, block.Modes[0]);
switch (mode)
switch (CheckMode(mbx, mby, block.Modes[0]))
{
case 0:
LossyUtils.DC16(yDst, yuv, yOff);
@ -475,15 +508,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
for (int n = 0; n < 16; ++n, bits <<= 2)
{
this.DoTransform(bits, coeffs.AsSpan(n * 16), yDst[WebpConstants.Scan[n]..], this.scratch);
DoTransform(bits, coeffs.AsSpan(n * 16), yDst[WebpConstants.Scan[n]..], this.scratch);
}
}
}
// Chroma
uint bitsUv = block.NonZeroUv;
int chromaMode = CheckMode(mbx, mby, block.UvMode);
switch (chromaMode)
switch (CheckMode(mbx, mby, block.UvMode))
{
case 0:
LossyUtils.DC8uv(uDst, yuv, uOff);
@ -515,8 +547,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
break;
}
this.DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, this.scratch);
this.DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, this.scratch);
DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, this.scratch);
DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, this.scratch);
// Stash away top samples for next block.
if (mby < dec.MbHeight - 1)
@ -544,16 +576,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private void FilterRow(Vp8Decoder dec)
private static void FilterRow(Vp8Decoder dec)
{
int mby = dec.MbY;
for (int mbx = dec.TopLeftMbX; mbx < dec.BottomRightMbX; ++mbx)
{
this.DoFilter(dec, mbx, mby);
DoFilter(dec, mbx, mby);
}
}
private void DoFilter(Vp8Decoder dec, int mbx, int mby)
private static void DoFilter(Vp8Decoder dec, int mbx, int mby)
{
int yBps = dec.CacheYStride;
Vp8FilterInfo filterInfo = dec.FilterInfo[mbx];
@ -620,7 +652,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private void FinishRow(Vp8Decoder dec, Vp8Io io)
private static void FinishRow(Vp8Decoder dec, Vp8Io io)
{
int extraYRows = WebpConstants.FilterExtraRows[(int)dec.Filter];
int ySize = extraYRows * dec.CacheYStride;
@ -635,7 +667,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (filterRow)
{
this.FilterRow(dec);
FilterRow(dec);
}
int yStart = mby * 16;
@ -669,7 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
io.MbY = yStart;
io.MbW = io.Width;
io.MbH = yEnd - yStart;
this.EmitRgb(dec, io);
EmitRgb(dec, io);
}
// Rotate top samples if needed.
@ -681,7 +713,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private int EmitRgb(Vp8Decoder dec, Vp8Io io)
private static int EmitRgb(Vp8Decoder dec, Vp8Io io)
{
Span<byte> buf = dec.Pixels.Memory.Span;
int numLinesOut = io.MbH; // a priori guess.
@ -693,7 +725,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> tmpVBuffer = dec.TmpVBuffer.Memory.Span;
Span<byte> topU = tmpUBuffer;
Span<byte> topV = tmpVBuffer;
int bpp = 3;
const int bpp = 3;
int bufferStride = bpp * io.Width;
int dstStartIdx = io.MbY * bufferStride;
Span<byte> dst = buf[dstStartIdx..];
@ -753,7 +785,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return numLinesOut;
}
private void DoTransform(uint bits, Span<short> src, Span<byte> dst, Span<int> scratch)
private static void DoTransform(uint bits, Span<short> src, Span<byte> dst, Span<int> scratch)
{
switch (bits >> 30)
{
@ -769,7 +801,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
private void DoUVTransform(uint bits, Span<short> src, Span<byte> dst, Span<int> scratch)
private static void DoUVTransform(uint bits, Span<short> src, Span<byte> dst, Span<int> scratch)
{
// any non-zero coeff at all?
if ((bits & 0xff) > 0)
@ -845,7 +877,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Parse DC
short[] dc = new short[16];
int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs);
int nz = this.GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc);
int nz = GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc);
mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0);
if (nz > 1)
{
@ -876,7 +908,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int x = 0; x < 4; x++)
{
int ctx = l + (tnz & 1);
int nz = this.GetCoeffs(br, acProba, ctx, q.Y1Mat, first, dst.AsSpan(dstOffset));
int nz = GetCoeffs(br, acProba, ctx, q.Y1Mat, first, dst.AsSpan(dstOffset));
l = nz > first ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 7));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
@ -903,7 +935,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int x = 0; x < 2; x++)
{
int ctx = l + (tnz & 1);
int nz = this.GetCoeffs(br, bands[2], ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
int nz = GetCoeffs(br, bands[2], ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
l = nz > 0 ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 3));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
@ -929,7 +961,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return (nonZeroY | nonZeroUv) == 0;
}
private int GetCoeffs(Vp8BitReader br, Vp8BandProbas[] prob, int ctx, int[] dq, int n, Span<short> coeffs)
private static int GetCoeffs(Vp8BitReader br, Vp8BandProbas[] prob, int ctx, int[] dq, int n, Span<short> coeffs)
{
// Returns the position of the last non-zero coeff plus one.
Vp8ProbaArray p = prob[n].Probabilities[ctx];
@ -960,7 +992,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
else
{
v = this.GetLargeValue(br, p.Probabilities);
v = GetLargeValue(br, p.Probabilities);
p = prob[n + 1].Probabilities[2];
}
@ -971,7 +1003,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return 16;
}
private int GetLargeValue(Vp8BitReader br, byte[] p)
private static int GetLargeValue(Vp8BitReader br, byte[] p)
{
// See section 13 - 2: http://tools.ietf.org/html/rfc6386#section-13.2
int v;
@ -986,53 +1018,50 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
v = 3 + br.GetBit(p[5]);
}
}
else
else if (br.GetBit(p[6]) == 0)
{
if (br.GetBit(p[6]) == 0)
if (br.GetBit(p[7]) == 0)
{
if (br.GetBit(p[7]) == 0)
{
v = 5 + br.GetBit(159);
}
else
{
v = 7 + (2 * br.GetBit(165));
v += br.GetBit(145);
}
v = 5 + br.GetBit(159);
}
else
{
int bit1 = br.GetBit(p[8]);
int bit0 = br.GetBit(p[9 + bit1]);
int cat = (2 * bit1) + bit0;
v = 0;
byte[] tab = null;
switch (cat)
{
case 0:
tab = WebpConstants.Cat3;
break;
case 1:
tab = WebpConstants.Cat4;
break;
case 2:
tab = WebpConstants.Cat5;
break;
case 3:
tab = WebpConstants.Cat6;
break;
default:
WebpThrowHelper.ThrowImageFormatException("VP8 parsing error");
break;
}
for (int i = 0; i < tab.Length; i++)
{
v += v + br.GetBit(tab[i]);
}
v = 7 + (2 * br.GetBit(165));
v += br.GetBit(145);
}
}
else
{
int bit1 = br.GetBit(p[8]);
int bit0 = br.GetBit(p[9 + bit1]);
int cat = (2 * bit1) + bit0;
v = 0;
byte[] tab = null;
switch (cat)
{
case 0:
tab = WebpConstants.Cat3;
break;
case 1:
tab = WebpConstants.Cat4;
break;
case 2:
tab = WebpConstants.Cat5;
break;
case 3:
tab = WebpConstants.Cat6;
break;
default:
WebpThrowHelper.ThrowImageFormatException("VP8 parsing error");
break;
}
v += 3 + (8 << cat);
for (int i = 0; i < tab.Length; i++)
{
v += v + br.GetBit(tab[i]);
}
v += 3 + (8 << cat);
}
return v;
@ -1040,7 +1069,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba)
{
var vp8SegmentHeader = new Vp8SegmentHeader
Vp8SegmentHeader vp8SegmentHeader = new()
{
UseSegment = this.bitReader.ReadBool()
};
@ -1055,15 +1084,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
{
hasValue = this.bitReader.ReadBool();
byte quantizeValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0);
vp8SegmentHeader.Quantizer[i] = quantizeValue;
vp8SegmentHeader.Quantizer[i] = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0);
}
for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
{
hasValue = this.bitReader.ReadBool();
byte filterStrengthValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0);
vp8SegmentHeader.FilterStrength[i] = filterStrengthValue;
vp8SegmentHeader.FilterStrength[i] = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0);
}
if (vp8SegmentHeader.UpdateMap)
@ -1188,10 +1215,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
decoder.DeQuantMatrices[i] = decoder.DeQuantMatrices[0];
continue;
}
else
{
q = baseQ0;
}
q = baseQ0;
}
Vp8QuantMatrix m = decoder.DeQuantMatrices[i];
@ -1228,10 +1253,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int p = 0; p < WebpConstants.NumProbas; ++p)
{
byte prob = WebpLookupTables.CoeffsUpdateProba[t, b, c, p];
byte v = (byte)(this.bitReader.GetBit(prob) != 0
proba.Bands[t, b].Probabilities[c].Probabilities[p] = (byte)(this.bitReader.GetBit(prob) != 0
? this.bitReader.ReadValue(8)
: WebpLookupTables.DefaultCoeffsProba[t, b, c, p]);
proba.Bands[t, b].Probabilities[c].Probabilities[p] = v;
}
}
}
@ -1251,7 +1275,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static Vp8Io InitializeVp8Io(Vp8Decoder dec, Vp8PictureHeader pictureHeader)
{
var io = default(Vp8Io);
Vp8Io io = default;
io.Width = (int)pictureHeader.Width;
io.Height = (int)pictureHeader.Height;
io.UseScaling = false;
@ -1311,7 +1335,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz)
{
nzCoeffs <<= 2;
nzCoeffs |= (uint)(nz > 3 ? 3 : nz > 1 ? 2 : dcNz);
if (nz > 3)
{
nzCoeffs |= 3;
}
else if (nz > 1)
{
nzCoeffs |= 2;
}
else
{
nzCoeffs |= (uint)dcNz;
}
return nzCoeffs;
}
@ -1337,6 +1373,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int Clip(int value, int max) => value < 0 ? 0 : value > max ? max : value;
private static int Clip(int value, int max) => Math.Clamp(value, 0, max);
}
}

50
src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
WebpImageInfo webpInfo = null;
var features = new WebpFeatures();
WebpFeatures features = new();
switch (chunkType)
{
case WebpChunkType.Vp8:
@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
image = new Image<TPixel>(this.configuration, (int)width, (int)height, backgroundColor.ToPixel<TPixel>(), this.metadata);
this.SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData.Duration);
SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData.Duration);
imageFrame = image.Frames.RootFrame;
}
@ -177,7 +177,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
this.SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
imageFrame = currentFrame;
}
@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
int frameY = (int)(frameData.Y * 2);
int frameWidth = (int)frameData.Width;
int frameHeight = (int)frameData.Height;
var regionRectangle = Rectangle.FromLTRB(frameX, frameY, frameX + frameWidth, frameY + frameHeight);
Rectangle regionRectangle = Rectangle.FromLTRB(frameX, frameY, frameX + frameWidth, frameY + frameHeight);
if (frameData.DisposalMethod is AnimationDisposalMethod.Dispose)
{
@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
using Buffer2D<TPixel> decodedImage = this.DecodeImageData<TPixel>(frameData, webpInfo);
this.DrawDecodedImageOnCanvas(decodedImage, imageFrame, frameX, frameY, frameWidth, frameHeight);
DrawDecodedImageOnCanvas(decodedImage, imageFrame, frameX, frameY, frameWidth, frameHeight);
if (previousFrame != null && frameData.BlendingMethod is AnimationBlendingMethod.AlphaBlending)
{
@ -213,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <param name="meta">The metadata.</param>
/// <param name="duration">The frame duration.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
private static void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
{
WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
frameMetadata.FrameDuration = duration;
@ -248,19 +248,19 @@ namespace SixLabors.ImageSharp.Formats.Webp
private Buffer2D<TPixel> DecodeImageData<TPixel>(AnimationFrameData frameData, WebpImageInfo webpInfo)
where TPixel : unmanaged, IPixel<TPixel>
{
var decodedImage = new Image<TPixel>((int)frameData.Width, (int)frameData.Height);
Image<TPixel> decodedImage = new((int)frameData.Width, (int)frameData.Height);
try
{
Buffer2D<TPixel> pixelBufferDecoded = decodedImage.Frames.RootFrame.PixelBuffer;
if (webpInfo.IsLossless)
{
var losslessDecoder = new WebpLosslessDecoder(webpInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
WebpLosslessDecoder losslessDecoder = new(webpInfo.Vp8LBitReader, this.memoryAllocator, this.configuration);
losslessDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height);
}
else
{
var lossyDecoder = new WebpLossyDecoder(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
WebpLossyDecoder lossyDecoder = new(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration);
lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.alphaData);
}
@ -278,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
/// <summary>
/// Draws the decoded image on canvas. The decoded image can be smaller the the canvas.
/// Draws the decoded image on canvas. The decoded image can be smaller the canvas.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="decodedImage">The decoded image.</param>
@ -287,7 +287,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <param name="frameY">The frame y coordinate.</param>
/// <param name="frameWidth">The width of the frame.</param>
/// <param name="frameHeight">The height of the frame.</param>
private void DrawDecodedImageOnCanvas<TPixel>(Buffer2D<TPixel> decodedImage, ImageFrame<TPixel> imageFrame, int frameX, int frameY, int frameWidth, int frameHeight)
private static void DrawDecodedImageOnCanvas<TPixel>(Buffer2D<TPixel> decodedImage, ImageFrame<TPixel> imageFrame, int frameX, int frameY, int frameWidth, int frameHeight)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> imageFramePixels = imageFrame.PixelBuffer;
@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
return;
}
var interest = Rectangle.Intersect(imageFrame.Bounds(), this.restoreArea.Value);
Rectangle interest = Rectangle.Intersect(imageFrame.Bounds(), this.restoreArea.Value);
Buffer2DRegion<TPixel> pixelRegion = imageFrame.PixelBuffer.GetRegion(interest);
TPixel backgroundPixel = backgroundColor.ToPixel<TPixel>();
pixelRegion.Fill(backgroundPixel);
@ -354,25 +354,25 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <returns>Animation frame data.</returns>
private AnimationFrameData ReadFrameHeader(BufferedReadStream stream)
{
var data = new AnimationFrameData
AnimationFrameData data = new()
{
DataSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer)
};
DataSize = WebpChunkParsingUtils.ReadChunkSize(stream, this.buffer),
// 3 bytes for the X coordinate of the upper left corner of the frame.
data.X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer);
// 3 bytes for the X coordinate of the upper left corner of the frame.
X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer),
// 3 bytes for the Y coordinate of the upper left corner of the frame.
data.Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer);
// 3 bytes for the Y coordinate of the upper left corner of the frame.
Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer),
// Frame width Minus One.
data.Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1;
// Frame width Minus One.
Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1,
// Frame height Minus One.
data.Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1;
// Frame height Minus One.
Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1,
// Frame duration.
data.Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer);
// Frame duration.
Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer)
};
byte flags = (byte)stream.ReadByte();
data.DisposalMethod = (flags & 1) == 1 ? AnimationDisposalMethod.Dispose : AnimationDisposalMethod.DoNotDispose;

12
src/ImageSharp/Formats/Webp/WebpImageFormatDetector.cs

@ -14,22 +14,26 @@ namespace SixLabors.ImageSharp.Formats.Webp
public int HeaderSize => 12;
/// <inheritdoc />
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? WebpFormat.Instance : null;
public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
=> this.IsSupportedFileFormat(header) ? WebpFormat.Instance : null;
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) => header.Length >= this.HeaderSize && this.IsRiffContainer(header) && this.IsWebpFile(header);
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
=> header.Length >= this.HeaderSize && IsRiffContainer(header) && IsWebpFile(header);
/// <summary>
/// Checks, if the header starts with a valid RIFF FourCC.
/// </summary>
/// <param name="header">The header bytes.</param>
/// <returns>True, if its a valid RIFF FourCC.</returns>
private bool IsRiffContainer(ReadOnlySpan<byte> header) => header[..4].SequenceEqual(WebpConstants.RiffFourCc);
private static bool IsRiffContainer(ReadOnlySpan<byte> header)
=> header[..4].SequenceEqual(WebpConstants.RiffFourCc);
/// <summary>
/// Checks if 'WEBP' is present in the header.
/// </summary>
/// <param name="header">The header bytes.</param>
/// <returns>True, if its a webp file.</returns>
private bool IsWebpFile(ReadOnlySpan<byte> header) => header.Slice(8, 4).SequenceEqual(WebpConstants.WebpHeader);
private static bool IsWebpFile(ReadOnlySpan<byte> header)
=> header.Slice(8, 4).SequenceEqual(WebpConstants.WebpHeader);
}
}

8
src/ImageSharp/Formats/Webp/WebpImageInfo.cs

@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Formats.Webp
public Vp8FrameHeader Vp8FrameHeader { get; set; }
/// <summary>
/// Gets or sets the VP8L bitreader. Will be null, if its not a lossless image.
/// Gets or sets the VP8L bitreader. Will be <see langword="null"/>, if its not a lossless image.
/// </summary>
public Vp8LBitReader Vp8LBitReader { get; set; } = null;
public Vp8LBitReader Vp8LBitReader { get; set; }
/// <summary>
/// Gets or sets the VP8 bitreader. Will be null, if its not a lossy image.
/// Gets or sets the VP8 bitreader. Will be <see langword="null"/>, if its not a lossy image.
/// </summary>
public Vp8BitReader Vp8BitReader { get; set; } = null;
public Vp8BitReader Vp8BitReader { get; set; }
/// <inheritdoc/>
public void Dispose()

2
src/ImageSharp/IO/BufferedReadStream.cs

@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.IO
public override bool CanSeek { get; } = true;
/// <inheritdoc/>
public override bool CanWrite { get; } = false;
public override bool CanWrite { get; }
/// <summary>
/// Gets remaining byte count available to read.

4
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.IO
this.isDisposed = true;
if (disposing)
{
this.ReleaseMemoryChunks(this.memoryChunk);
ReleaseMemoryChunks(this.memoryChunk);
}
this.memoryChunk = null;
@ -530,7 +530,7 @@ namespace SixLabors.ImageSharp.IO
};
}
private void ReleaseMemoryChunks(MemoryChunk chunk)
private static void ReleaseMemoryChunks(MemoryChunk chunk)
{
while (chunk != null)
{

17
src/ImageSharp/Image.FromStream.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
@ -546,7 +547,7 @@ namespace SixLabors.ImageSharp
}
// We want to be able to load images from things like HttpContext.Request.Body
using var memoryStream = new ChunkedMemoryStream(configuration.MemoryAllocator);
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
memoryStream.Position = 0;
@ -562,6 +563,7 @@ namespace SixLabors.ImageSharp
/// <param name="action">The action to perform.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The <see cref="Task{T}"/>.</returns>
/// <exception cref="NotSupportedException">Cannot read from the stream.</exception>
internal static async Task<T> WithSeekableStreamAsync<T>(
DecoderOptions options,
Stream stream,
@ -577,19 +579,16 @@ namespace SixLabors.ImageSharp
}
Configuration configuration = options.Configuration;
if (stream.CanSeek)
if (stream.CanSeek && configuration.ReadOrigin == ReadOrigin.Begin)
{
if (configuration.ReadOrigin == ReadOrigin.Begin)
{
stream.Position = 0;
}
stream.Position = 0;
// NOTE: We are explicitly not executing the action against the stream here as we do in WithSeekableStream() because that
// would incur synchronous IO reads which must be avoided in this asynchronous method. Instead, we will *always* run the
// code below to copy the stream to an in-memory buffer before invoking the action.
}
using var memoryStream = new ChunkedMemoryStream(configuration.MemoryAllocator);
using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator);
await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
@ -599,12 +598,12 @@ namespace SixLabors.ImageSharp
[DoesNotReturn]
private static void ThrowNotLoaded(DecoderOptions options)
{
var sb = new StringBuilder();
StringBuilder sb = new();
sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in options.Configuration.ImageFormatsManager.ImageDecoders)
{
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new UnknownImageFormatException(sb.ToString());

16
src/ImageSharp/Image.WrapMemory.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(metadata, nameof(metadata));
Guard.IsTrue(pixelMemory.Length >= width * height, nameof(pixelMemory), "The length of the input memory is less than the specified image size");
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemory);
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(pixelMemory);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}
@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(metadata, nameof(metadata));
Guard.IsTrue(pixelMemoryOwner.Memory.Length >= width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size");
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}
@ -231,11 +231,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
var memoryManager = new ByteMemoryManager<TPixel>(byteMemory);
ByteMemoryManager<TPixel> memoryManager = new(byteMemory);
Guard.IsTrue(memoryManager.Memory.Length >= width * height, nameof(byteMemory), "The length of the input memory is less than the specified image size");
var memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}
@ -329,11 +329,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
var pixelMemoryOwner = new ByteMemoryOwner<TPixel>(byteMemoryOwner);
ByteMemoryOwner<TPixel> pixelMemoryOwner = new(byteMemoryOwner);
Guard.IsTrue(pixelMemoryOwner.Memory.Length >= (long)width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size");
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}
@ -422,9 +422,9 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
var memoryManager = new UnmanagedMemoryManager<TPixel>(pointer, width * height);
UnmanagedMemoryManager<TPixel> memoryManager = new(pointer, width * height);
var memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}

11
src/ImageSharp/ImageExtensions.cs

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
@ -106,12 +107,12 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
var sb = new StringBuilder();
StringBuilder sb = new();
sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@ -150,12 +151,12 @@ namespace SixLabors.ImageSharp
if (encoder is null)
{
var sb = new StringBuilder();
StringBuilder sb = new();
sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
sb.AppendFormat(CultureInfo.InvariantCulture, " - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
}
throw new NotSupportedException(sb.ToString());
@ -182,7 +183,7 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(format, nameof(format));
using var stream = new MemoryStream();
using MemoryStream stream = new();
source.Save(stream, format);
// Always available.

15
src/ImageSharp/ImageSharp.csproj.DotSettings

@ -1,15 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=color/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=memory_005Callocators/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=memory_005Cdiscontiguousbuffers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cgenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpackedpixels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixelimplementations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixeltypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cutils/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cextensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Cconvolution_005Ckernels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresamplers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresize/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

5
src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject

@ -1,5 +0,0 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

27
src/ImageSharp/Memory/Allocators/Internals/UnmanagedMemoryHandle.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
// A Monitor to wait/signal when we are low on memory.
private static object lowMemoryMonitor;
public static readonly UnmanagedMemoryHandle NullHandle = default;
public static readonly UnmanagedMemoryHandle NullHandle;
private IntPtr handle;
private int lengthInBytes;
@ -80,24 +80,17 @@ namespace SixLabors.ImageSharp.Memory.Internals
{
handle = Marshal.AllocHGlobal(lengthInBytes);
}
catch (OutOfMemoryException)
catch (OutOfMemoryException) when (counter < MaxAllocationAttempts)
{
// We are low on memory, but expect some memory to be freed soon.
// Block the thread & retry to avoid OOM.
if (counter < MaxAllocationAttempts)
{
counter++;
Interlocked.Increment(ref totalOomRetries);
Interlocked.CompareExchange(ref lowMemoryMonitor, new object(), null);
Monitor.Enter(lowMemoryMonitor);
Monitor.Wait(lowMemoryMonitor, millisecondsTimeout: 1);
Monitor.Exit(lowMemoryMonitor);
}
else
{
throw;
}
counter++;
Interlocked.Increment(ref totalOomRetries);
Interlocked.CompareExchange(ref lowMemoryMonitor, new object(), null);
Monitor.Enter(lowMemoryMonitor);
Monitor.Wait(lowMemoryMonitor, millisecondsTimeout: 1);
Monitor.Exit(lowMemoryMonitor);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save