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/) # Version: 4.1.1 (Using https://semver.org/)
# Updated: 2021-03-03 # Updated: 2022-05-23
# See https://github.com/RehanSaeed/EditorConfig/releases for release notes. # See https://github.com/RehanSaeed/EditorConfig/releases for release notes.
# See https://github.com/RehanSaeed/EditorConfig for updates to this file. # See https://github.com/RehanSaeed/EditorConfig for updates to this file.
# See http://EditorConfig.org for more information about .editorconfig files. # See http://EditorConfig.org for more information about .editorconfig files.
@ -49,11 +49,11 @@ indent_size = 2
indent_size = 2 indent_size = 2
# Markdown Files # Markdown Files
[*.md] [*.{md,mdx}]
trim_trailing_whitespace = false trim_trailing_whitespace = false
# Web Files # 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 indent_size = 2
# Batch Files # Batch Files
@ -75,7 +75,7 @@ indent_style = tab
[*.{cs,csx,cake,vb,vbx}] [*.{cs,csx,cake,vb,vbx}]
# Default Severity for all .NET Code Style rules below # Default Severity for all .NET Code Style rules below
dotnet_analyzer_diagnostic.category-style.severity = warning dotnet_analyzer_diagnostic.severity = warning
########################################## ##########################################
# Language Rules # Language Rules
@ -128,14 +128,15 @@ file_header_template = Copyright (c) Six Labors.\nLicensed under the Six Labors
# dotnet_diagnostic.SA1636.severity = none # dotnet_diagnostic.SA1636.severity = none
# Undocumented # 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 # C# Style Rules
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules # https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules
[*.{cs,csx,cake}] [*.{cs,csx,cake}]
# 'var' preferences # 'var' preferences
csharp_style_var_for_built_in_types = never csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_when_type_is_apparent = false:warning
csharp_style_var_elsewhere = false:warning csharp_style_var_elsewhere = false:warning
# Expression-bodied members # Expression-bodied members
csharp_style_expression_bodied_methods = true:warning csharp_style_expression_bodied_methods = true:warning
@ -200,12 +201,15 @@ dotnet_diagnostic.IDE0059.severity = suggestion
# Organize using directives # Organize using directives
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false dotnet_separate_import_directive_groups = false
# Dotnet namespace options
dotnet_style_namespace_match_folder = true:suggestion
dotnet_diagnostic.IDE0130.severity = suggestion
# C# formatting rules # C# formatting rules
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules # https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules
[*.{cs,csx,cake}] [*.{cs,csx,cake}]
# Newline options # 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_open_brace = all
csharp_new_line_before_else = true csharp_new_line_before_else = true
csharp_new_line_before_catch = 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_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true csharp_new_line_between_query_expression_clauses = true
# Indentation options # 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_case_contents = true
csharp_indent_switch_labels = true csharp_indent_switch_labels = true
csharp_indent_labels = no_change csharp_indent_labels = no_change
@ -222,7 +226,7 @@ csharp_indent_block_contents = true
csharp_indent_braces = false csharp_indent_braces = false
csharp_indent_case_contents_when_block = false csharp_indent_case_contents_when_block = false
# Spacing options # 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_cast = false
csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_parentheses = false 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_empty_square_brackets = false
csharp_space_between_square_brackets = false csharp_space_between_square_brackets = false
# Wrap options # 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_statements = false
csharp_preserve_single_line_blocks = true 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 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE. # 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -34,11 +35,11 @@ namespace SixLabors.ImageSharp.Advanced
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format is null) if (format is null)
{ {
var sb = new StringBuilder(); StringBuilder sb = new();
sb.AppendLine($"No encoder was found for extension '{ext}'. Registered encoders include:"); sb.AppendLine(CultureInfo.InvariantCulture, $"No encoder was found for extension '{ext}'. Registered encoders include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) 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()); throw new NotSupportedException(sb.ToString());
@ -48,11 +49,11 @@ namespace SixLabors.ImageSharp.Advanced
if (encoder is null) if (encoder is null)
{ {
var sb = new StringBuilder(); StringBuilder sb = new();
sb.AppendLine($"No encoder was found for extension '{ext}' using image format '{format.Name}'. Registered encoders include:"); 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) 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()); 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, /// 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}"/>. /// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks> /// </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) public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this ImageFrame<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> source?.PixelBuffer.FastMemoryGroup.View ?? throw new ArgumentNullException(nameof(source)); => 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, /// 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}"/>. /// therefore it's not recommended to mutate the image while holding a reference to it's <see cref="IMemoryGroup{T}"/>.
/// </remarks> /// </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) public static IMemoryGroup<TPixel> GetPixelMemoryGroup<TPixel>(this Image<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
=> source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source)); => source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException(nameof(source));
/// <summary> /// <summary>
/// Gets the representation of the pixels as a <see cref="Span{T}"/> of contiguous memory /// 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> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>
@ -154,8 +157,8 @@ namespace SixLabors.ImageSharp.Advanced
} }
/// <summary> /// <summary>
/// Gets the representation of the pixels as <see cref="Span{T}"/> of of contiguous memory /// Gets the representation of the pixels as <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> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param> /// <param name="source">The source.</param>

5
src/ImageSharp/Advanced/PreserveAttribute.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Advanced namespace SixLabors.ImageSharp.Advanced
{ {
/// <summary> /// <summary>
@ -8,7 +10,8 @@ namespace SixLabors.ImageSharp.Advanced
/// The only thing that matters is the class name. /// The only thing that matters is the class name.
/// There is no need to use or inherit from the PreserveAttribute class in each environment. /// There is no need to use or inherit from the PreserveAttribute class in each environment.
/// </summary> /// </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)] [MethodImpl(InliningOptions.ShortMethod)]
public static Color ParseHex(string hex) public static Color ParseHex(string hex)
{ {
var rgba = Rgba32.ParseHex(hex); Rgba32 rgba = Rgba32.ParseHex(hex);
return new Color(rgba); return new Color(rgba);
} }
@ -193,6 +193,7 @@ namespace SixLabors.ImageSharp
/// <returns> /// <returns>
/// The <see cref="Color"/>. /// The <see cref="Color"/>.
/// </returns> /// </returns>
/// <exception cref="ArgumentException">Input string is not in the correct format.</exception>
public static Color Parse(string input) public static Color Parse(string input)
{ {
Guard.NotNull(input, nameof(input)); Guard.NotNull(input, nameof(input));
@ -241,7 +242,7 @@ namespace SixLabors.ImageSharp
/// <returns>The color having it's alpha channel altered.</returns> /// <returns>The color having it's alpha channel altered.</returns>
public Color WithAlpha(float alpha) public Color WithAlpha(float alpha)
{ {
var v = (Vector4)this; Vector4 v = (Vector4)this;
v.W = alpha; v.W = alpha;
return new Color(v); 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. /// Bulk converts a span of <see cref="Color"/> to a span of a specified <typeparamref name="TPixel"/> type.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type to convert to.</typeparam> /// <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="source">The source color span.</param>
/// <param name="destination">The destination pixel span.</param> /// <param name="destination">The destination pixel span.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void ToPixel<TPixel>( public static void ToPixel<TPixel>(ReadOnlySpan<Color> source, Span<TPixel> destination)
Configuration configuration,
ReadOnlySpan<Color> source,
Span<TPixel> destination)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -19,29 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; 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> /// <summary>
/// Initializes a new instance of the <see cref="CieLab"/> struct. /// Initializes a new instance of the <see cref="CieLab"/> struct.
/// </summary> /// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint; 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> /// <summary>
/// Compares two <see cref="CieLab"/> objects for equality. /// Compares two <see cref="CieLab"/> objects for equality.
/// </summary> /// </summary>
@ -128,12 +128,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLab other) public bool Equals(CieLab other) =>
{ this.L.Equals(other.L)
return this.L.Equals(other.L) && this.A.Equals(other.A)
&& this.A.Equals(other.A) && this.B.Equals(other.B)
&& this.B.Equals(other.B) && this.WhitePoint.Equals(other.WhitePoint);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -19,31 +19,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; public static readonly CieXyz DefaultWhitePoint = Illuminants.D50;
private static readonly Vector3 Min = new Vector3(0, -200, 0); private static readonly Vector3 Min = new(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360); private static readonly Vector3 Max = new(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;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CieLch"/> struct. /// Initializes a new instance of the <see cref="CieLch"/> struct.
@ -97,6 +74,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint; 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> /// <summary>
/// Compares two <see cref="CieLch"/> objects for equality. /// Compares two <see cref="CieLch"/> objects for equality.
/// </summary> /// </summary>
@ -121,9 +121,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
public override int GetHashCode() public override int GetHashCode()
{ => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
return HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
}
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); 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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLch other) public bool Equals(CieLch other)
{ => this.L.Equals(other.L)
return this.L.Equals(other.L) && this.C.Equals(other.C)
&& this.C.Equals(other.C) && this.H.Equals(other.H)
&& this.H.Equals(other.H) && this.WhitePoint.Equals(other.WhitePoint);
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <summary> /// <summary>
/// Computes the saturation of the color (chroma normalized by lightness) /// 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -13,8 +13,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public readonly struct CieLchuv : IEquatable<CieLchuv> public readonly struct CieLchuv : IEquatable<CieLchuv>
{ {
private static readonly Vector3 Min = new Vector3(0, -200, 0); private static readonly Vector3 Min = new(0, -200, 0);
private static readonly Vector3 Max = new Vector3(100, 200, 360); private static readonly Vector3 Max = new(100, 200, 360);
/// <summary> /// <summary>
/// D50 standard illuminant. /// D50 standard illuminant.
@ -22,29 +22,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; 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> /// <summary>
/// Initializes a new instance of the <see cref="CieLchuv"/> struct. /// Initializes a new instance of the <see cref="CieLchuv"/> struct.
/// </summary> /// </summary>
@ -98,6 +75,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint; 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> /// <summary>
/// Compares two <see cref="CieLchuv"/> objects for equality. /// Compares two <see cref="CieLchuv"/> objects for equality.
/// </summary> /// </summary>
@ -130,12 +130,10 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLchuv other) public bool Equals(CieLchuv other)
{ => this.L.Equals(other.L)
return this.L.Equals(other.L) && this.C.Equals(other.C)
&& this.C.Equals(other.C) && this.H.Equals(other.H)
&& this.H.Equals(other.H) && this.WhitePoint.Equals(other.WhitePoint);
&& this.WhitePoint.Equals(other.WhitePoint);
}
/// <summary> /// <summary>
/// Computes the saturation of the color (chroma normalized by lightness) /// 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -21,29 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; 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> /// <summary>
/// Initializes a new instance of the <see cref="CieLuv"/> struct. /// Initializes a new instance of the <see cref="CieLuv"/> struct.
/// </summary> /// </summary>
@ -96,6 +73,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint; 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> /// <summary>
/// Compares two <see cref="CieLuv"/> objects for equality. /// Compares two <see cref="CieLuv"/> objects for equality.
/// </summary> /// </summary>
@ -130,11 +130,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieLuv other) public bool Equals(CieLuv other)
{ => this.L.Equals(other.L)
return this.L.Equals(other.L) && this.U.Equals(other.U)
&& this.U.Equals(other.U) && this.V.Equals(other.V)
&& this.V.Equals(other.V) && this.WhitePoint.Equals(other.WhitePoint);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -13,24 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public readonly struct CieXyy : IEquatable<CieXyy> 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> /// <summary>
/// Initializes a new instance of the <see cref="CieXyy"/> struct. /// Initializes a new instance of the <see cref="CieXyy"/> struct.
/// </summary> /// </summary>
@ -60,6 +42,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Yl = vector.Z; 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> /// <summary>
/// Compares two <see cref="CieXyy"/> objects for equality. /// Compares two <see cref="CieXyy"/> objects for equality.
/// </summary> /// </summary>
@ -94,10 +94,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyy other) public bool Equals(CieXyy other)
{ => this.X.Equals(other.X)
return this.X.Equals(other.X) && this.Y.Equals(other.Y)
&& this.Y.Equals(other.Y) && this.Yl.Equals(other.Yl);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -13,24 +13,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public readonly struct CieXyz : IEquatable<CieXyz> 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> /// <summary>
/// Initializes a new instance of the <see cref="CieXyz"/> struct. /// Initializes a new instance of the <see cref="CieXyz"/> struct.
/// </summary> /// </summary>
@ -56,6 +38,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Z = vector.Z; 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> /// <summary>
/// Compares two <see cref="CieXyz"/> objects for equality. /// Compares two <see cref="CieXyz"/> objects for equality.
/// </summary> /// </summary>
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <returns>The <see cref="Vector3"/>.</returns> /// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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/> /// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
@ -97,10 +97,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(CieXyz other) public bool Equals(CieXyz other)
{ => this.X.Equals(other.X)
return this.X.Equals(other.X) && this.Y.Equals(other.Y)
&& this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -15,30 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
private static readonly Vector4 Min = Vector4.Zero; private static readonly Vector4 Min = Vector4.Zero;
private static readonly Vector4 Max = Vector4.One; 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> /// <summary>
/// Initializes a new instance of the <see cref="Cmyk"/> struct. /// Initializes a new instance of the <see cref="Cmyk"/> struct.
/// </summary> /// </summary>
@ -66,6 +42,30 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.K = vector.W; 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> /// <summary>
/// Compares two <see cref="Cmyk"/> objects for equality. /// Compares two <see cref="Cmyk"/> objects for equality.
/// </summary> /// </summary>
@ -101,11 +101,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Cmyk other) public bool Equals(Cmyk other)
{ => this.C.Equals(other.C)
return this.C.Equals(other.C) && this.M.Equals(other.M)
&& this.M.Equals(other.M) && this.Y.Equals(other.Y)
&& this.Y.Equals(other.Y) && this.K.Equals(other.K);
&& this.K.Equals(other.K);
}
} }
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </content> /// </content>
public partial class ColorSpaceConverter public partial class ColorSpaceConverter
{ {
private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter();
/// <summary> /// <summary>
/// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>. /// Performs the bulk conversion from <see cref="CieLab"/> into <see cref="YCbCr"/>.
/// </summary> /// </summary>
@ -124,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination 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)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length; int count = source.Length;
@ -136,7 +134,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, 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> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination 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)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length; int count = source.Length;
@ -157,7 +155,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); ref Hsl sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, 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> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination 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)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length; int count = source.Length;
@ -178,7 +176,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); ref Hsv sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, 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> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination 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)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length; int count = source.Length;
@ -220,7 +218,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, 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> /// </summary>
/// <param name="source">The span to the source colors</param> /// <param name="source">The span to the source colors</param>
/// <param name="destination">The span to the destination 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)); Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
int count = source.Length; int count = source.Length;
@ -262,7 +260,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
{ {
ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); ref Rgb sp = ref Unsafe.Add(ref sourceRef, i);
ref YCbCr dp = ref Unsafe.Add(ref destRef, 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> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLab color) public YCbCr ToYCbCr(in CieLab color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLch color) public YCbCr ToYCbCr(in CieLch color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -297,7 +295,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieLuv color) public YCbCr ToYCbCr(in CieLuv color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -309,7 +307,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyy color) public YCbCr ToYCbCr(in CieXyy color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -321,7 +319,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in CieXyz color) public YCbCr ToYCbCr(in CieXyz color)
{ {
var rgb = this.ToRgb(color); Rgb rgb = this.ToRgb(color);
return YCbCrAndRgbConverter.Convert(rgb); return YCbCrAndRgbConverter.Convert(rgb);
} }
@ -331,9 +329,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <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); return YCbCrAndRgbConverter.Convert(rgb);
} }
@ -343,9 +341,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <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); return YCbCrAndRgbConverter.Convert(rgb);
} }
@ -355,9 +353,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <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); return YCbCrAndRgbConverter.Convert(rgb);
} }
@ -369,7 +367,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in HunterLab color) public YCbCr ToYCbCr(in HunterLab color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -379,9 +377,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <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); return YCbCrAndRgbConverter.Convert(rgb);
} }
@ -393,7 +391,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="YCbCr"/></returns> /// <returns>The <see cref="YCbCr"/></returns>
public YCbCr ToYCbCr(in Lms color) public YCbCr ToYCbCr(in Lms color)
{ {
var xyzColor = this.ToCieXyz(color); CieXyz xyzColor = this.ToCieXyz(color);
return this.ToYCbCr(xyzColor); return this.ToYCbCr(xyzColor);
} }
@ -403,6 +401,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// </summary> /// </summary>
/// <param name="color">The color to convert.</param> /// <param name="color">The color to convert.</param>
/// <returns>The <see cref="YCbCr"/></returns> /// <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> /// </summary>
public readonly struct CieXyChromaticityCoordinates : IEquatable<CieXyChromaticityCoordinates> 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> /// <summary>
/// Gets the chromaticity X-coordinate. /// Gets the chromaticity X-coordinate.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Ranges usually from 0 to 1. /// Ranges usually from 0 to 1.
/// </remarks> /// </remarks>
public readonly float X; public readonly float X { get; }
/// <summary> /// <summary>
/// Gets the chromaticity Y-coordinate /// Gets the chromaticity Y-coordinate
@ -26,19 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <remarks> /// <remarks>
/// Ranges usually from 0 to 1. /// Ranges usually from 0 to 1.
/// </remarks> /// </remarks>
public readonly float Y; public readonly float Y { get; }
/// <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> /// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for equality. /// 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. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(InliningOptions.ShortMethod)] [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> /// <summary>
/// Compares two <see cref="CieXyChromaticityCoordinates"/> objects for inequality /// 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. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
[MethodImpl(InliningOptions.ShortMethod)] [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 /> /// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y); public override int GetHashCode()
=> HashCode.Combine(this.X, this.Y);
/// <inheritdoc/> /// <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/> /// <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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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. // Licensed under the Six Labors Split License.
using System; using System;
@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary> /// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>. /// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary> /// </summary>
internal sealed class CieLchToCieLabConverter internal static class CieLchToCieLabConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLch"/> input to an instance of <see cref="CieLab"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLab Convert(in CieLch input) public static CieLab Convert(in CieLch input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // 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> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary> /// </summary>
internal sealed class CieLabToCieLchConverter internal static class CieLabToCieLchConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieLch"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLch Convert(in CieLab input) public static CieLch Convert(in CieLab input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC // 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> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieXyz"/>.
/// </summary> /// </summary>
internal sealed class CieLabToCieXyzConverter internal static class CieLabToCieXyzConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLab"/> input to an instance of <see cref="CieXyz"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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 // 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; 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 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; 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) // 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; Vector3 xyz = xyzr * wxyz;
return new CieXyz(xyz); return new CieXyz(xyz);

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

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary> /// <summary>
/// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>. /// Converts from <see cref="CieLch"/> to <see cref="CieLab"/>.
/// </summary> /// </summary>
internal sealed class CieLchuvToCieLuvConverter internal static class CieLchuvToCieLuvConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLchuv"/> input to an instance of <see cref="CieLuv"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLuv Convert(in CieLchuv input) public static CieLuv Convert(in CieLchuv input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // 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> /// <summary>
/// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>. /// Converts from <see cref="CieLab"/> to <see cref="CieLch"/>.
/// </summary> /// </summary>
internal sealed class CieLuvToCieLchuvConverter internal static class CieLuvToCieLchuvConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieLchuv"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieLchuv Convert(in CieLuv input) public static CieLchuv Convert(in CieLuv input)
{ {
// Conversion algorithm described here: // Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 // 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> /// <summary>
/// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>. /// Converts from <see cref="CieLuv"/> to <see cref="CieXyz"/>.
/// </summary> /// </summary>
internal sealed class CieLuvToCieXyzConverter internal static class CieLuvToCieXyzConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieXyz"/> type. /// Performs the conversion from the <see cref="CieLuv"/> input to an instance of <see cref="CieXyz"/> type.
/// </summary> /// </summary>
/// <param name="input">The input color instance.</param> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <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 // 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; 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// Color converter between CIE XYZ and CIE xyY. /// Color converter between CIE XYZ and CIE xyY.
/// <see href="http://www.brucelindbloom.com/"/> for formulas. /// <see href="http://www.brucelindbloom.com/"/> for formulas.
/// </summary> /// </summary>
internal sealed class CieXyzAndCieXyyConverter internal static class CieXyzAndCieXyyConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="CieXyz"/> input to an instance of <see cref="CieXyy"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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 x = input.X / (input.X + input.Y + input.Z);
float y = input.Y / (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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in CieXyy input) public static CieXyz Convert(in CieXyy input)
{ {
if (MathF.Abs(input.Y) < Constants.Epsilon) 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. // Licensed under the Six Labors Split License.
using System.Numerics; using System.Numerics;
@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Lms Convert(in CieXyz input) 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); return new Lms(vector);
} }
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CieXyz Convert(in Lms input) 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); 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary> /// <summary>
/// Color converter between <see cref="Cmyk"/> and <see cref="Rgb"/>. /// Color converter between <see cref="Cmyk"/> and <see cref="Rgb"/>.
/// </summary> /// </summary>
internal sealed class CmykAndRgbConverter internal static class CmykAndRgbConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="Cmyk"/> input to an instance of <see cref="Rgb"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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)); Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K));
return new Rgb(rgb); return new Rgb(rgb);
@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param> /// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns> /// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Cmyk Convert(in Rgb input) public static Cmyk Convert(in Rgb input)
{ {
// To CMY // To CMY
Vector3 cmy = Vector3.One - input.ToVector3(); Vector3 cmy = Vector3.One - input.ToVector3();
// To CMYK // 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) 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 /// Color converter between HSL and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas. /// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary> /// </summary>
internal sealed class HslAndRgbConverter internal static class HslAndRgbConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="Hsl"/> input to an instance of <see cref="Rgb"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsl input) public static Rgb Convert(in Hsl input)
{ {
float rangedH = input.H / 360F; float rangedH = input.H / 360F;
float r = 0; float r = 0;
@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsl Convert(in Rgb input) public static Hsl Convert(in Rgb input)
{ {
float r = input.R; float r = input.R;
float g = input.G; 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 /// Color converter between HSV and Rgb
/// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas. /// See <see href="http://www.poynton.com/PDFs/coloureq.pdf"/> for formulas.
/// </summary> /// </summary>
internal sealed class HsvAndRgbConverter internal static class HsvAndRgbConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="Hsv"/> input to an instance of <see cref="Rgb"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in Hsv input) public static Rgb Convert(in Hsv input)
{ {
float s = input.S; float s = input.S;
float v = input.V; float v = input.V;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Hsv Convert(in Rgb input) public static Hsv Convert(in Rgb input)
{ {
float r = input.R; float r = input.R;
float g = input.G; float g = input.G;

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

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary> /// <summary>
/// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/> /// Color converter between <see cref="HunterLab"/> and <see cref="CieXyz"/>
/// </summary> /// </summary>
internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="HunterLab"/> input to an instance of <see cref="CieXyz"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result</returns> /// <returns>The converted result</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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 // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab
float l = input.L, a = input.A, b = input.B; 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 yb = chromaticity.B.Y;
float mXr = xr / yr; float mXr = xr / yr;
const float Yr = 1;
float mZr = (1 - xr - yr) / yr; float mZr = (1 - xr - yr) / yr;
float mXg = xg / yg; float mXg = xg / yg;
const float Yg = 1;
float mZg = (1 - xg - yg) / yg; float mZg = (1 - xg - yg) / yg;
float mXb = xb / yb; float mXb = xb / yb;
const float Yb = 1;
float mZb = (1 - xb - yb) / yb; float mZb = (1 - xb - yb) / yb;
var xyzMatrix = new Matrix4x4 Matrix4x4 xyzMatrix = new()
{ {
M11 = mXr, M11 = mXr,
M21 = mXg, M21 = mXg,
M31 = mXb, M31 = mXb,
M12 = Yr, M12 = 1F,
M22 = Yg, M22 = 1F,
M32 = Yb, M32 = 1F,
M13 = mZr, M13 = mZr,
M23 = mZg, M23 = mZg,
M33 = mZb, M33 = mZb,
@ -55,7 +52,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); 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 // Use transposed Rows/Columns
// TODO: Is there a built in method for this multiplication? // TODO: Is there a built in method for this multiplication?
@ -64,9 +61,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
M11 = vector.X * mXr, M11 = vector.X * mXr,
M21 = vector.Y * mXg, M21 = vector.Y * mXg,
M31 = vector.Z * mXb, M31 = vector.Z * mXb,
M12 = vector.X * Yr, M12 = vector.X * 1,
M22 = vector.Y * Yg, M22 = vector.Y * 1,
M32 = vector.Z * Yb, M32 = vector.Z * 1,
M13 = vector.X * mZr, M13 = vector.X * mZr,
M23 = vector.Y * mZg, M23 = vector.Y * mZg,
M33 = vector.Z * mZb, 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."); 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); return new CieXyz(vector);
} }
} }

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

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

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

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <summary> /// <summary>
/// Color converter between Rgb and LinearRgb. /// Color converter between Rgb and LinearRgb.
/// </summary> /// </summary>
internal class RgbToLinearRgbConverter internal static class RgbToLinearRgbConverter
{ {
/// <summary> /// <summary>
/// Performs the conversion from the <see cref="Rgb"/> input to an instance of <see cref="LinearRgb"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns> /// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public LinearRgb Convert(in Rgb input) public static LinearRgb Convert(in Rgb input)
{ => new(
return new LinearRgb(
r: input.WorkingSpace.Expand(input.R), r: input.WorkingSpace.Expand(input.R),
g: input.WorkingSpace.Expand(input.G), g: input.WorkingSpace.Expand(input.G),
b: input.WorkingSpace.Expand(input.B), b: input.WorkingSpace.Expand(input.B),
workingSpace: input.WorkingSpace); 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"/> /// Color converter between <see cref="YCbCr"/> and <see cref="Rgb"/>
/// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas. /// See <see href="https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion"/> for formulas.
/// </summary> /// </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> /// <summary>
/// Performs the conversion from the <see cref="YCbCr"/> input to an instance of <see cref="Rgb"/> type. /// 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> /// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns> /// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Rgb Convert(in YCbCr input) public static Rgb Convert(in YCbCr input)
{ {
float y = input.Y; float y = input.Y;
float cb = input.Cb - 128F; float cb = input.Cb - 128F;
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <param name="input">The input color instance.</param> /// <param name="input">The input color instance.</param>
/// <returns>The converted result.</returns> /// <returns>The converted result.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public YCbCr Convert(in Rgb input) public static YCbCr Convert(in Rgb input)
{ {
Vector3 rgb = input.ToVector3() * MaxBytes; Vector3 rgb = input.ToVector3() * MaxBytes;
float r = rgb.X; 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -13,25 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly struct Hsl : IEquatable<Hsl> public readonly struct Hsl : IEquatable<Hsl>
{ {
private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1); private static readonly Vector3 Max = new(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;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct. /// Initializes a new instance of the <see cref="Hsl"/> struct.
@ -58,6 +40,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.L = vector.Z; 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> /// <summary>
/// Compares two <see cref="Hsl"/> objects for equality. /// Compares two <see cref="Hsl"/> objects for equality.
/// </summary> /// </summary>
@ -95,10 +95,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsl other) public bool Equals(Hsl other)
{ => this.H.Equals(other.H)
return this.H.Equals(other.H) && this.S.Equals(other.S)
&& this.S.Equals(other.S) && this.L.Equals(other.L);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -13,25 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public readonly struct Hsv : IEquatable<Hsv> public readonly struct Hsv : IEquatable<Hsv>
{ {
private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(360, 1, 1); private static readonly Vector3 Max = new(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;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Hsv"/> struct. /// Initializes a new instance of the <see cref="Hsv"/> struct.
@ -58,6 +40,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.V = vector.Z; 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> /// <summary>
/// Compares two <see cref="Hsv"/> objects for equality. /// Compares two <see cref="Hsv"/> objects for equality.
/// </summary> /// </summary>
@ -93,10 +93,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Hsv other) public bool Equals(Hsv other)
{ => this.H.Equals(other.H)
return this.H.Equals(other.H) && this.S.Equals(other.S)
&& this.S.Equals(other.S) && this.V.Equals(other.V);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -19,29 +19,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly CieXyz DefaultWhitePoint = Illuminants.C; 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> /// <summary>
/// Initializes a new instance of the <see cref="HunterLab"/> struct. /// Initializes a new instance of the <see cref="HunterLab"/> struct.
/// </summary> /// </summary>
@ -94,6 +71,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WhitePoint = whitePoint; 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> /// <summary>
/// Compares two <see cref="HunterLab"/> objects for equality. /// Compares two <see cref="HunterLab"/> objects for equality.
/// </summary> /// </summary>
@ -128,11 +128,9 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(HunterLab other) public bool Equals(HunterLab other)
{ => this.L.Equals(other.L)
return this.L.Equals(other.L) && this.A.Equals(other.A)
&& this.A.Equals(other.A) && this.B.Equals(other.B)
&& this.B.Equals(other.B) && this.WhitePoint.Equals(other.WhitePoint);
&& this.WhitePoint.Equals(other.WhitePoint);
}
} }
} }

56
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -21,29 +21,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; 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> /// <summary>
/// Initializes a new instance of the <see cref="LinearRgb"/> struct. /// Initializes a new instance of the <see cref="LinearRgb"/> struct.
/// </summary> /// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WorkingSpace = workingSpace; 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> /// <summary>
/// Compares two <see cref="LinearRgb"/> objects for equality. /// Compares two <see cref="LinearRgb"/> objects for equality.
/// </summary> /// </summary>
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <returns>The <see cref="Vector3"/>.</returns> /// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -137,10 +137,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(LinearRgb other) public bool Equals(LinearRgb other)
{ => this.R.Equals(other.R)
return this.R.Equals(other.R) && this.G.Equals(other.G)
&& this.G.Equals(other.G) && this.B.Equals(other.B);
&& 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -14,24 +14,6 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
public readonly struct Lms : IEquatable<Lms> 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> /// <summary>
/// Initializes a new instance of the <see cref="Lms"/> struct. /// Initializes a new instance of the <see cref="Lms"/> struct.
/// </summary> /// </summary>
@ -57,6 +39,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.S = vector.Z; 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> /// <summary>
/// Compares two <see cref="Lms"/> objects for equality. /// Compares two <see cref="Lms"/> objects for equality.
/// </summary> /// </summary>
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <returns>The <see cref="Vector3"/>.</returns> /// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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/> /// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S);
@ -98,10 +98,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Lms other) public bool Equals(Lms other)
{ => this.L.Equals(other.L)
return this.L.Equals(other.L) && this.M.Equals(other.M)
&& this.M.Equals(other.M) && this.S.Equals(other.S);
&& 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 Min = Vector3.Zero;
private static readonly Vector3 Max = Vector3.One; 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> /// <summary>
/// Initializes a new instance of the <see cref="Rgb"/> struct. /// Initializes a new instance of the <see cref="Rgb"/> struct.
/// </summary> /// </summary>
@ -95,6 +72,29 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.WorkingSpace = workingSpace; 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> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a /// Allows the implicit conversion of an instance of <see cref="Rgb24"/> to a
/// <see cref="Rgb"/>. /// <see cref="Rgb"/>.
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param> /// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns> /// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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> /// <summary>
/// Allows the implicit conversion of an instance of <see cref="Rgba32"/> to a /// 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> /// <param name="color">The instance of <see cref="Rgba32"/> to convert.</param>
/// <returns>An instance of <see cref="Rgb"/>.</returns> /// <returns>An instance of <see cref="Rgb"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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> /// <summary>
/// Compares two <see cref="Rgb"/> objects for equality. /// Compares two <see cref="Rgb"/> objects for equality.
@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// </summary> /// </summary>
/// <returns>The <see cref="Vector3"/>.</returns> /// <returns>The <see cref="Vector3"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [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/> /// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
@ -158,10 +158,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Rgb other) public bool Equals(Rgb other)
{ => this.R.Equals(other.R)
return this.R.Equals(other.R) && this.G.Equals(other.G)
&& this.G.Equals(other.G) && this.B.Equals(other.B);
&& 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> public readonly struct YCbCr : IEquatable<YCbCr>
{ {
private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Min = Vector3.Zero;
private static readonly Vector3 Max = new Vector3(255); private static readonly Vector3 Max = new(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;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="YCbCr"/> struct. /// Initializes a new instance of the <see cref="YCbCr"/> struct.
@ -60,6 +42,24 @@ namespace SixLabors.ImageSharp.ColorSpaces
this.Cr = vector.Z; 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> /// <summary>
/// Compares two <see cref="YCbCr"/> objects for equality. /// Compares two <see cref="YCbCr"/> objects for equality.
/// </summary> /// </summary>
@ -94,10 +94,8 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(YCbCr other) public bool Equals(YCbCr other)
{ => this.Y.Equals(other.Y)
return this.Y.Equals(other.Y) && this.Cb.Equals(other.Cb)
&& this.Cb.Equals(other.Cb) && this.Cr.Equals(other.Cr);
&& this.Cr.Equals(other.Cr);
}
} }
} }

1
src/ImageSharp/Diagnostics/MemoryDiagnostics.cs

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

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

@ -491,7 +491,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int max = cmd[1]; int max = cmd[1];
int bytesToRead = (max + 1) / 2; int bytesToRead = (max + 1) / 2;
var run = new byte[bytesToRead]; byte[] run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length); this.stream.Read(run, 0, run.Length);
@ -501,13 +501,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
byte twoPixels = run[idx]; byte twoPixels = run[idx];
if (i % 2 == 0) if (i % 2 == 0)
{ {
byte leftPixel = (byte)((twoPixels >> 4) & 0xF); buffer[count++] = (byte)((twoPixels >> 4) & 0xF);
buffer[count++] = leftPixel;
} }
else else
{ {
byte rightPixel = (byte)(twoPixels & 0xF); buffer[count++] = (byte)(twoPixels & 0xF);
buffer[count++] = rightPixel;
idx++; idx++;
} }
} }
@ -597,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// Take this number of bytes from the stream as uncompressed data. // Take this number of bytes from the stream as uncompressed data.
int length = cmd[1]; int length = cmd[1];
var run = new byte[length]; byte[] run = new byte[length];
this.stream.Read(run, 0, run.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. // Take this number of bytes from the stream as uncompressed data.
int length = cmd[1]; int length = cmd[1];
var run = new byte[length * 3]; byte[] run = new byte[length * 3];
this.stream.Read(run, 0, run.Length); 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 r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask);
int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask); int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask);
int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask); 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); color.FromRgb24(rgb);
pixelRow[x] = color; pixelRow[x] = color;
@ -1164,7 +1162,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
uint g = (uint)(temp & greenMask) >> rightShiftGreenMask; uint g = (uint)(temp & greenMask) >> rightShiftGreenMask;
uint b = (uint)(temp & blueMask) >> rightShiftBlueMask; uint b = (uint)(temp & blueMask) >> rightShiftBlueMask;
float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f; float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f;
var vector4 = new Vector4( Vector4 vector4 = new(
r * invMaxValueRed, r * invMaxValueRed,
g * invMaxValueGreen, g * invMaxValueGreen,
b * invMaxValueBlue, b * invMaxValueBlue,
@ -1246,7 +1244,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); 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}'."); 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. // color masks for each color channel follow the info header.
if (this.infoHeader.Compression == BmpCompression.BitFields) if (this.infoHeader.Compression == BmpCompression.BitFields)
{ {
var bitfieldsBuffer = new byte[12]; byte[] bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12); this.stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan(); Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
@ -1286,7 +1284,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
{ {
var bitfieldsBuffer = new byte[16]; byte[] bitfieldsBuffer = new byte[16];
this.stream.Read(bitfieldsBuffer, 0, 16); this.stream.Read(bitfieldsBuffer, 0, 16);
Span<byte> data = bitfieldsBuffer.AsSpan(); Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
@ -1396,6 +1394,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields. /// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields.
/// </summary> /// </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 /// <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> /// 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) 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]; 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.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame); this.WriteImage(stream, image.Frames.RootFrame);
this.WriteColorProfile(stream, iccProfileData, buffer); WriteColorProfile(stream, iccProfileData, buffer);
stream.Flush(); stream.Flush();
} }
@ -184,34 +184,33 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int hResolution = 0; int hResolution = 0;
int vResolution = 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)); hResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution)); vResolution = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution));
break; break;
case PixelResolutionUnit.PixelsPerCentimeter: case PixelResolutionUnit.PixelsPerCentimeter:
hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution)); hResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution)); vResolution = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution));
break; break;
case PixelResolutionUnit.PixelsPerMeter: case PixelResolutionUnit.PixelsPerMeter:
hResolution = (int)Math.Round(metadata.HorizontalResolution); hResolution = (int)Math.Round(metadata.HorizontalResolution);
vResolution = (int)Math.Round(metadata.VerticalResolution); vResolution = (int)Math.Round(metadata.VerticalResolution);
break; break;
}
} }
} }
var infoHeader = new BmpInfoHeader( BmpInfoHeader infoHeader = new(
headerSize: infoHeaderSize, headerSize: infoHeaderSize,
height: height, height: height,
width: width, width: width,
@ -248,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="iccProfileData">The color profile data.</param> /// <param name="iccProfileData">The color profile data.</param>
/// <param name="buffer">The buffer.</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) 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="iccProfileSize">The size in bytes of the color profile.</param>
/// <param name="infoHeader">The information header to write.</param> /// <param name="infoHeader">The information header to write.</param>
/// <param name="buffer">The buffer to write to.</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, type: BmpConstants.TypeMarkers.Bitmap,
fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + iccProfileSize + infoHeader.ImageSize, fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + iccProfileSize + infoHeader.ImageSize,
reserved: 0, reserved: 0,
@ -555,7 +554,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
if (pixelRowSpan.Length % 2 != 0) 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++) 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"/> /// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan<byte> data) public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan<byte> data)
{ {
var infoHeader = new BmpInfoHeader( BmpInfoHeader infoHeader = new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data[..4]), headerSize: BinaryPrimitives.ReadInt32LittleEndian(data[..4]),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 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. // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
// Map the OS/2 value to the windows values. // Map the OS/2 value to the windows values.
switch (compression) switch (BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)))
{ {
case 0: case 0:
infoHeader.Compression = BmpCompression.RGB; infoHeader.Compression = BmpCompression.RGB;
@ -465,11 +463,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns> /// <returns>The parsed header.</returns>
/// <seealso href="https://docs.microsoft.com/de-de/windows/win32/api/wingdi/ns-wingdi-bitmapv5header?redirectedfrom=MSDN"/> /// <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) public static BmpInfoHeader ParseV5(ReadOnlySpan<byte> data)
{ {
if (data.Length < SizeV5) 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]; return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
@ -545,13 +544,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal void VerifyDimensions() 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( throw new InvalidOperationException(
$"The input bmp '{this.Width}x{this.Height}' is " $"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> /// <summary>
/// Gets or sets the target size to decode the image into. /// Gets or sets the target size to decode the image into.
/// </summary> /// </summary>
public Size? TargetSize { get; set; } = null; public Size? TargetSize { get; set; }
/// <summary> /// <summary>
/// Gets or sets the sampler to use when resizing during decoding. /// Gets or sets the sampler to use when resizing during decoding.
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to ignore encoded metadata when decoding. /// Gets or sets a value indicating whether to ignore encoded metadata when decoding.
/// </summary> /// </summary>
public bool SkipMetadata { get; set; } = false; public bool SkipMetadata { get; set; }
/// <summary> /// <summary>
/// Gets or sets the maximum number of image frames to decode, inclusive. /// 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); this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
// Write the header. // Write the header.
this.WriteHeader(stream); WriteHeader(stream);
// Write the LSD. // Write the LSD.
int index = this.GetTransparentIndex(quantized); int index = GetTransparentIndex(quantized);
this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream); this.WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, index, useGlobalTable, stream);
if (useGlobalTable) if (useGlobalTable)
@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength
&& frameMetadata.ColorTableLength > 0) && frameMetadata.ColorTableLength > 0)
{ {
var options = new QuantizerOptions QuantizerOptions options = new()
{ {
Dither = this.quantizer.Options.Dither, Dither = this.quantizer.Options.Dither,
DitherScale = this.quantizer.Options.DitherScale, DitherScale = this.quantizer.Options.DitherScale,
@ -211,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
this.bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length); 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.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream); this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream); this.WriteImageData(quantized, stream);
@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <returns> /// <returns>
/// The <see cref="int"/>. /// The <see cref="int"/>.
/// </returns> /// </returns>
private int GetTransparentIndex<TPixel>(IndexedImageFrame<TPixel> quantized) private static int GetTransparentIndex<TPixel>(IndexedImageFrame<TPixel> quantized)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Transparent pixels are much more likely to be found at the end of a palette. // 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> /// </summary>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber); private static void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber);
/// <summary> /// <summary>
/// Writes the logical screen descriptor to the stream. /// 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, width: (ushort)width,
height: (ushort)height, height: (ushort)height,
packed: packedValue, packed: packedValue,
@ -332,14 +332,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Application Extension: Loop repeat count. // Application Extension: Loop repeat count.
if (frameCount > 1 && repeatCount != 1) if (frameCount > 1 && repeatCount != 1)
{ {
var loopingExtension = new GifNetscapeLoopingApplicationExtension(repeatCount); GifNetscapeLoopingApplicationExtension loopingExtension = new(repeatCount);
this.WriteExtension(loopingExtension, stream); this.WriteExtension(loopingExtension, stream);
} }
// Application Extension: XMP Profile. // Application Extension: XMP Profile.
if (xmpProfile != null) if (xmpProfile != null)
{ {
var xmpExtension = new GifXmpApplicationExtension(xmpProfile.Data); GifXmpApplicationExtension xmpExtension = new(xmpProfile.Data);
this.WriteExtension(xmpExtension, stream); this.WriteExtension(xmpExtension, stream);
} }
} }
@ -411,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
disposalMethod: metadata.DisposalMethod, disposalMethod: metadata.DisposalMethod,
transparencyFlag: transparencyIndex > -1); transparencyFlag: transparencyIndex > -1);
var extension = new GifGraphicControlExtension( GifGraphicControlExtension extension = new(
packed: packedValue, packed: packedValue,
delayTime: (ushort)metadata.FrameDelay, delayTime: (ushort)metadata.FrameDelay,
transparencyIndex: unchecked((byte)transparencyIndex)); transparencyIndex: unchecked((byte)transparencyIndex));
@ -422,6 +422,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Writes the provided extension to the stream. /// Writes the provided extension to the stream.
/// </summary> /// </summary>
/// <typeparam name="TGifExtension">The type of gif extension.</typeparam>
/// <param name="extension">The extension to write to the stream.</param> /// <param name="extension">The extension to write to the stream.</param>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
private void WriteExtension<TGifExtension>(TGifExtension extension, Stream stream) private void WriteExtension<TGifExtension>(TGifExtension extension, Stream stream)
@ -472,7 +473,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
sortFlag: false, sortFlag: false,
localColorTableSize: this.bitDepth - 1); localColorTableSize: this.bitDepth - 1);
var descriptor = new GifImageDescriptor( GifImageDescriptor descriptor = new(
left: 0, left: 0,
top: 0, top: 0,
width: (ushort)image.Width, width: (ushort)image.Width,
@ -517,7 +518,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void WriteImageData<TPixel>(IndexedImageFrame<TPixel> image, Stream stream) private void WriteImageData<TPixel>(IndexedImageFrame<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel> 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); 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) if (Avx.IsSupported)
{ {
var valueVec = Vector256.Create(value); Vector256<float> valueVec = Vector256.Create(value);
this.V0 = Avx.Multiply(this.V0, valueVec); this.V0 = Avx.Multiply(this.V0, valueVec);
this.V1 = Avx.Multiply(this.V1, valueVec); this.V1 = Avx.Multiply(this.V1, valueVec);
this.V2 = Avx.Multiply(this.V2, valueVec); this.V2 = Avx.Multiply(this.V2, valueVec);
@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
else else
{ {
var valueVec = new Vector4(value); Vector4 valueVec = new(value);
this.V0L *= valueVec; this.V0L *= valueVec;
this.V0R *= valueVec; this.V0R *= valueVec;
this.V1L *= valueVec; this.V1L *= valueVec;
@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Multiply all elements of the block by the corresponding elements of 'other'. /// Multiply all elements of the block by the corresponding elements of 'other'.
/// </summary> /// </summary>
/// <param name="other">The other block.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public unsafe void MultiplyInPlace(ref Block8x8F other) public unsafe void MultiplyInPlace(ref Block8x8F other)
{ {
@ -240,7 +241,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
if (Avx.IsSupported) if (Avx.IsSupported)
{ {
var valueVec = Vector256.Create(value); Vector256<float> valueVec = Vector256.Create(value);
this.V0 = Avx.Add(this.V0, valueVec); this.V0 = Avx.Add(this.V0, valueVec);
this.V1 = Avx.Add(this.V1, valueVec); this.V1 = Avx.Add(this.V1, valueVec);
this.V2 = Avx.Add(this.V2, valueVec); this.V2 = Avx.Add(this.V2, valueVec);
@ -252,7 +253,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
else else
{ {
var valueVec = new Vector4(value); Vector4 valueVec = new(value);
this.V0L += valueVec; this.V0L += valueVec;
this.V0R += valueVec; this.V0R += valueVec;
this.V1L += valueVec; this.V1L += valueVec;
@ -330,6 +331,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block. /// Level shift by +maximum/2, clip to [0..maximum], and round all the values in the block.
/// </summary> /// </summary>
/// <param name="maximum">The maximum value.</param>
public void NormalizeColorsAndRoundInPlace(float maximum) public void NormalizeColorsAndRoundInPlace(float maximum)
{ {
if (SimdUtils.HasVector8) 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); 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; ref Vector256<float> blockStride = ref this.V0;
for (int i = 0; i < RowCount; i++) for (int i = 0; i < RowCount; i++)
@ -468,20 +470,46 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
&& this.V7L == other.V7L && this.V7L == other.V7L
&& this.V7R == other.V7R; && 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 /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
var sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('['); sb.Append('[');
for (int i = 0; i < Size - 1; i++) for (int i = 0; i < Size - 1; i++)
{ {
sb.Append(this[i]); sb.Append(this[i]).Append(',');
sb.Append(',');
} }
sb.Append(this[Size - 1]); sb.Append(this[Size - 1]).Append(']');
sb.Append(']');
return sb.ToString(); return sb.ToString();
} }

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

@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverterBase"/> class. /// Initializes a new instance of the <see cref="JpegColorConverterBase"/> class.
/// </summary> /// </summary>
/// <param name="colorSpace">The color space.</param>
/// <param name="precision">The precision in bits.</param>
protected JpegColorConverterBase(JpegColorSpace colorSpace, int precision) protected JpegColorConverterBase(JpegColorSpace colorSpace, int precision)
{ {
this.ColorSpace = colorSpace; this.ColorSpace = colorSpace;
@ -66,6 +68,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/> corresponding to the given <see cref="JpegColorSpace"/> /// Returns the <see cref="JpegColorConverterBase"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary> /// </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) public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision)
{ {
JpegColorConverterBase converter = Array.Find( JpegColorConverterBase converter = Array.Find(
@ -75,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
if (converter is null) 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; return converter;
@ -104,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
// 5 color types with 2 supported precisions: 8 bit & 12 bit // 5 color types with 2 supported precisions: 8 bit & 12 bit
const int colorConvertersCount = 5 * 2; const int colorConvertersCount = 5 * 2;
var converters = new JpegColorConverterBase[colorConvertersCount]; JpegColorConverterBase[] converters = new JpegColorConverterBase[colorConvertersCount];
// 8-bit converters // 8-bit converters
converters[0] = GetYCbCrConverter(8); converters[0] = GetYCbCrConverter(8);
@ -126,6 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the YCbCr colorspace.
/// </summary> /// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYCbCrConverter(int precision) private static JpegColorConverterBase GetYCbCrConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported) if (JpegColorConverterAvx.IsSupported)
@ -144,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the YccK colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the YccK colorspace.
/// </summary> /// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetYccKConverter(int precision) private static JpegColorConverterBase GetYccKConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported) if (JpegColorConverterAvx.IsSupported)
@ -162,6 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the CMYK colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the CMYK colorspace.
/// </summary> /// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetCmykConverter(int precision) private static JpegColorConverterBase GetCmykConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported) if (JpegColorConverterAvx.IsSupported)
@ -180,6 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the gray scale colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the gray scale colorspace.
/// </summary> /// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetGrayScaleConverter(int precision) private static JpegColorConverterBase GetGrayScaleConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported) if (JpegColorConverterAvx.IsSupported)
@ -198,6 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Returns the <see cref="JpegColorConverterBase"/>s for the RGB colorspace. /// Returns the <see cref="JpegColorConverterBase"/>s for the RGB colorspace.
/// </summary> /// </summary>
/// <param name="precision">The precision in bits.</param>
private static JpegColorConverterBase GetRgbConverter(int precision) private static JpegColorConverterBase GetRgbConverter(int precision)
{ {
if (JpegColorConverterAvx.IsSupported) 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++) for (int i = 0; i < this.components.Length; i++)
{ {
var component = this.components[i] as ArithmeticDecodingComponent; ArithmeticDecodingComponent component = this.components[i] as ArithmeticDecodingComponent;
this.dcDecodingTables[i] = this.GetArithmeticTable(arithmeticDecodingTables, true, component.DcTableId); this.dcDecodingTables[i] = GetArithmeticTable(arithmeticDecodingTables, true, component.DcTableId);
component.DcStatistics = this.CreateOrGetStatisticsBin(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); component.AcStatistics = this.CreateOrGetStatisticsBin(false, component.AcTableId);
} }
} }
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.spectralConverter.InjectFrameData(frame, jpegData); 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; 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); this.statistics.Add(statistic);
return statistic; return statistic;
} }
private void ParseBaselineData() 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.DcPredictor = 0;
component.DcContext = 0; component.DcContext = 0;
component.DcStatistics?.Reset(); component.DcStatistics?.Reset();
@ -441,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++) for (int k = 0; k < this.scanComponentCount; k++)
{ {
int order = this.frame.ComponentOrder[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 dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId]; ref ArithmeticDecodingTable acDecodingTable = ref this.acDecodingTables[component.AcTableId];
@ -491,7 +492,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataSingleComponent() 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 mcuLines = this.frame.McusPerColumn;
int w = component.WidthInBlocks; int w = component.WidthInBlocks;
int h = component.SamplingFactors.Height; int h = component.SamplingFactors.Height;
@ -537,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseBaselineDataNonInterleaved() 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; ref JpegBitReader reader = ref this.scanBuffer;
int w = component.WidthInBlocks; int w = component.WidthInBlocks;
@ -586,7 +587,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int k = 0; k < this.scanComponentCount; k++) for (int k = 0; k < this.scanComponentCount; k++)
{ {
int order = this.frame.ComponentOrder[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 dcDecodingTable = ref this.dcDecodingTables[component.DcTableId];
int h = component.HorizontalSamplingFactor; int h = component.HorizontalSamplingFactor;
@ -628,7 +629,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private void ParseProgressiveDataNonInterleaved() 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; ref JpegBitReader reader = ref this.scanBuffer;
int w = component.WidthInBlocks; int w = component.WidthInBlocks;
@ -1140,7 +1141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
for (int i = 0; i < this.components.Length; i++) 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; 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. // Licensed under the Six Labors Split License.
using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -62,8 +63,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <inheritdoc/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ => this.Marker.ToString("X", CultureInfo.InvariantCulture);
return this.Marker.ToString("X");
}
} }
} }

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 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 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> /// <summary>
/// Initializes a new instance of the <see cref="HuffmanSpec"/> struct. /// Initializes a new instance of the <see cref="HuffmanSpec"/> struct.
/// </summary> /// </summary>
@ -146,5 +136,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
this.Count = count; this.Count = count;
this.Values = values; 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> /// <summary>
/// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>. /// Cache 8 pixel rows on the stack, which may originate from different buffers of a <see cref="MemoryGroup{T}"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of element in each row.</typeparam>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal ref struct RowOctet<T> internal ref struct RowOctet<T>
where T : struct where T : struct
@ -92,6 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private static Span<T> ThrowIndexOutOfRangeException() private static Span<T> ThrowIndexOutOfRangeException()
#pragma warning disable CA2201 // Do not raise reserved exception types
=> throw new IndexOutOfRangeException(); => 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) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> 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.ParseStream(stream, spectralConverter, cancellationToken);
this.InitExifProfile(); this.InitExifProfile();
this.InitIccProfile(); this.InitIccProfile();
@ -257,12 +257,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker"); JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
} }
using var ms = new MemoryStream(tableBytes); using MemoryStream ms = new(tableBytes);
using var stream = new BufferedReadStream(this.configuration, ms); using BufferedReadStream stream = new(this.configuration, ms);
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
int bytesRead = stream.Read(this.markerBuffer, 0, 2); 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) if (fileMarker.Marker != JpegConstants.Markers.SOI)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
@ -290,7 +290,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
switch (fileMarker.Marker) switch (fileMarker.Marker)
{ {
case JpegConstants.Markers.SOI: case JpegConstants.Markers.SOI:
break;
case JpegConstants.Markers.RST0: case JpegConstants.Markers.RST0:
case JpegConstants.Markers.RST7: case JpegConstants.Markers.RST7:
break; break;
@ -335,7 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Check for the Start Of Image marker. // Check for the Start Of Image marker.
stream.Read(this.markerBuffer, 0, 2); 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) if (fileMarker.Marker != JpegConstants.Markers.SOI)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
@ -409,12 +408,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.ProcessStartOfScanMarker(stream, markerContentByteSize); this.ProcessStartOfScanMarker(stream, markerContentByteSize);
break; break;
} }
else
{ // It's highly unlikely that APPn related data will be found after the SOS marker
// It's highly unlikely that APPn related data will be found after the SOS marker // We should have gathered everything we need by now.
// We should have gathered everything we need by now. return;
return;
}
case JpegConstants.Markers.DHT: case JpegConstants.Markers.DHT:
@ -654,7 +651,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
if (this.hasIcc) if (this.hasIcc)
{ {
var profile = new IccProfile(this.iccData); IccProfile profile = new(this.iccData);
if (profile.CheckIsValid()) if (profile.CheckIsValid())
{ {
this.Metadata.IccProfile = profile; this.Metadata.IccProfile = profile;
@ -669,8 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
if (this.hasIptc) if (this.hasIptc)
{ {
var profile = new IptcProfile(this.iptcData); this.Metadata.IptcProfile = new IptcProfile(this.iptcData);
this.Metadata.IptcProfile = profile;
} }
} }
@ -681,8 +677,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
if (this.hasXmp) if (this.hasXmp)
{ {
var profile = new XmpProfile(this.xmpData); this.Metadata.XmpProfile = new XmpProfile(this.xmpData);
this.Metadata.XmpProfile = profile;
} }
} }
@ -723,7 +718,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="profile">The profile data array.</param> /// <param name="profile">The profile data array.</param>
/// <param name="extension">The array containing addition profile data.</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; int currentLength = profile.Length;
@ -751,7 +746,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
stream.Read(this.temp, 0, JFifMarker.Length); stream.Read(this.temp, 0, JFifMarker.Length);
remaining -= JFifMarker.Length; remaining -= JFifMarker.Length;
JFifMarker.TryParse(this.temp, out this.jFif); _ = JFifMarker.TryParse(this.temp, out this.jFif);
// TODO: thumbnail // TODO: thumbnail
if (remaining > 0) if (remaining > 0)
@ -772,9 +767,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApp1Marker(BufferedReadStream stream, int remaining) private void ProcessApp1Marker(BufferedReadStream stream, int remaining)
{ {
const int ExifMarkerLength = 6; const int exifMarkerLength = 6;
const int XmpMarkerLength = 29; const int xmpMarkerLength = 29;
if (remaining < ExifMarkerLength || this.skipMetadata) if (remaining < exifMarkerLength || this.skipMetadata)
{ {
// Skip the application header length. // Skip the application header length.
stream.Skip(remaining); 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. // XMP marker is the longer then the EXIF marker, so first try read the EXIF marker bytes.
stream.Read(this.temp, 0, ExifMarkerLength); stream.Read(this.temp, 0, exifMarkerLength);
remaining -= ExifMarkerLength; remaining -= exifMarkerLength;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.ExifMarker)) if (ProfileResolver.IsProfile(this.temp, ProfileResolver.ExifMarker))
{ {
@ -803,15 +798,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else else
{ {
// If the EXIF information exceeds 64K, it will be split over multiple APP1 markers. // 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; 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) if (remaining < remainingXmpMarkerBytes || this.skipMetadata)
{ {
// Skip the application header length. // Skip the application header length.
@ -819,7 +814,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return; return;
} }
stream.Read(this.temp, ExifMarkerLength, remainingXmpMarkerBytes); stream.Read(this.temp, exifMarkerLength, remainingXmpMarkerBytes);
remaining -= remainingXmpMarkerBytes; remaining -= remainingXmpMarkerBytes;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker)) if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker))
{ {
@ -834,7 +829,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else else
{ {
// If the XMP information exceeds 64K, it will be split over multiple APP1 markers. // 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; remaining = 0;
@ -853,16 +848,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void ProcessApp2Marker(BufferedReadStream stream, int remaining) private void ProcessApp2Marker(BufferedReadStream stream, int remaining)
{ {
// Length is 14 though we only need to check 12. // Length is 14 though we only need to check 12.
const int Icclength = 14; const int icclength = 14;
if (remaining < Icclength || this.skipMetadata) if (remaining < icclength || this.skipMetadata)
{ {
stream.Skip(remaining); stream.Skip(remaining);
return; return;
} }
byte[] identifier = new byte[Icclength]; byte[] identifier = new byte[icclength];
stream.Read(identifier, 0, Icclength); stream.Read(identifier, 0, icclength);
remaining -= Icclength; // We have read it by this point remaining -= icclength; // We have read it by this point
if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker))
{ {
@ -877,7 +872,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
else else
{ {
// If the ICC information exceeds 64K, it will be split over multiple APP2 markers // 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 else
@ -971,7 +966,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
byte conditioningTableValue = (byte)stream.ReadByte(); byte conditioningTableValue = (byte)stream.ReadByte();
remaining--; remaining--;
var arithmeticTable = new ArithmeticDecodingTable(tableClass, identifier); ArithmeticDecodingTable arithmeticTable = new(tableClass, identifier);
arithmeticTable.Configure(conditioningTableValue); arithmeticTable.Configure(conditioningTableValue);
bool tableEntryReplaced = false; bool tableEntryReplaced = false;
@ -1029,16 +1024,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="remaining">The remaining bytes in the segment block.</param> /// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApp14Marker(BufferedReadStream stream, int remaining) private void ProcessApp14Marker(BufferedReadStream stream, int remaining)
{ {
const int MarkerLength = AdobeMarker.Length; const int markerLength = AdobeMarker.Length;
if (remaining < MarkerLength) if (remaining < markerLength)
{ {
// Skip the application header length // Skip the application header length
stream.Skip(remaining); stream.Skip(remaining);
return; return;
} }
stream.Read(this.temp, 0, MarkerLength); stream.Read(this.temp, 0, markerLength);
remaining -= MarkerLength; remaining -= markerLength;
if (AdobeMarker.TryParse(this.temp, out this.adobe)) if (AdobeMarker.TryParse(this.temp, out this.adobe))
{ {
@ -1086,7 +1081,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
// 8 bit values // 8 bit values
case 0: case 0:
{
// Validate: 8 bit table needs exactly 64 bytes // Validate: 8 bit table needs exactly 64 bytes
if (remaining < 64) if (remaining < 64)
{ {
@ -1103,11 +1097,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
break; break;
}
// 16 bit values // 16 bit values
case 1: case 1:
{
// Validate: 16 bit table needs exactly 128 bytes // Validate: 16 bit table needs exactly 128 bytes
if (remaining < 128) if (remaining < 128)
{ {
@ -1124,14 +1116,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
break; break;
}
// Unknown precision - error // Unknown precision - error
default: default:
{
JpegThrowHelper.ThrowBadQuantizationTablePrecision(tablePrecision); JpegThrowHelper.ThrowBadQuantizationTablePrecision(tablePrecision);
break; break;
}
} }
// Estimating quality // Estimating quality
@ -1139,17 +1128,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
{ {
// luminance table // luminance table
case 0: case 0:
{
jpegMetadata.LuminanceQuality = Quantization.EstimateLuminanceQuality(ref table); jpegMetadata.LuminanceQuality = Quantization.EstimateLuminanceQuality(ref table);
break; break;
}
// chrominance table // chrominance table
case 1: case 1:
{
jpegMetadata.ChrominanceQuality = Quantization.EstimateChrominanceQuality(ref table); jpegMetadata.ChrominanceQuality = Quantization.EstimateChrominanceQuality(ref table);
break; break;
}
} }
} }
} }
@ -1312,7 +1297,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int codeValuesMaxByteSize = 256; const int codeValuesMaxByteSize = 256;
const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize; const int totalBufferSize = codeLengthsByteSize + codeValuesMaxByteSize + HuffmanTable.WorkspaceByteSize;
var huffmanScanDecoder = this.scanDecoder as HuffmanScanDecoder; HuffmanScanDecoder huffmanScanDecoder = this.scanDecoder as HuffmanScanDecoder;
if (huffmanScanDecoder is null) if (huffmanScanDecoder is null)
{ {
JpegThrowHelper.ThrowInvalidImageContentException("missing huffman table data"); JpegThrowHelper.ThrowInvalidImageContentException("missing huffman table data");
@ -1399,6 +1384,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Processes the SOS (Start of scan marker). /// Processes the SOS (Start of scan marker).
/// </summary> /// </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) private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
{ {
if (this.Frame is null) 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"); JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data");
} }
int spectralStart = this.temp[0]; this.scanDecoder.SpectralStart = this.temp[0];
this.scanDecoder.SpectralStart = spectralStart;
int spectralEnd = this.temp[1]; this.scanDecoder.SpectralEnd = this.temp[1];
this.scanDecoder.SpectralEnd = spectralEnd;
int successiveApproximation = this.temp[2]; int successiveApproximation = this.temp[2];
this.scanDecoder.SuccessiveHigh = successiveApproximation >> 4; 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -15,22 +15,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/> /// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) public IImageFormat DetectFormat(ReadOnlySpan<byte> header)
{ => this.IsSupportedFileFormat(header) ? JpegFormat.Instance : null;
return this.IsSupportedFileFormat(header) ? JpegFormat.Instance : null;
}
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{ => header.Length >= this.HeaderSize
return header.Length >= this.HeaderSize && && (IsJfif(header) || IsExif(header) || IsJpeg(header));
(this.IsJfif(header) || this.IsExif(header) || this.IsJpeg(header));
}
/// <summary> /// <summary>
/// Returns a value indicating whether the given bytes identify Jfif data. /// Returns a value indicating whether the given bytes identify Jfif data.
/// </summary> /// </summary>
/// <param name="header">The bytes representing the file header.</param> /// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns> /// <returns>The <see cref="bool"/></returns>
private bool IsJfif(ReadOnlySpan<byte> header) => private static bool IsJfif(ReadOnlySpan<byte> header) =>
header[6] == 0x4A && // J header[6] == 0x4A && // J
header[7] == 0x46 && // F header[7] == 0x46 && // F
header[8] == 0x49 && // I header[8] == 0x49 && // I
@ -42,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="header">The bytes representing the file header.</param> /// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns> /// <returns>The <see cref="bool"/></returns>
private bool IsExif(ReadOnlySpan<byte> header) => private static bool IsExif(ReadOnlySpan<byte> header) =>
header[6] == 0x45 && // E header[6] == 0x45 && // E
header[7] == 0x78 && // X header[7] == 0x78 && // X
header[8] == 0x69 && // I header[8] == 0x69 && // I
@ -55,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary> /// </summary>
/// <param name="header">The bytes representing the file header.</param> /// <param name="header">The bytes representing the file header.</param>
/// <returns>The <see cref="bool"/></returns> /// <returns>The <see cref="bool"/></returns>
private bool IsJpeg(ReadOnlySpan<byte> header) => private static bool IsJpeg(ReadOnlySpan<byte> header) =>
header[0] == 0xFF && // 255 header[0] == 0xFF && // 255
header[1] == 0xD8; // 216 header[1] == 0xD8; // 216
} }

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

@ -18,13 +18,11 @@ namespace SixLabors.ImageSharp.Formats.Pbm
public int HeaderSize => 2; public int HeaderSize => 2;
/// <inheritdoc/> /// <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 ((uint)header.Length > 1)
if (1 < (uint)header.Length)
#pragma warning restore SA1131 // Use readable conditions
{ {
// Signature should be between P1 and P6. // Signature should be between P1 and P6.
return header[0] == P && (uint)(header[1] - Zero - 1) < (Seven - Zero - 1); 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) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var metadata = new ImageMetadata(); ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
@ -250,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc/> /// <inheritdoc/>
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
var metadata = new ImageMetadata(); ImageMetadata metadata = new();
PngMetadata pngMetadata = metadata.GetPngMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream; this.currentStream = stream;
this.currentStream.Skip(8); this.currentStream.Skip(8);
@ -434,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The data containing physical data.</param> /// <param name="data">The data containing physical data.</param>
private static void ReadPhysicalChunk(ImageMetadata metadata, ReadOnlySpan<byte> data) 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 metadata.ResolutionUnits = physicalChunk.UnitSpecifier == byte.MinValue
? PixelResolutionUnit.AspectRatio ? PixelResolutionUnit.AspectRatio
@ -560,7 +560,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata) private void ReadScanlines<TPixel>(PngChunk chunk, ImageFrame<TPixel> image, PngMetadata pngMetadata)
where TPixel : unmanaged, IPixel<TPixel> 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); deframeStream.AllocateNewBytes(chunk.Length, true);
DeflateStream dataStream = deframeStream.CompressedStream; DeflateStream dataStream = deframeStream.CompressedStream;
@ -1018,7 +1018,7 @@ namespace SixLabors.ImageSharp.Formats.Png
string value = PngConstants.Encoding.GetString(data[(zeroIndex + 1)..]); 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)); 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)..]; ReadOnlySpan<byte> compressedData = data[(zeroIndex + 2)..];
if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed) && if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed)
!this.TryReadTextChunkMetadata(baseMetadata, name, uncompressed)) && !TryReadTextChunkMetadata(baseMetadata, name, uncompressed))
{ {
metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty)); 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 /// <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 /// text chunk was not identified as metadata, and should be stored in the metadata
/// object unmodified.</returns> /// 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) && if (chunkName.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase) &&
this.TryReadLegacyExifTextChunk(baseMetadata, chunkText)) TryReadLegacyExifTextChunk(baseMetadata, chunkText))
{ {
// Successfully parsed legacy exif data from text // Successfully parsed legacy exif data from text
return true; return true;
@ -1096,7 +1096,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
/// <param name="metadata">The <see cref="ImageMetadata"/> to store the decoded exif tags into.</param> /// <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> /// <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(); ReadOnlySpan<char> dataSpan = data.AsSpan();
dataSpan = dataSpan.TrimStart(); dataSpan = dataSpan.TrimStart();
@ -1219,10 +1219,10 @@ namespace SixLabors.ImageSharp.Formats.Png
fixed (byte* compressedDataBase = compressedData) fixed (byte* compressedDataBase = compressedData)
{ {
using IMemoryOwner<byte> destBuffer = this.memoryAllocator.Allocate<byte>(this.configuration.StreamProcessingBufferSize); using IMemoryOwner<byte> destBuffer = this.memoryAllocator.Allocate<byte>(this.configuration.StreamProcessingBufferSize);
using var memoryStreamOutput = new MemoryStream(compressedData.Length); using MemoryStream memoryStreamOutput = new(compressedData.Length);
using var memoryStreamInput = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length); using UnmanagedMemoryStream memoryStreamInput = new(compressedDataBase, compressedData.Length);
using var bufferedStream = new BufferedReadStream(this.configuration, memoryStreamInput); using BufferedReadStream bufferedStream = new(this.configuration, memoryStreamInput);
using var inflateStream = new ZlibInflateStream(bufferedStream); using ZlibInflateStream inflateStream = new(bufferedStream);
Span<byte> destUncompressedData = destBuffer.GetSpan(); Span<byte> destUncompressedData = destBuffer.GetSpan();
if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) if (!inflateStream.AllocateNewBytes(compressedData.Length, false))
@ -1358,7 +1358,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
else if (IsXmpTextData(keywordBytes)) else if (IsXmpTextData(keywordBytes))
{ {
var xmpProfile = new XmpProfile(data[dataStartIdx..].ToArray()); XmpProfile xmpProfile = new(data[dataStartIdx..].ToArray());
metadata.XmpProfile = xmpProfile; metadata.XmpProfile = xmpProfile;
} }
else else
@ -1561,18 +1561,17 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
} }
else
{
PngThrowHelper.ThrowInvalidChunkType();
// The IDE cannot detect the throw here. PngThrowHelper.ThrowInvalidChunkType();
return default;
} // The IDE cannot detect the throw here.
return default;
} }
/// <summary> /// <summary>
/// Attempts to read the length of the next chunk. /// Attempts to read the length of the next chunk.
/// </summary> /// </summary>
/// <param name="result">The result length. If the return type is <see langword="false"/> this parameter is passed uninitialized.</param>
/// <returns> /// <returns>
/// Whether the length was read. /// Whether the length was read.
/// </returns> /// </returns>
@ -1613,10 +1612,13 @@ namespace SixLabors.ImageSharp.Formats.Png
// Keywords should not be empty or have leading or trailing whitespace. // Keywords should not be empty or have leading or trailing whitespace.
name = PngConstants.Encoding.GetString(keywordBytes); 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() private void SwapScanlineBuffers()
=> (this.scanline, this.previousScanline) = (this.previousScanline, this.scanline); => (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 else if (this.bitDepth == 8)
{
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)
{ {
// 16 bit grayscale + alpha // 8 bit grayscale
using IMemoryOwner<La32> laBuffer = this.memoryAllocator.Allocate<La32>(rowSpan.Length); PixelOperations<TPixel>.Instance.ToL8Bytes(
Span<La32> laSpan = laBuffer.GetSpan(); this.configuration,
ref La32 laRef = ref MemoryMarshal.GetReference(laSpan); rowSpan,
PixelOperations<TPixel>.Instance.ToLa32(this.configuration, rowSpan, laSpan); rawScanlineSpan,
rowSpan.Length);
// 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 else
{ {
// 8 bit grayscale + alpha // 1, 2, and 4 bit grayscale
PixelOperations<TPixel>.Instance.ToLa16Bytes( 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, this.configuration,
rowSpan, rowSpan,
rawScanlineSpan, tempSpan,
rowSpan.Length); 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> /// <summary>
@ -316,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Png
switch (this.bytesPerPixel) switch (this.bytesPerPixel)
{ {
case 4: case 4:
{
// 8 bit Rgba // 8 bit Rgba
PixelOperations<TPixel>.Instance.ToRgba32Bytes( PixelOperations<TPixel>.Instance.ToRgba32Bytes(
this.configuration, this.configuration,
@ -324,10 +318,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rawScanlineSpan, rawScanlineSpan,
rowSpan.Length); rowSpan.Length);
break; break;
}
case 3: case 3:
{
// 8 bit Rgb // 8 bit Rgb
PixelOperations<TPixel>.Instance.ToRgb24Bytes( PixelOperations<TPixel>.Instance.ToRgb24Bytes(
this.configuration, this.configuration,
@ -335,10 +328,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rawScanlineSpan, rawScanlineSpan,
rowSpan.Length); rowSpan.Length);
break; break;
}
case 8: case 8:
{
// 16 bit Rgba // 16 bit Rgba
using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length)) using (IMemoryOwner<Rgba64> rgbaBuffer = this.memoryAllocator.Allocate<Rgba64>(rowSpan.Length))
{ {
@ -358,10 +350,9 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
break; break;
}
default: default:
{
// 16 bit Rgb // 16 bit Rgb
using (IMemoryOwner<Rgb48> rgbBuffer = this.memoryAllocator.Allocate<Rgb48>(rowSpan.Length)) using (IMemoryOwner<Rgb48> rgbBuffer = this.memoryAllocator.Allocate<Rgb48>(rowSpan.Length))
{ {
@ -380,7 +371,6 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
break; break;
}
} }
} }
@ -413,8 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(rowSpan); this.CollectGrayscaleBytes(rowSpan);
break; break;
case PngColorType.Rgb:
case PngColorType.RgbWithAlpha:
default: default:
this.CollectTPixelBytes(rowSpan); this.CollectTPixelBytes(rowSpan);
break; break;
@ -424,6 +412,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary> /// <summary>
/// Apply the line filter for the raw scanline to enable better compression. /// Apply the line filter for the raw scanline to enable better compression.
/// </summary> /// </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) private void FilterPixelBytes(ref Span<byte> filter, ref Span<byte> attempt)
{ {
switch (this.options.FilterMethod) switch (this.options.FilterMethod)
@ -446,7 +436,6 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngFilterMethod.Paeth: case PngFilterMethod.Paeth:
PaethFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _); PaethFilter.Encode(this.currentScanline.GetSpan(), this.previousScanline.GetSpan(), filter, this.bytesPerPixel, out int _);
break; break;
case PngFilterMethod.Adaptive:
default: default:
this.ApplyOptimalFilteredScanline(ref filter, ref attempt); this.ApplyOptimalFilteredScanline(ref filter, ref attempt);
break; 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 /// 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. /// to be most compressible, using lowest total variation as proxy for compressibility.
/// </summary> /// </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) private void ApplyOptimalFilteredScanline(ref Span<byte> filter, ref Span<byte> attempt)
{ {
// Palette images don't compress well with adaptive filtering. // 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> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteHeaderChunk(Stream stream) private void WriteHeaderChunk(Stream stream)
{ {
var header = new PngHeader( PngHeader header = new(
width: this.width, width: this.width,
height: this.height, height: this.height,
bitDepth: this.bitDepth, bitDepth: this.bitDepth,
@ -688,25 +679,24 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
int payloadLength = xmpData.Length + PngConstants.XmpKeyword.Length + iTxtHeaderSize; int payloadLength = xmpData.Length + PngConstants.XmpKeyword.Length + iTxtHeaderSize;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{ using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> payload = owner.GetSpan(); Span<byte> payload = owner.GetSpan();
PngConstants.XmpKeyword.CopyTo(payload); PngConstants.XmpKeyword.CopyTo(payload);
int bytesWritten = PngConstants.XmpKeyword.Length; int bytesWritten = PngConstants.XmpKeyword.Length;
// Write the iTxt header (all zeros in this case). // Write the iTxt header (all zeros in this case).
Span<byte> iTxtHeader = payload[bytesWritten..]; Span<byte> iTxtHeader = payload[bytesWritten..];
iTxtHeader[4] = 0; iTxtHeader[4] = 0;
iTxtHeader[3] = 0; iTxtHeader[3] = 0;
iTxtHeader[2] = 0; iTxtHeader[2] = 0;
iTxtHeader[1] = 0; iTxtHeader[1] = 0;
iTxtHeader[0] = 0; iTxtHeader[0] = 0;
bytesWritten += 5; bytesWritten += 5;
// And the XMP data itself. // And the XMP data itself.
xmpData.CopyTo(payload[bytesWritten..]); xmpData.CopyTo(payload[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, payload); this.WriteChunk(stream, PngChunkType.InternationalText, payload);
}
} }
/// <summary> /// <summary>
@ -725,16 +715,15 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] compressedData = this.GetZlibCompressedBytes(iccProfileBytes); byte[] compressedData = this.GetZlibCompressedBytes(iccProfileBytes);
int payloadLength = ColorProfileName.Length + compressedData.Length + 2; int payloadLength = ColorProfileName.Length + compressedData.Length + 2;
using (IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength))
{ using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
Span<byte> outputBytes = owner.GetSpan(); Span<byte> outputBytes = owner.GetSpan();
PngConstants.Encoding.GetBytes(ColorProfileName).CopyTo(outputBytes); PngConstants.Encoding.GetBytes(ColorProfileName).CopyTo(outputBytes);
int bytesWritten = ColorProfileName.Length; int bytesWritten = ColorProfileName.Length;
outputBytes[bytesWritten++] = 0; // Null separator. outputBytes[bytesWritten++] = 0; // Null separator.
outputBytes[bytesWritten++] = 0; // Compression. outputBytes[bytesWritten++] = 0; // Compression.
compressedData.CopyTo(outputBytes[bytesWritten..]); compressedData.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.EmbeddedColorProfile, outputBytes); this.WriteChunk(stream, PngChunkType.EmbeddedColorProfile, outputBytes);
}
} }
/// <summary> /// <summary>
@ -776,65 +765,59 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] languageTag = PngConstants.LanguageEncoding.GetBytes(textData.LanguageTag); byte[] languageTag = PngConstants.LanguageEncoding.GetBytes(textData.LanguageTag);
int payloadLength = keywordBytes.Length + textBytes.Length + translatedKeyword.Length + languageTag.Length + 5; 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; using IMemoryOwner<byte> owner = this.memoryAllocator.Allocate<byte>(payloadLength);
languageTag.CopyTo(outputBytes[bytesWritten..]); Span<byte> outputBytes = owner.GetSpan();
bytesWritten += languageTag.Length; keywordBytes.CopyTo(outputBytes);
outputBytes[bytesWritten++] = 0; int bytesWritten = keywordBytes.Length;
translatedKeyword.CopyTo(outputBytes[bytesWritten..]); outputBytes[bytesWritten++] = 0;
bytesWritten += translatedKeyword.Length;
outputBytes[bytesWritten++] = 0;
textBytes.CopyTo(outputBytes[bytesWritten..]);
this.WriteChunk(stream, PngChunkType.InternationalText, outputBytes);
}
}
else
{
if (textData.Value.Length > this.options.TextCompressionThreshold) if (textData.Value.Length > this.options.TextCompressionThreshold)
{ {
// Write zTXt chunk. // Indicate that the text is compressed.
byte[] compressedData = this.GetZlibCompressedBytes(PngConstants.Encoding.GetBytes(textData.Value)); outputBytes[bytesWritten++] = 1;
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 else
{ {
// Write tEXt chunk. outputBytes[bytesWritten++] = 0;
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;
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> /// <returns>The compressed byte array.</returns>
private byte[] GetZlibCompressedBytes(byte[] dataBytes) 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);
{
deflateStream.Write(dataBytes);
}
return memoryStream.ToArray();
} }
return memoryStream.ToArray();
} }
/// <summary> /// <summary>
@ -944,9 +925,9 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] buffer; byte[] buffer;
int bufferLength; 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) 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 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); 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. /// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator ==(PngTextData left, PngTextData right) public static bool operator ==(PngTextData left, PngTextData right)
{ => left.Equals(right);
return left.Equals(right);
}
/// <summary> /// <summary>
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values /// 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. /// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns> /// </returns>
public static bool operator !=(PngTextData left, PngTextData right) public static bool operator !=(PngTextData left, PngTextData right)
{ => !(left == right);
return !(left == right);
}
/// <summary> /// <summary>
/// Indicates whether this instance and a specified object are equal. /// Indicates whether this instance and a specified object are equal.
@ -105,9 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// same value; otherwise, false. /// same value; otherwise, false.
/// </returns> /// </returns>
public override bool Equals(object obj) public override bool Equals(object obj)
{ => obj is PngTextData other && this.Equals(other);
return obj is PngTextData other && this.Equals(other);
}
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Returns the hash code for this instance.
@ -115,7 +109,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns> /// <returns>
/// A 32-bit signed integer that is the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance.
/// </returns> /// </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> /// <summary>
/// Returns the fully qualified type name of this instance. /// Returns the fully qualified type name of this instance.
@ -123,7 +118,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns> /// <returns>
/// A <see cref="T:System.String"/> containing a fully qualified type name. /// A <see cref="T:System.String"/> containing a fully qualified type name.
/// </returns> /// </returns>
public override string ToString() => $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]"; public override string ToString()
=> $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]";
/// <summary> /// <summary>
/// Indicates whether the current object is equal to another object of the same type. /// Indicates whether the current object is equal to another object of the same type.
@ -133,11 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </returns> /// </returns>
/// <param name="other">An object to compare with this object.</param> /// <param name="other">An object to compare with this object.</param>
public bool Equals(PngTextData other) public bool Equals(PngTextData other)
{ => this.Keyword.Equals(other.Keyword, StringComparison.OrdinalIgnoreCase)
return this.Keyword.Equals(other.Keyword) && this.Value.Equals(other.Value, StringComparison.OrdinalIgnoreCase)
&& this.Value.Equals(other.Value) && this.LanguageTag.Equals(other.LanguageTag, StringComparison.OrdinalIgnoreCase)
&& this.LanguageTag.Equals(other.LanguageTag) && this.TranslatedKeyword.Equals(other.TranslatedKeyword, StringComparison.OrdinalIgnoreCase);
&& this.TranslatedKeyword.Equals(other.TranslatedKeyword);
}
} }
} }

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

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
@ -11,31 +12,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
internal static class PngThrowHelper internal static class PngThrowHelper
{ {
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException)
=> throw new InvalidImageContentException(errorMessage, innerException); => throw new InvalidImageContentException(errorMessage, innerException);
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoHeader() => throw new InvalidImageContentException("PNG Image does not contain a header chunk"); public static void ThrowNoHeader() => throw new InvalidImageContentException("PNG Image does not contain a header chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNoData() => throw new InvalidImageContentException("PNG Image does not contain a data chunk"); public static void ThrowNoData() => throw new InvalidImageContentException("PNG Image does not contain a data chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowMissingPalette() => throw new InvalidImageContentException("PNG Image does not contain a palette chunk"); public static void ThrowMissingPalette() => throw new InvalidImageContentException("PNG Image does not contain a palette chunk");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data."); public static void ThrowInvalidChunkType() => throw new InvalidImageContentException("Invalid PNG data.");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkType(string message) => throw new InvalidImageContentException(message); public static void ThrowInvalidChunkType(string message) => throw new InvalidImageContentException(message);
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); public static void ThrowInvalidChunkCrc(string chunkTypeName) => throw new InvalidImageContentException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowNotSupportedColor() => throw new NotSupportedException("Unsupported PNG color type"); public static void ThrowNotSupportedColor() => throw new NotSupportedException("Unsupported PNG color type");
[DoesNotReturn]
[MethodImpl(InliningOptions.ColdPath)] [MethodImpl(InliningOptions.ColdPath)]
public static void ThrowUnknownFilter() => throw new InvalidImageContentException("Unknown filter type."); 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;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
@ -99,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
imageDescriptor |= 0x1; imageDescriptor |= 0x1;
} }
var fileHeader = new TgaFileHeader( TgaFileHeader fileHeader = new(
idLength: 0, idLength: 0,
colorMapType: 0, colorMapType: 0,
imageType: imageType, imageType: imageType,
@ -158,8 +159,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
case TgaBitsPerPixel.Pixel32: case TgaBitsPerPixel.Pixel32:
this.Write32Bit(stream, pixels); this.Write32Bit(stream, pixels);
break; break;
default:
break;
} }
} }
@ -181,7 +180,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{ {
TPixel currentPixel = pixelRow[x]; TPixel currentPixel = pixelRow[x];
currentPixel.ToRgba32(ref color); currentPixel.ToRgba32(ref color);
byte equalPixelCount = this.FindEqualPixels(pixelRow, x); byte equalPixelCount = FindEqualPixels(pixelRow, x);
if (equalPixelCount > 0) if (equalPixelCount > 0)
{ {
@ -193,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
else else
{ {
// Write Raw Packet (i.e., Non-Run-Length Encoded): // Write Raw Packet (i.e., Non-Run-Length Encoded):
byte unEqualPixelCount = this.FindUnEqualPixels(pixelRow, x); byte unEqualPixelCount = FindUnEqualPixels(pixelRow, x);
stream.WriteByte(unEqualPixelCount); stream.WriteByte(unEqualPixelCount);
this.WritePixel(stream, currentPixel, color); this.WritePixel(stream, currentPixel, color);
x++; x++;
@ -227,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
break; break;
case TgaBitsPerPixel.Pixel16: case TgaBitsPerPixel.Pixel16:
var bgra5551 = new Bgra5551(color.ToVector4()); Bgra5551 bgra5551 = new(color.ToVector4());
BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); BinaryPrimitives.TryWriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue);
stream.WriteByte(this.buffer[0]); stream.WriteByte(this.buffer[0]);
stream.WriteByte(this.buffer[1]); stream.WriteByte(this.buffer[1]);
@ -246,8 +245,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
stream.WriteByte(color.R); stream.WriteByte(color.R);
stream.WriteByte(color.A); stream.WriteByte(color.A);
break; 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="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the same pixels.</param> /// <param name="xStart">X coordinate to start searching for the same pixels.</param>
/// <returns>The number of equal pixels.</returns> /// <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> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte equalPixelCount = 0; 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="pixelRow">A pixel row of the image to encode.</param>
/// <param name="xStart">X coordinate to start searching for the unequal pixels.</param> /// <param name="xStart">X coordinate to start searching for the unequal pixels.</param>
/// <returns>The number of equal pixels.</returns> /// <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> where TPixel : unmanaged, IPixel<TPixel>
{ {
byte unEqualPixelCount = 0; byte unEqualPixelCount = 0;
@ -419,12 +416,13 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary> /// <summary>
/// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of pixel format.</typeparam>
/// <param name="sourcePixel">The pixel to get the luminance from.</param> /// <param name="sourcePixel">The pixel to get the luminance from.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static int GetLuminance<TPixel>(TPixel sourcePixel) public static int GetLuminance<TPixel>(TPixel sourcePixel)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var vector = sourcePixel.ToVector4(); Vector4 vector = sourcePixel.ToVector4();
return ColorNumerics.GetBT709Luminance(ref vector, 256); return ColorNumerics.GetBT709Luminance(ref vector, 256);
} }
} }

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

@ -20,9 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary> /// </summary>
/// <param name="other">The metadata to create an instance from.</param> /// <param name="other">The metadata to create an instance from.</param>
private TgaMetadata(TgaMetadata other) private TgaMetadata(TgaMetadata other)
{ => this.BitsPerPixel = other.BitsPerPixel;
this.BitsPerPixel = other.BitsPerPixel;
}
/// <summary> /// <summary>
/// Gets or sets the number of bits per pixel. /// 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; public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24;
/// <summary> /// <summary>
/// Gets or sets the the number of alpha bits per pixel. /// Gets or sets the number of alpha bits per pixel.
/// </summary> /// </summary>
public byte AlphaChannelBits { get; set; } = 0; public byte AlphaChannelBits { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new TgaMetadata(this); 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; uint codeLength;
if (runLength <= 63) if (runLength <= 63)
{ {
code = this.GetTermCode(runLength, out codeLength, isWhiteRun); code = GetTermCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData); this.WriteCode(codeLength, code, compressedData);
x += (int)runLength; x += (int)runLength;
} }
else else
{ {
runLength = this.GetBestFittingMakeupRunLength(runLength); runLength = GetBestFittingMakeupRunLength(runLength);
code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun); code = GetMakeupCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData); this.WriteCode(codeLength, code, compressedData);
x += (int)runLength; 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); Span<byte> row = pixelsAsGray.Slice(y * this.Width, this.Width);
uint a0 = 0; uint a0 = 0;
uint a1 = row[0] == 0 ? 0 : this.FindRunEnd(row, 0); uint a1 = row[0] == 0 ? 0 : FindRunEnd(row, 0);
uint b1 = referenceLine[0] == 0 ? 0 : this.FindRunEnd(referenceLine, 0); uint b1 = referenceLine[0] == 0 ? 0 : FindRunEnd(referenceLine, 0);
while (true) while (true)
{ {
uint b2 = this.FindRunEnd(referenceLine, b1); uint b2 = FindRunEnd(referenceLine, b1);
if (b2 < a1) if (b2 < a1)
{ {
// Pass mode. // Pass mode.
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
d = -(int)(a1 - b1); d = -(int)(a1 - b1);
} }
if ((d >= -3) && (d <= 3)) if (d is >= -3 and <= 3)
{ {
// Vertical mode. // Vertical mode.
(uint length, uint code) = VerticalCodes[d + 3]; (uint length, uint code) = VerticalCodes[d + 3];
@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
// Horizontal mode. // Horizontal mode.
this.WriteCode(3, 1, compressedData); this.WriteCode(3, 1, compressedData);
uint a2 = this.FindRunEnd(row, a1); uint a2 = FindRunEnd(row, a1);
if ((a0 + a1 == 0) || (row[(int)a0] != 0)) if ((a0 + a1 == 0) || (row[(int)a0] != 0))
{ {
this.WriteRun(a1 - a0, true, compressedData); this.WriteRun(a1 - a0, true, compressedData);
@ -119,9 +119,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
} }
byte thisPixel = row[(int)a0]; byte thisPixel = row[(int)a0];
a1 = this.FindRunEnd(row, a0, thisPixel); a1 = FindRunEnd(row, a0, thisPixel);
b1 = this.FindRunEnd(referenceLine, a0, (byte)~thisPixel); b1 = FindRunEnd(referenceLine, a0, (byte)~thisPixel);
b1 = this.FindRunEnd(referenceLine, b1, thisPixel); b1 = FindRunEnd(referenceLine, b1, thisPixel);
} }
// This row is now the reference line. // 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"/> /// <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"/>, /// that does not match <paramref name="color"/>, or the length of <paramref name="row"/>,
/// whichever comes first.</returns> /// 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) if (startIndex >= row.Length)
{ {
return (uint)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++) for (int i = (int)startIndex; i < row.Length; i++)
{ {
if (row[i] != colorValue) if (row[i] != colorValue)
@ -188,13 +188,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
uint codeLength; uint codeLength;
while (runLength > 63) while (runLength > 63)
{ {
uint makeupLength = this.GetBestFittingMakeupRunLength(runLength); uint makeupLength = GetBestFittingMakeupRunLength(runLength);
code = this.GetMakeupCode(makeupLength, out codeLength, isWhiteRun); code = GetMakeupCode(makeupLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData); this.WriteCode(codeLength, code, compressedData);
runLength -= makeupLength; runLength -= makeupLength;
} }
code = this.GetTermCode(runLength, out codeLength, isWhiteRun); code = GetTermCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData); 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; this.bitPosition = 0;
} }
private uint GetWhiteMakeupCode(uint runLength, out uint codeLength) private static uint GetWhiteMakeupCode(uint runLength, out uint codeLength)
{ {
codeLength = 0; codeLength = 0;
@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0; return 0;
} }
private uint GetBlackMakeupCode(uint runLength, out uint codeLength) private static uint GetBlackMakeupCode(uint runLength, out uint codeLength)
{ {
codeLength = 0; codeLength = 0;
@ -275,7 +275,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0; return 0;
} }
private uint GetWhiteTermCode(uint runLength, out uint codeLength) private static uint GetWhiteTermCode(uint runLength, out uint codeLength)
{ {
codeLength = 0; codeLength = 0;
@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
return 0; return 0;
} }
private uint GetBlackTermCode(uint runLength, out uint codeLength) private static uint GetBlackTermCode(uint runLength, out uint codeLength)
{ {
codeLength = 0; codeLength = 0;
@ -390,7 +390,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// </summary> /// </summary>
/// <param name="runLength">A run length needing a makeup code</param> /// <param name="runLength">A run length needing a makeup code</param>
/// <returns>The makeup length for <paramref name="runLength"/>.</returns> /// <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)); 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> /// <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. /// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
/// If <c>false</c> the run is of black pixels</param> /// If <c>false</c> the run is of black pixels</param>
/// <returns>The terminating code for a run of length <paramref name="runLength"/></returns> /// <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) 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> /// <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. /// <param name="isWhiteRun">If <c>true</c>, the run is of white pixels.
/// If <c>false</c> the run is of black pixels</param> /// If <c>false</c> the run is of black pixels</param>
/// <returns>The makeup code for a run of length <paramref name="runLength"/></returns> /// <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) 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> /// <summary>
@ -494,12 +494,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
/// <summary> /// <summary>
/// Writes a image compressed with CCITT T6 to the stream. /// Writes a image compressed with CCITT T6 to the stream.
/// </summary> /// </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> /// <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(rows.Length / height == this.Width, "Values must be equals");
DebugGuard.IsTrue(pixelsAsGray.Length % height == 0, "Values must be equals"); DebugGuard.IsTrue(rows.Length % height == 0, "Values must be equals");
this.compressedDataBuffer.Clear(); this.compressedDataBuffer.Clear();
Span<byte> compressedData = this.compressedDataBuffer.GetSpan(); Span<byte> compressedData = this.compressedDataBuffer.GetSpan();
@ -507,7 +507,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors
this.bytePosition = 0; this.bytePosition = 0;
this.bitPosition = 0; this.bitPosition = 0;
this.CompressStrip(pixelsAsGray, height, compressedData); this.CompressStrip(rows, height, compressedData);
// Write the compressed data to the stream. // Write the compressed data to the stream.
int bytesToWrite = this.bitPosition != 0 ? this.bytePosition + 1 : this.bytePosition; 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> /// </summary>
public readonly struct TiffBitsPerSample : IEquatable<TiffBitsPerSample> 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> /// <summary>
/// Initializes a new instance of the <see cref="TiffBitsPerSample"/> struct. /// Initializes a new instance of the <see cref="TiffBitsPerSample"/> struct.
/// </summary> /// </summary>
@ -56,6 +31,53 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.Channels += (byte)(this.Channel3 != 0 ? 1 : 0); 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> /// <summary>
/// Tries to parse a ushort array and convert it into a TiffBitsPerSample struct. /// Tries to parse a ushort array and convert it into a TiffBitsPerSample struct.
/// </summary> /// </summary>

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

@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary> /// </summary>
private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
private readonly List<(long, uint)> frameMarkers = new List<(long, uint)>(); private readonly List<(long, uint)> frameMarkers = new();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TiffEncoderCore"/> class. /// 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. // Make sure, the Encoder options makes sense in combination with each other.
this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); this.SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor);
using var writer = new TiffStreamWriter(stream); using TiffStreamWriter writer = new(stream);
long ifdMarker = this.WriteHeader(writer); long ifdMarker = WriteHeader(writer);
Image<TPixel> metadataImage = image; Image<TPixel> metadataImage = image;
foreach (ImageFrame<TPixel> frame in image.Frames) foreach (ImageFrame<TPixel> frame in image.Frames)
{ {
cancellationToken.ThrowIfCancellationRequested(); 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); ifdMarker = this.WriteFrame(writer, frame, image.Metadata, metadataImage, ifdMarker);
metadataImage = null; metadataImage = null;
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <returns> /// <returns>
/// The marker to write the first IFD offset. /// The marker to write the first IFD offset.
/// </returns> /// </returns>
public long WriteHeader(TiffStreamWriter writer) public static long WriteHeader(TiffStreamWriter writer)
{ {
writer.Write(ByteOrderMarker); writer.Write(ByteOrderMarker);
writer.Write(TiffConstants.HeaderMagicNumber); writer.Write(TiffConstants.HeaderMagicNumber);
@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.compressionLevel, this.compressionLevel,
this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None); this.HorizontalPredictor == TiffPredictor.Horizontal ? this.HorizontalPredictor.Value : TiffPredictor.None);
var entriesCollector = new TiffEncoderEntriesCollector(); TiffEncoderEntriesCollector entriesCollector = new();
using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create( using TiffBaseColorWriter<TPixel> colorWriter = TiffColorWriterFactory.Create(
this.PhotometricInterpretation, this.PhotometricInterpretation,
frame, frame,
@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
entriesCollector, entriesCollector,
(int)this.BitsPerPixel); (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); 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="bytesPerRow">The number of bytes per row.</param>
/// <param name="compression">The compression used.</param> /// <param name="compression">The compression used.</param>
/// <returns>Number of rows per strip.</returns> /// <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(height, 0, nameof(height));
DebugGuard.MustBeGreaterThan(bytesPerRow, 0, nameof(bytesPerRow)); 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)); 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); 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) public static bool IsOneBitCompression(TiffCompression? compression)
{ => compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax;
if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax)
{
return true;
}
return false;
}
} }
} }

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

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Writers
/// <summary> /// <summary>
/// Gets a value indicating whether the architecture is little-endian. /// Gets a value indicating whether the architecture is little-endian.
/// </summary> /// </summary>
public bool IsLittleEndian => BitConverter.IsLittleEndian; public static bool IsLittleEndian => BitConverter.IsLittleEndian;
/// <summary> /// <summary>
/// Gets the current position within the stream. /// 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> /// <param name="value">The two-byte unsigned integer to write.</param>
public void Write(ushort value) public void Write(ushort value)
{ {
if (this.IsLittleEndian) if (IsLittleEndian)
{ {
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); 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> /// <param name="value">The four-byte unsigned integer to write.</param>
public void Write(uint value) public void Write(uint value)
{ {
if (this.IsLittleEndian) if (IsLittleEndian)
{ {
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); 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; uint split = this.range >> 1;
ulong value = this.value >> pos; ulong value = this.value >> pos;
ulong mask = (split - value) >> 31; // -1 or 0 ulong mask = (split - value) >> 31; // -1 or 0
this.bits -= 1; this.bits--;
this.range = (this.range + (uint)mask) | 1; this.range = (this.range + (uint)mask) | 1;
this.value -= ((split + 1) & mask) << pos; 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)); ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.Memory.Span.Slice((int)this.pos, 8));
this.pos += BitsCount >> 3; this.pos += BitsCount >> 3;
ulong bits = this.ByteSwap64(inBits); ulong bits = ByteSwap64(inBits);
bits >>= 64 - BitsCount; bits >>= 64 - BitsCount;
this.value = bits | (this.value << BitsCount); this.value = bits | (this.value << BitsCount);
this.bits += BitsCount; this.bits += BitsCount;
@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitReader
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private ulong ByteSwap64(ulong x) private static ulong ByteSwap64(ulong x)
{ {
x = ((x & 0xffffffff00000000ul) >> 32) | ((x & 0x00000000fffffffful) << 32); x = ((x & 0xffffffff00000000ul) >> 32) | ((x & 0x00000000fffffffful) << 32);
x = ((x & 0xffff0000ffff0000ul) >> 16) | ((x & 0x0000ffff0000fffful) << 16); 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.Buffers.Binary;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
namespace SixLabors.ImageSharp.Formats.Webp.BitWriter 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. /// Initializes a new instance of the <see cref="BitWriterBase"/> class.
/// Used internally for cloning. /// Used internally for cloning.
/// </summary> /// </summary>
/// <param name="buffer">The byte buffer.</param>
private protected BitWriterBase(byte[] buffer) => this.buffer = buffer; private protected BitWriterBase(byte[] buffer) => this.buffer = buffer;
public byte[] Buffer => this.buffer; public byte[] Buffer => this.buffer;
@ -102,12 +102,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// </summary> /// </summary>
/// <param name="metadataBytes">The metadata profile bytes.</param> /// <param name="metadataBytes">The metadata profile bytes.</param>
/// <returns>The metadata chunk size in bytes.</returns> /// <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 metaSize = (uint)metadataBytes.Length;
uint metaChunkSize = WebpConstants.ChunkHeaderSize + metaSize + (metaSize & 1); return WebpConstants.ChunkHeaderSize + metaSize + (metaSize & 1);
return metaChunkSize;
} }
/// <summary> /// <summary>
@ -115,12 +113,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// </summary> /// </summary>
/// <param name="alphaBytes">The alpha chunk bytes.</param> /// <param name="alphaBytes">The alpha chunk bytes.</param>
/// <returns>The alpha data chunk size in bytes.</returns> /// <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 alphaSize = (uint)alphaBytes.Length + 1;
uint alphaChunkSize = WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1); return WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
return alphaChunkSize;
} }
/// <summary> /// <summary>

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

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

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

@ -150,21 +150,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
{ {
isVp8X = true; isVp8X = true;
exifBytes = exifProfile.ToByteArray(); exifBytes = exifProfile.ToByteArray();
riffSize += this.MetadataChunkSize(exifBytes); riffSize += MetadataChunkSize(exifBytes);
} }
if (xmpProfile != null) if (xmpProfile != null)
{ {
isVp8X = true; isVp8X = true;
xmpBytes = xmpProfile.Data; xmpBytes = xmpProfile.Data;
riffSize += this.MetadataChunkSize(xmpBytes); riffSize += MetadataChunkSize(xmpBytes);
} }
if (iccProfile != null) if (iccProfile != null)
{ {
isVp8X = true; isVp8X = true;
iccBytes = iccProfile.ToByteArray(); iccBytes = iccProfile.ToByteArray();
riffSize += this.MetadataChunkSize(iccBytes); riffSize += MetadataChunkSize(iccBytes);
} }
if (isVp8X) 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) foreach (CrunchConfig crunchConfig in crunchConfigs)
{ {
bgra.CopyTo(encodedData); bgra.CopyTo(encodedData);
bool useCache = true; const bool useCache = true;
this.UsePalette = crunchConfig.EntropyIdx is EntropyIx.Palette or EntropyIx.PaletteAndSpatial; this.UsePalette = crunchConfig.EntropyIdx is EntropyIx.Palette or EntropyIx.PaletteAndSpatial;
this.UseSubtractGreenTransform = crunchConfig.EntropyIdx is EntropyIx.SubGreen or EntropyIx.SpatialSubGreen; this.UseSubtractGreenTransform = crunchConfig.EntropyIdx is EntropyIx.SubGreen or EntropyIx.SpatialSubGreen;
this.UsePredictorTransform = crunchConfig.EntropyIdx is EntropyIx.Spatial 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) if (isFirstConfig || this.bitWriter.NumBytes() < bestSize)
{ {
bestSize = this.bitWriter.NumBytes(); 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. // Reset the bit writer for the following iteration if any.
@ -430,7 +430,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
isFirstConfig = false; isFirstConfig = false;
} }
this.BitWriterSwap(ref bitWriterBest, ref this.bitWriter); BitWriterSwap(ref bitWriterBest, ref this.bitWriter);
} }
/// <summary> /// <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); EntropyIx entropyIdx = this.AnalyzeEntropy(bgra, width, height, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero);
bool doNotCache = false; bool doNotCache = false;
var crunchConfigs = new List<CrunchConfig>(); List<CrunchConfig> crunchConfigs = new();
if (this.method == WebpEncodingMethod.BestQuality && this.quality == 100) if (this.method == WebpEncodingMethod.BestQuality && this.quality == 100)
{ {
@ -540,7 +540,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Span<uint> bgra = this.EncodedData.GetSpan(); Span<uint> bgra = this.EncodedData.GetSpan();
int histogramImageXySize = LosslessUtils.SubSampleSize(width, this.HistoBits) * LosslessUtils.SubSampleSize(height, this.HistoBits); int histogramImageXySize = LosslessUtils.SubSampleSize(width, this.HistoBits) * LosslessUtils.SubSampleSize(height, this.HistoBits);
ushort[] histogramSymbols = new ushort[histogramImageXySize]; 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++) for (int i = 0; i < huffTree.Length; i++)
{ {
huffTree[i] = default; huffTree[i] = default;
@ -583,8 +583,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0]; Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
this.bitWriter.Reset(bwInit); this.bitWriter.Reset(bwInit);
var tmpHisto = new Vp8LHistogram(cacheBits); Vp8LHistogram tmpHisto = new(cacheBits);
var histogramImage = new List<Vp8LHistogram>(histogramImageXySize); List<Vp8LHistogram> histogramImage = new(histogramImageXySize);
for (int i = 0; i < histogramImageXySize; i++) for (int i = 0; i < histogramImageXySize; i++)
{ {
histogramImage.Add(new Vp8LHistogram(cacheBits)); 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. // Create Huffman bit lengths and codes for each histogram image.
int histogramImageSize = histogramImage.Count; int histogramImageSize = histogramImage.Count;
int bitArraySize = 5 * histogramImageSize; int bitArraySize = 5 * histogramImageSize;
var huffmanCodes = new HuffmanTreeCode[bitArraySize]; HuffmanTreeCode[] huffmanCodes = new HuffmanTreeCode[bitArraySize];
for (int i = 0; i < huffmanCodes.Length; i++) for (int i = 0; i < huffmanCodes.Length; i++)
{ {
huffmanCodes[i] = default; 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++) for (int i = 0; i < tokens.Length; i++)
{ {
tokens[i] = new HuffmanTreeToken(); tokens[i] = new HuffmanTreeToken();
@ -767,13 +767,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int cacheBits = 0; int cacheBits = 0;
ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol. 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++) for (int i = 0; i < huffmanCodes.Length; i++)
{ {
huffmanCodes[i] = default; 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++) for (int i = 0; i < huffTree.Length; i++)
{ {
huffTree[i] = default; huffTree[i] = default;
@ -794,7 +794,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
refsTmp1, refsTmp1,
refsTmp2); refsTmp2);
var histogramImage = new List<Vp8LHistogram>() List<Vp8LHistogram> histogramImage = new()
{ {
new(cacheBits) 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++) for (int i = 0; i < tokens.Length; i++)
{ {
tokens[i] = new HuffmanTreeToken(); tokens[i] = new HuffmanTreeToken();
@ -842,8 +842,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int count = 0; int count = 0;
Span<int> symbols = this.scratch.AsSpan(0, 2); Span<int> symbols = this.scratch.AsSpan(0, 2);
symbols.Clear(); symbols.Clear();
int maxBits = 8; const int maxBits = 8;
int maxSymbol = 1 << maxBits; const int maxSymbol = 1 << maxBits;
// Check whether it's a small tree. // Check whether it's a small tree.
for (int i = 0; i < huffmanCode.NumSymbols && count < 3; i++) for (int i = 0; i < huffmanCode.NumSymbols && count < 3; i++)
@ -896,7 +896,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int i; int i;
byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes];
short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes]; short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes];
var huffmanCode = new HuffmanTreeCode HuffmanTreeCode huffmanCode = new()
{ {
NumSymbols = WebpConstants.CodeLengthCodes, NumSymbols = WebpConstants.CodeLengthCodes,
CodeLengths = codeLengthBitDepth, CodeLengths = codeLengthBitDepth,
@ -1099,7 +1099,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint pix = currentRow[x]; uint pix = currentRow[x];
uint pixDiff = LosslessUtils.SubPixels(pix, pixPrev); uint pixDiff = LosslessUtils.SubPixels(pix, pixPrev);
pixPrev = pix; pixPrev = pix;
if (pixDiff == 0 || (prevRow != null && pix == prevRow[x])) if (pixDiff == 0 || (prevRow.Length > 0 && pix == prevRow[x]))
{ {
continue; continue;
} }
@ -1147,7 +1147,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
histo[(int)HistoIx.HistoBluePred * 256]++; histo[(int)HistoIx.HistoBluePred * 256]++;
histo[(int)HistoIx.HistoAlphaPred * 256]++; histo[(int)HistoIx.HistoAlphaPred * 256]++;
var bitEntropy = new Vp8LBitEntropy(); Vp8LBitEntropy bitEntropy = new();
for (int j = 0; j < (int)HistoIx.HistoTotal; j++) for (int j = 0; j < (int)HistoIx.HistoTotal; j++)
{ {
bitEntropy.Init(); bitEntropy.Init();
@ -1239,7 +1239,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private bool AnalyzeAndCreatePalette(ReadOnlySpan<uint> bgra, int width, int height) private bool AnalyzeAndCreatePalette(ReadOnlySpan<uint> bgra, int width, int height)
{ {
Span<uint> palette = this.Palette.Memory.Span; 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) if (this.PaletteSize > WebpConstants.MaxPaletteSize)
{ {
this.PaletteSize = 0; this.PaletteSize = 0;
@ -1265,9 +1265,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="palette">The span to store the palette into.</param> /// <param name="palette">The span to store the palette into.</param>
/// <returns>The number of palette entries.</returns> /// <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++) for (int y = 0; y < height; y++)
{ {
ReadOnlySpan<uint> bgraRow = bgra.Slice(y * width, width); 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> /// <returns>True, if the palette has no monotonous deltas.</returns>
private static bool PaletteHasNonMonotonousDeltas(Span<uint> palette, int numColors) private static bool PaletteHasNonMonotonousDeltas(Span<uint> palette, int numColors)
{ {
uint predict = 0x000000; const uint predict = 0x000000;
byte signFound = 0x00; byte signFound = 0x00;
for (int i = 0; i < numColors; i++) for (int i = 0; i < numColors; i++)
{ {
@ -1637,7 +1637,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Create Huffman trees. // Create Huffman trees.
bool[] bufRle = new bool[maxNumSymbols]; 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++) for (int i = 0; i < huffTree.Length; i++)
{ {
huffTree[i] = default; huffTree[i] = default;
@ -1665,7 +1665,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static uint PaletteColorDistance(uint col1, uint col2) private static uint PaletteColorDistance(uint col1, uint col2)
{ {
uint diff = LosslessUtils.SubPixels(col1, col2); uint diff = LosslessUtils.SubPixels(col1, col2);
uint moreWeightForRGBThanForAlpha = 9; const uint moreWeightForRGBThanForAlpha = 9;
uint score = PaletteComponentDistance((diff >> 0) & 0xff); uint score = PaletteComponentDistance((diff >> 0) & 0xff);
score += PaletteComponentDistance((diff >> 8) & 0xff); score += PaletteComponentDistance((diff >> 8) & 0xff);
score += PaletteComponentDistance((diff >> 16) & 0xff); score += PaletteComponentDistance((diff >> 16) & 0xff);
@ -1730,7 +1730,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
[MethodImpl(InliningOptions.ShortMethod)] [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; Vp8LBitWriter tmp = src;
src = dst; 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) public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : unmanaged, IPixel<TPixel> 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.DecodeImageStream(decoder, width, height, true);
this.DecodeImageData(decoder, decoder.Pixels.Memory.Span); this.DecodeImageData(decoder, decoder.Pixels.Memory.Span);
@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
decoder.Metadata.ColorCacheSize = 0; decoder.Metadata.ColorCacheSize = 0;
} }
this.UpdateDecoder(decoder, transformXSize, transformYSize); UpdateDecoder(decoder, transformXSize, transformYSize);
if (isLevel0) if (isLevel0)
{ {
// level 0 complete. // level 0 complete.
@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void DecodeImageData(Vp8LDecoder decoder, Span<uint> pixelData) public void DecodeImageData(Vp8LDecoder decoder, Span<uint> pixelData)
{ {
int lastPixel = 0; const int lastPixel = 0;
int width = decoder.Width; int width = decoder.Width;
int height = decoder.Height; int height = decoder.Height;
int row = lastPixel / width; int row = lastPixel / width;
@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (hTreeGroup[0].IsTrivialCode) if (hTreeGroup[0].IsTrivialCode)
{ {
pixelData[decodedPixels] = hTreeGroup[0].LiteralArb; 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; continue;
} }
@ -248,7 +248,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (code == PackedNonLiteralCode) 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; 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); 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) else if (code < lenCodeLimit)
{ {
@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
pixelData[decodedPixels] = colorCache.Lookup(key); 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 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++; col++;
decodedPixels++; decodedPixels++;
@ -414,8 +414,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
int tableSize = TableSize[colorCacheBits]; int tableSize = TableSize[colorCacheBits];
var huffmanTables = new HuffmanCode[numHTreeGroups * tableSize]; HuffmanCode[] huffmanTables = new HuffmanCode[numHTreeGroups * tableSize];
var hTreeGroups = new HTreeGroup[numHTreeGroups]; HTreeGroup[] hTreeGroups = new HTreeGroup[numHTreeGroups];
Span<HuffmanCode> huffmanTable = huffmanTables.AsSpan(); Span<HuffmanCode> huffmanTable = huffmanTables.AsSpan();
int[] codeLengths = new int[maxAlphabetSize]; int[] codeLengths = new int[maxAlphabetSize];
for (int i = 0; i < numHTreeGroupsMax; i++) for (int i = 0; i < numHTreeGroupsMax; i++)
@ -488,7 +488,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits; hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && maxBits < HuffmanUtils.HuffmanPackedBits;
if (hTreeGroup.UsePackedTable) if (hTreeGroup.UsePackedTable)
{ {
this.BuildPackedTable(hTreeGroup); BuildPackedTable(hTreeGroup);
} }
} }
@ -546,9 +546,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.ReadHuffmanCodeLengths(table, codeLengthCodeLengths, alphabetSize, codeLengths); this.ReadHuffmanCodeLengths(table, codeLengthCodeLengths, alphabetSize, codeLengths);
} }
int size = HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize); return HuffmanUtils.BuildHuffmanTable(table, HuffmanUtils.HuffmanTableBits, codeLengths, alphabetSize);
return size;
} }
private void ReadHuffmanCodeLengths(Span<HuffmanCode> table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) 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> /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder) private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
{ {
var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2); Vp8LTransformType transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
var transform = new Vp8LTransform(transformType, xSize, ySize); Vp8LTransform transform = new(transformType, xSize, ySize);
// Each transform is allowed to be used only once. // Each transform is allowed to be used only once.
foreach (Vp8LTransform decoderTransform in decoder.Transforms) 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. // The transform data contains color table size and the entries in the color table.
// 8 bit value for color table size. // 8 bit value for color table size.
uint numColors = this.bitReader.ReadValue(8) + 1; uint numColors = this.bitReader.ReadValue(8) + 1;
int bits = numColors > 16 ? 0 if (numColors > 16)
: numColors > 4 ? 1 {
: numColors > 2 ? 2 transform.Bits = 0;
: 3; }
transform.Bits = bits; 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)) using (IMemoryOwner<uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
{ {
int finalNumColors = 1 << (8 >> transform.Bits); int finalNumColors = 1 << (8 >> transform.Bits);
@ -660,15 +670,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
case Vp8LTransformType.CrossColorTransform: case Vp8LTransformType.CrossColorTransform:
{
// The first 3 bits of prediction data define the block width and height in number of bits. // 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; transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits); int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits); int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
IMemoryOwner<uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false); transform.Data = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
transform.Data = transformData;
break; break;
}
} }
decoder.Transforms.Add(transform); decoder.Transforms.Add(transform);
@ -687,8 +695,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
for (int i = transforms.Count - 1; i >= 0; i--) for (int i = transforms.Count - 1; i >= 0; i--)
{ {
Vp8LTransform transform = transforms[i]; Vp8LTransform transform = transforms[i];
Vp8LTransformType transformType = transform.TransformType; switch (transform.TransformType)
switch (transformType)
{ {
case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.PredictorTransform:
using (IMemoryOwner<uint> output = memoryAllocator.Allocate<uint>(pixelData.Length, AllocationOptions.Clean)) 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); 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; int numBits = decoder.Metadata.HuffmanSubSampleBits;
decoder.Width = width; decoder.Width = width;
@ -831,7 +838,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return code.Value; return code.Value;
} }
private void BuildPackedTable(HTreeGroup hTreeGroup) private static void BuildPackedTable(HTreeGroup hTreeGroup)
{ {
for (uint code = 0; code < HuffmanUtils.HuffmanPackedTableSize; code++) 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. /// 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. /// FillBitWindow() needs to be called at minimum every second call to ReadSymbol, in order to pre-fetch enough bits.
/// </summary> /// </summary>
/// <param name="table">The Huffman table.</param>
private uint ReadSymbol(Span<HuffmanCode> table) private uint ReadSymbol(Span<HuffmanCode> table)
{ {
uint val = (uint)this.bitReader.PrefetchBits(); 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> /// </summary>
private readonly int predsWidth; 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 = private readonly byte[] vp8TopLeftI4 =
{ {
17, 21, 25, 29, 17, 21, 25, 29,
@ -46,9 +48,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private int currentMbIdx; private int currentMbIdx;
private int nzIdx; private int nzIdx;
private int predIdx;
private int yTopIdx; private int yTopIdx;
private int uvTopIdx; private int uvTopIdx;
@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.yTopIdx = 0; this.yTopIdx = 0;
this.uvTopIdx = 0; this.uvTopIdx = 0;
this.predsWidth = (4 * mbw) + 1; this.predsWidth = (4 * mbw) + 1;
this.predIdx = this.predsWidth; this.PredIdx = this.predsWidth;
this.YuvIn = new byte[WebpConstants.Bps * 16]; this.YuvIn = new byte[WebpConstants.Bps * 16];
this.YuvOut = new byte[WebpConstants.Bps * 16]; this.YuvOut = new byte[WebpConstants.Bps * 16];
this.YuvOut2 = 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]; this.Scratch3 = new int[16];
// To match the C initial values of the reference implementation, initialize all with 204. // 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.YuvIn.AsSpan().Fill(defaultInitVal);
this.YuvOut.AsSpan().Fill(defaultInitVal); this.YuvOut.AsSpan().Fill(defaultInitVal);
this.YuvOut2.AsSpan().Fill(defaultInitVal); this.YuvOut2.AsSpan().Fill(defaultInitVal);
@ -160,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary> /// <summary>
/// Gets the current start index of the intra mode predictors. /// Gets the current start index of the intra mode predictors.
/// </summary> /// </summary>
public int PredIdx => this.predIdx; public int PredIdx { get; private set; }
/// <summary> /// <summary>
/// Gets the non-zero pattern. /// Gets the non-zero pattern.
@ -238,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void Init() => this.Reset(); public void Init() => this.Reset();
public void InitFilter() public static void InitFilter()
{ {
// TODO: add support for autofilter // TODO: add support for autofilter
} }
@ -299,9 +298,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> yuvIn = this.YuvIn.AsSpan(YOffEnc); Span<byte> yuvIn = this.YuvIn.AsSpan(YOffEnc);
Span<byte> uIn = this.YuvIn.AsSpan(UOffEnc); Span<byte> uIn = this.YuvIn.AsSpan(UOffEnc);
Span<byte> vIn = this.YuvIn.AsSpan(VOffEnc); Span<byte> vIn = this.YuvIn.AsSpan(VOffEnc);
this.ImportBlock(ySrc, yStride, yuvIn, w, h, 16); ImportBlock(ySrc, yStride, yuvIn, w, h, 16);
this.ImportBlock(uSrc, uvStride, uIn, uvw, uvh, 8); ImportBlock(uSrc, uvStride, uIn, uvw, uvh, 8);
this.ImportBlock(vSrc, uvStride, vIn, uvw, uvh, 8); ImportBlock(vSrc, uvStride, vIn, uvw, uvh, 8);
if (!importBoundarySamples) if (!importBoundarySamples)
{ {
@ -331,9 +330,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
vLeft[0] = v[uvStartIdx - 1 - uvStride]; vLeft[0] = v[uvStartIdx - 1 - uvStride];
} }
this.ImportLine(y[(yStartIdx - 1)..], yStride, yLeft[1..], h, 16); ImportLine(y[(yStartIdx - 1)..], yStride, yLeft[1..], h, 16);
this.ImportLine(u[(uvStartIdx - 1)..], uvStride, uLeft[1..], uvh, 8); ImportLine(u[(uvStartIdx - 1)..], uvStride, uLeft[1..], uvh, 8);
this.ImportLine(v[(uvStartIdx - 1)..], uvStride, vLeft[1..], uvh, 8); ImportLine(v[(uvStartIdx - 1)..], uvStride, vLeft[1..], uvh, 8);
} }
Span<byte> yTop = this.YTop.AsSpan(this.yTopIdx, 16); Span<byte> yTop = this.YTop.AsSpan(this.yTopIdx, 16);
@ -344,9 +343,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
else else
{ {
this.ImportLine(y[(yStartIdx - yStride)..], 1, yTop, w, 16); ImportLine(y[(yStartIdx - yStride)..], 1, yTop, w, 16);
this.ImportLine(u[(uvStartIdx - uvStride)..], 1, this.UvTop.AsSpan(this.uvTopIdx, 8), uvw, 8); 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(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() public int MbAnalyzeBestIntra16Mode()
{ {
int maxMode = MaxIntra16Mode; const int maxMode = MaxIntra16Mode;
int mode; int mode;
int bestAlpha = DefaultAlpha; int bestAlpha = DefaultAlpha;
int bestMode = 0; int bestMode = 0;
@ -394,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.MakeLuma16Preds(); this.MakeLuma16Preds();
for (mode = 0; mode < maxMode; mode++) 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); histo.CollectHistogram(this.YuvIn.AsSpan(YOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]), 0, 16);
int alpha = histo.GetAlpha(); int alpha = histo.GetAlpha();
if (alpha > bestAlpha) if (alpha > bestAlpha)
@ -411,15 +410,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int MbAnalyzeBestIntra4Mode(int bestAlpha) public int MbAnalyzeBestIntra4Mode(int bestAlpha)
{ {
byte[] modes = new byte[16]; byte[] modes = new byte[16];
int maxMode = MaxIntra4Mode; const int maxMode = MaxIntra4Mode;
var totalHisto = new Vp8Histogram(); Vp8Histogram totalHisto = new();
int curHisto = 0; int curHisto = 0;
this.StartI4(); this.StartI4();
do do
{ {
int mode; int mode;
int bestModeAlpha = DefaultAlpha; int bestModeAlpha = DefaultAlpha;
var histos = new Vp8Histogram[2]; Vp8Histogram[] histos = new Vp8Histogram[2];
Span<byte> src = this.YuvIn.AsSpan(YOffEnc + WebpLookupTables.Vp8Scan[this.I4]); Span<byte> src = this.YuvIn.AsSpan(YOffEnc + WebpLookupTables.Vp8Scan[this.I4]);
this.MakeIntra4Preds(); this.MakeIntra4Preds();
@ -459,13 +458,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int bestAlpha = DefaultAlpha; int bestAlpha = DefaultAlpha;
int smallestAlpha = 0; int smallestAlpha = 0;
int bestMode = 0; int bestMode = 0;
int maxMode = MaxUvMode; const int maxMode = MaxUvMode;
int mode; int mode;
this.MakeChroma8Preds(); this.MakeChroma8Preds();
for (mode = 0; mode < maxMode; ++mode) 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); histo.CollectHistogram(this.YuvIn.AsSpan(UOffEnc), this.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]), 16, 16 + 4 + 4);
int alpha = histo.GetAlpha(); int alpha = histo.GetAlpha();
if (alpha > bestAlpha) if (alpha > bestAlpha)
@ -487,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetIntra16Mode(int mode) 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++) for (int y = 0; y < 4; y++)
{ {
preds[..4].Fill((byte)mode); preds[..4].Fill((byte)mode);
@ -500,7 +499,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public void SetIntra4Mode(byte[] modes) public void SetIntra4Mode(byte[] modes)
{ {
int modesIdx = 0; int modesIdx = 0;
int predIdx = this.predIdx; int predIdx = this.PredIdx;
for (int y = 4; y > 0; y--) for (int y = 4; y > 0; y--)
{ {
modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx)); modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx));
@ -542,7 +541,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public short[] GetCostModeI4(byte[] modes) public short[] GetCostModeI4(byte[] modes)
{ {
int predsWidth = this.predsWidth; int predsWidth = this.predsWidth;
int predIdx = this.predIdx; int predIdx = this.PredIdx;
int x = this.I4 & 3; int x = this.I4 & 3;
int y = this.I4 >> 2; int y = this.I4 >> 2;
int left = x == 0 ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1]; 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.currentMbIdx++;
this.nzIdx++; this.nzIdx++;
this.predIdx += 4; this.PredIdx += 4;
this.yTopIdx += 16; this.yTopIdx += 16;
this.uvTopIdx += 16; this.uvTopIdx += 16;
} }
@ -772,35 +771,35 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<int> leftNz = this.LeftNz; Span<int> leftNz = this.LeftNz;
// Top-Y // Top-Y
topNz[0] = this.Bit(tnz, 12); topNz[0] = Bit(tnz, 12);
topNz[1] = this.Bit(tnz, 13); topNz[1] = Bit(tnz, 13);
topNz[2] = this.Bit(tnz, 14); topNz[2] = Bit(tnz, 14);
topNz[3] = this.Bit(tnz, 15); topNz[3] = Bit(tnz, 15);
// Top-U // Top-U
topNz[4] = this.Bit(tnz, 18); topNz[4] = Bit(tnz, 18);
topNz[5] = this.Bit(tnz, 19); topNz[5] = Bit(tnz, 19);
// Top-V // Top-V
topNz[6] = this.Bit(tnz, 22); topNz[6] = Bit(tnz, 22);
topNz[7] = this.Bit(tnz, 23); topNz[7] = Bit(tnz, 23);
// DC // DC
topNz[8] = this.Bit(tnz, 24); topNz[8] = Bit(tnz, 24);
// left-Y // left-Y
leftNz[0] = this.Bit(lnz, 3); leftNz[0] = Bit(lnz, 3);
leftNz[1] = this.Bit(lnz, 7); leftNz[1] = Bit(lnz, 7);
leftNz[2] = this.Bit(lnz, 11); leftNz[2] = Bit(lnz, 11);
leftNz[3] = this.Bit(lnz, 15); leftNz[3] = Bit(lnz, 15);
// left-U // left-U
leftNz[4] = this.Bit(lnz, 17); leftNz[4] = Bit(lnz, 17);
leftNz[5] = this.Bit(lnz, 19); leftNz[5] = Bit(lnz, 19);
// left-V // left-V
leftNz[6] = this.Bit(lnz, 21); leftNz[6] = Bit(lnz, 21);
leftNz[7] = this.Bit(lnz, 23); leftNz[7] = Bit(lnz, 23);
// left-DC is special, iterated separately. // left-DC is special, iterated separately.
} }
@ -826,7 +825,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Nz[this.nzIdx] = nz; 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 dstIdx = 0;
int srcIdx = 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 i;
int srcIdx = 0; 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.nzIdx = 1; // note: in reference source nz starts at -1.
this.yTopIdx = 0; this.yTopIdx = 0;
this.uvTopIdx = 0; this.uvTopIdx = 0;
this.predIdx = this.predsWidth + (y * 4 * this.predsWidth); this.PredIdx = this.predsWidth + (y * 4 * this.predsWidth);
this.InitLeft(); this.InitLeft();
} }
@ -931,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.TopDerr.AsSpan().Clear(); 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> /// <summary>
/// Set count down. /// 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.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100); this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
this.alphaCompression = alphaCompression; this.alphaCompression = alphaCompression;
this.rdOptLevel = method is WebpEncodingMethod.BestQuality ? Vp8RdLevel.RdOptTrellisAll if (method is WebpEncodingMethod.BestQuality)
: method >= WebpEncodingMethod.Level5 ? Vp8RdLevel.RdOptTrellis {
: method >= WebpEncodingMethod.Level3 ? Vp8RdLevel.RdOptBasic this.rdOptLevel = Vp8RdLevel.RdOptTrellisAll;
: Vp8RdLevel.RdOptNone; }
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; int pixelCount = width * height;
this.Mbw = (width + 15) >> 4; this.Mbw = (width + 15) >> 4;
@ -142,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.TopDerr = new sbyte[this.Mbw * 4]; this.TopDerr = new sbyte[this.Mbw * 4];
// TODO: make partition_limit configurable? // 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 = this.maxI4HeaderBits =
256 * 16 * 16 * limit * limit / (100 * 100); // ... modulated with a quadratic curve. 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 yStride = width;
int uvStride = (yStride + 1) >> 1; 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]; int[] alphas = new int[WebpConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha); this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
int totalMb = this.Mbw * this.Mbw; int totalMb = this.Mbw * this.Mbw;
@ -327,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Extract and encode alpha channel data, if present. // Extract and encode alpha channel data, if present.
int alphaDataSize = 0; int alphaDataSize = 0;
bool alphaCompressionSucceeded = false; bool alphaCompressionSucceeded = false;
using var alphaEncoder = new AlphaEncoder(); using AlphaEncoder alphaEncoder = new();
Span<byte> alphaData = Span<byte>.Empty; Span<byte> alphaData = Span<byte>.Empty;
if (hasAlpha) if (hasAlpha)
{ {
@ -344,9 +356,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Stats-collection loop. // Stats-collection loop.
this.StatLoop(width, height, yStride, uvStride); this.StatLoop(width, height, yStride, uvStride);
it.Init(); it.Init();
it.InitFilter(); Vp8EncIterator.InitFilter();
var info = new Vp8ModeScore(); Vp8ModeScore info = new();
var residual = new Vp8Residual(); Vp8Residual residual = new();
do do
{ {
bool dontUseSkip = !this.Proba.UseSkipProba; 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 /// This is used for deciding optimal probabilities. It also modifies the
/// quantizer value if some target (size, PSNR) was specified. /// quantizer value if some target (size, PSNR) was specified.
/// </summary> /// </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) private void StatLoop(int width, int height, int yStride, int uvStride)
{ {
int targetSize = 0; // TODO: target size is hardcoded. const int targetSize = 0; // TODO: target size is hardcoded.
float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded. const float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded.
bool doSearch = targetSize > 0 || targetPsnr > 0; const bool doSearch = targetSize > 0 || targetPsnr > 0;
bool fastProbe = (this.method == 0 || this.method == WebpEncodingMethod.Level3) && !doSearch; bool fastProbe = (this.method == 0 || this.method == WebpEncodingMethod.Level3) && !doSearch;
int numPassLeft = this.entropyPasses; int numPassLeft = this.entropyPasses;
Vp8RdLevel rdOpt = this.method >= WebpEncodingMethod.Level3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone; Vp8RdLevel rdOpt = this.method >= WebpEncodingMethod.Level3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
int nbMbs = this.Mbw * this.Mbh; 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(); this.Proba.ResetTokenStats();
// Fast mode: quick analysis pass over few mbs. Better than nothing. // 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 no target size: just do several pass without changing 'q'
if (doSearch) if (doSearch)
{ {
// Unreachable due to hardcoding above.
#pragma warning disable CS0162 // Unreachable code detected
stats.ComputeNextQ(); stats.ComputeNextQ();
#pragma warning restore CS0162 // Unreachable code detected
if (MathF.Abs(stats.Dq) <= DqLimit) if (MathF.Abs(stats.Dq) <= DqLimit)
{ {
break; break;
@ -474,7 +493,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> y = this.Y.GetSpan(); Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan(); Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.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 size = 0;
long sizeP0 = 0; long sizeP0 = 0;
long distortion = 0; long distortion = 0;
@ -482,7 +501,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
it.Init(); it.Init();
this.SetLoopParams(stats.Q); this.SetLoopParams(stats.Q);
var info = new Vp8ModeScore(); Vp8ModeScore info = new();
do do
{ {
info.Clear(); info.Clear();
@ -540,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// this '>> 3' accounts for some inverse WHT scaling // this '>> 3' accounts for some inverse WHT scaling
int delta = (dqm.MaxEdge * dqm.Y2.Q[1]) >> 3; 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) if (level > dqm.FStrength)
{ {
dqm.FStrength = level; dqm.FStrength = level;
@ -757,8 +776,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void SetupFilterStrength() private void SetupFilterStrength()
{ {
int filterSharpness = 0; // TODO: filterSharpness is hardcoded const int filterSharpness = 0; // TODO: filterSharpness is hardcoded
int filterType = 1; // TODO: filterType is hardcoded const int filterType = 1; // TODO: filterType is hardcoded
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering. // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
int level0 = 5 * this.filterStrength; int level0 = 5 * this.filterStrength;
@ -768,11 +787,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// We focus on the quantization of AC coeffs. // We focus on the quantization of AC coeffs.
int qstep = WebpLookupTables.AcTable[Numerics.Clamp(m.Quant, 0, 127)] >> 2; 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. // Segments with lower complexity ('beta') will be less filtered.
int f = baseStrength * level0 / (256 + m.Beta); 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). // 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. /// Same as CodeResiduals, but doesn't actually write anything.
/// Instead, it just records the event distribution. /// Instead, it just records the event distribution.
/// </summary> /// </summary>
/// <param name="it">The iterator.</param>
/// <param name="rd">The score accumulator.</param>
private void RecordResiduals(Vp8EncIterator it, Vp8ModeScore rd) private void RecordResiduals(Vp8EncIterator it, Vp8ModeScore rd)
{ {
int x, y, ch; int x, y, ch;
var residual = new Vp8Residual(); Vp8Residual residual = new();
bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
it.NzToBytes(); 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 /// is around q=75. Internally, our "good" middle is around c=50. So we
/// map accordingly using linear piece-wise function /// map accordingly using linear piece-wise function
/// </summary> /// </summary>
/// <param name="c">The compression level.</param>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static double QualityToCompression(double c) 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 // this power-law: quant ~= compression ^ 1/3. This law holds well for
// low quant. Finer modeling for high-quant would make use of AcTable[] // low quant. Finer modeling for high-quant would make use of AcTable[]
// more explicitly. // more explicitly.
double v = Math.Pow(linearC, 1 / 3.0d); return (double)Math.Pow(linearC, 1 / 3.0d);
return v;
} }
[MethodImpl(InliningOptions.ShortMethod)] [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; int pos = delta < WebpConstants.MaxDelzaSize ? delta : WebpConstants.MaxDelzaSize - 1;
return WebpLookupTables.LevelsFromDelta[sharpness, pos]; 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. // Paragraph 9.2: color space and clamp type follow.
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1); sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1);
sbyte clampType = (sbyte)this.bitReader.ReadValue(1); sbyte clampType = (sbyte)this.bitReader.ReadValue(1);
var pictureHeader = new Vp8PictureHeader() Vp8PictureHeader pictureHeader = new()
{ {
Width = (uint)width, Width = (uint)width,
Height = (uint)height, Height = (uint)height,
@ -83,10 +83,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}; };
// Paragraph 9.3: Parse the segment header. // Paragraph 9.3: Parse the segment header.
var proba = new Vp8Proba(); Vp8Proba proba = new();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba); 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); Vp8Io io = InitializeVp8Io(decoder, pictureHeader);
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (info.Features?.Alpha == true) if (info.Features?.Alpha == true)
{ {
using (var alphaDecoder = new AlphaDecoder( using (AlphaDecoder alphaDecoder = new(
width, width,
height, height,
alphaData, alphaData,
@ -120,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.configuration)) this.configuration))
{ {
alphaDecoder.Decode(); alphaDecoder.Decode();
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha); DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha);
} }
} }
else 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> where TPixel : unmanaged, IPixel<TPixel>
{ {
TPixel color = default; TPixel color = default;
@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
// Prepare for next scanline. // Prepare for next scanline.
this.InitScanline(dec); InitScanline(dec);
// Reconstruct, filter and emit the row. // Reconstruct, filter and emit the row.
this.ProcessRow(dec, io); this.ProcessRow(dec, io);
@ -222,9 +222,27 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (!block.IsI4x4) if (!block.IsI4x4)
{ {
// Hardcoded 16x16 intra-mode decision tree. // Hardcoded 16x16 intra-mode decision tree.
int yMode = this.bitReader.GetBit(156) != 0 ? int yMode;
this.bitReader.GetBit(128) != 0 ? (int)IntraPredictionMode.TrueMotion : (int)IntraPredictionMode.HPrediction : if (this.bitReader.GetBit(156) != 0)
this.bitReader.GetBit(163) != 0 ? (int)IntraPredictionMode.VPrediction : (int)IntraPredictionMode.DcPrediction; {
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; block.Modes[0] = (byte)yMode;
for (int i = 0; i < left.Length; i++) for (int i = 0; i < left.Length; i++)
{ {
@ -258,12 +276,29 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
// Hardcoded UVMode decision tree. // Hardcoded UVMode decision tree.
block.UvMode = (byte)(this.bitReader.GetBit(142) == 0 ? 0 : if (this.bitReader.GetBit(142) == 0)
this.bitReader.GetBit(114) == 0 ? 2 : {
this.bitReader.GetBit(183) != 0 ? 1 : 3); // 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; Vp8MacroBlock left = dec.LeftMacroBlock;
left.NoneZeroAcDcCoeffs = 0; left.NoneZeroAcDcCoeffs = 0;
@ -279,7 +314,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void ProcessRow(Vp8Decoder dec, Vp8Io io) private void ProcessRow(Vp8Decoder dec, Vp8Io io)
{ {
this.ReconstructRow(dec); this.ReconstructRow(dec);
this.FinishRow(dec, io); FinishRow(dec, io);
} }
private void ReconstructRow(Vp8Decoder dec) private void ReconstructRow(Vp8Decoder dec)
@ -404,8 +439,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
int offset = yOff + WebpConstants.Scan[n]; int offset = yOff + WebpConstants.Scan[n];
Span<byte> dst = yuv[offset..]; Span<byte> dst = yuv[offset..];
byte lumaMode = block.Modes[n]; switch (block.Modes[n])
switch (lumaMode)
{ {
case 0: case 0:
LossyUtils.DC4(dst, yuv, offset); LossyUtils.DC4(dst, yuv, offset);
@ -439,14 +473,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
break; break;
} }
this.DoTransform(bits, coeffs.AsSpan(n * 16), dst, this.scratch); DoTransform(bits, coeffs.AsSpan(n * 16), dst, this.scratch);
} }
} }
else else
{ {
// 16x16 // 16x16
int mode = CheckMode(mbx, mby, block.Modes[0]); switch (CheckMode(mbx, mby, block.Modes[0]))
switch (mode)
{ {
case 0: case 0:
LossyUtils.DC16(yDst, yuv, yOff); LossyUtils.DC16(yDst, yuv, yOff);
@ -475,15 +508,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{ {
for (int n = 0; n < 16; ++n, bits <<= 2) 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 // Chroma
uint bitsUv = block.NonZeroUv; uint bitsUv = block.NonZeroUv;
int chromaMode = CheckMode(mbx, mby, block.UvMode); switch (CheckMode(mbx, mby, block.UvMode))
switch (chromaMode)
{ {
case 0: case 0:
LossyUtils.DC8uv(uDst, yuv, uOff); LossyUtils.DC8uv(uDst, yuv, uOff);
@ -515,8 +547,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
break; break;
} }
this.DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, this.scratch); DoUVTransform(bitsUv, coeffs.AsSpan(16 * 16), uDst, this.scratch);
this.DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, this.scratch); DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst, this.scratch);
// Stash away top samples for next block. // Stash away top samples for next block.
if (mby < dec.MbHeight - 1) 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; int mby = dec.MbY;
for (int mbx = dec.TopLeftMbX; mbx < dec.BottomRightMbX; ++mbx) 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; int yBps = dec.CacheYStride;
Vp8FilterInfo filterInfo = dec.FilterInfo[mbx]; 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 extraYRows = WebpConstants.FilterExtraRows[(int)dec.Filter];
int ySize = extraYRows * dec.CacheYStride; int ySize = extraYRows * dec.CacheYStride;
@ -635,7 +667,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (filterRow) if (filterRow)
{ {
this.FilterRow(dec); FilterRow(dec);
} }
int yStart = mby * 16; int yStart = mby * 16;
@ -669,7 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
io.MbY = yStart; io.MbY = yStart;
io.MbW = io.Width; io.MbW = io.Width;
io.MbH = yEnd - yStart; io.MbH = yEnd - yStart;
this.EmitRgb(dec, io); EmitRgb(dec, io);
} }
// Rotate top samples if needed. // 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; Span<byte> buf = dec.Pixels.Memory.Span;
int numLinesOut = io.MbH; // a priori guess. 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> tmpVBuffer = dec.TmpVBuffer.Memory.Span;
Span<byte> topU = tmpUBuffer; Span<byte> topU = tmpUBuffer;
Span<byte> topV = tmpVBuffer; Span<byte> topV = tmpVBuffer;
int bpp = 3; const int bpp = 3;
int bufferStride = bpp * io.Width; int bufferStride = bpp * io.Width;
int dstStartIdx = io.MbY * bufferStride; int dstStartIdx = io.MbY * bufferStride;
Span<byte> dst = buf[dstStartIdx..]; Span<byte> dst = buf[dstStartIdx..];
@ -753,7 +785,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return numLinesOut; 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) 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? // any non-zero coeff at all?
if ((bits & 0xff) > 0) if ((bits & 0xff) > 0)
@ -845,7 +877,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Parse DC // Parse DC
short[] dc = new short[16]; short[] dc = new short[16];
int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs); 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); mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0);
if (nz > 1) if (nz > 1)
{ {
@ -876,7 +908,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int x = 0; x < 4; x++) for (int x = 0; x < 4; x++)
{ {
int ctx = l + (tnz & 1); 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; l = nz > first ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 7)); tnz = (byte)((tnz >> 1) | (l << 7));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0); 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++) for (int x = 0; x < 2; x++)
{ {
int ctx = l + (tnz & 1); 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; l = nz > 0 ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 3)); tnz = (byte)((tnz >> 1) | (l << 3));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0); nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
@ -929,7 +961,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return (nonZeroY | nonZeroUv) == 0; 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. // Returns the position of the last non-zero coeff plus one.
Vp8ProbaArray p = prob[n].Probabilities[ctx]; Vp8ProbaArray p = prob[n].Probabilities[ctx];
@ -960,7 +992,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
else else
{ {
v = this.GetLargeValue(br, p.Probabilities); v = GetLargeValue(br, p.Probabilities);
p = prob[n + 1].Probabilities[2]; p = prob[n + 1].Probabilities[2];
} }
@ -971,7 +1003,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return 16; 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 // See section 13 - 2: http://tools.ietf.org/html/rfc6386#section-13.2
int v; int v;
@ -986,53 +1018,50 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
v = 3 + br.GetBit(p[5]); 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);
{
v = 5 + br.GetBit(159);
}
else
{
v = 7 + (2 * br.GetBit(165));
v += br.GetBit(145);
}
} }
else else
{ {
int bit1 = br.GetBit(p[8]); v = 7 + (2 * br.GetBit(165));
int bit0 = br.GetBit(p[9 + bit1]); v += br.GetBit(145);
int cat = (2 * bit1) + bit0; }
v = 0; }
byte[] tab = null; else
switch (cat) {
{ int bit1 = br.GetBit(p[8]);
case 0: int bit0 = br.GetBit(p[9 + bit1]);
tab = WebpConstants.Cat3; int cat = (2 * bit1) + bit0;
break; v = 0;
case 1: byte[] tab = null;
tab = WebpConstants.Cat4; switch (cat)
break; {
case 2: case 0:
tab = WebpConstants.Cat5; tab = WebpConstants.Cat3;
break; break;
case 3: case 1:
tab = WebpConstants.Cat6; tab = WebpConstants.Cat4;
break; break;
default: case 2:
WebpThrowHelper.ThrowImageFormatException("VP8 parsing error"); tab = WebpConstants.Cat5;
break; break;
} case 3:
tab = WebpConstants.Cat6;
for (int i = 0; i < tab.Length; i++) break;
{ default:
v += v + br.GetBit(tab[i]); 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; return v;
@ -1040,7 +1069,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba) private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba)
{ {
var vp8SegmentHeader = new Vp8SegmentHeader Vp8SegmentHeader vp8SegmentHeader = new()
{ {
UseSegment = this.bitReader.ReadBool() UseSegment = this.bitReader.ReadBool()
}; };
@ -1055,15 +1084,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
{ {
hasValue = this.bitReader.ReadBool(); hasValue = this.bitReader.ReadBool();
byte quantizeValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0); vp8SegmentHeader.Quantizer[i] = (byte)(hasValue ? this.bitReader.ReadSignedValue(7) : 0);
vp8SegmentHeader.Quantizer[i] = quantizeValue;
} }
for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
{ {
hasValue = this.bitReader.ReadBool(); hasValue = this.bitReader.ReadBool();
byte filterStrengthValue = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0); vp8SegmentHeader.FilterStrength[i] = (byte)(hasValue ? this.bitReader.ReadSignedValue(6) : 0);
vp8SegmentHeader.FilterStrength[i] = filterStrengthValue;
} }
if (vp8SegmentHeader.UpdateMap) if (vp8SegmentHeader.UpdateMap)
@ -1188,10 +1215,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
decoder.DeQuantMatrices[i] = decoder.DeQuantMatrices[0]; decoder.DeQuantMatrices[i] = decoder.DeQuantMatrices[0];
continue; continue;
} }
else
{ q = baseQ0;
q = baseQ0;
}
} }
Vp8QuantMatrix m = decoder.DeQuantMatrices[i]; Vp8QuantMatrix m = decoder.DeQuantMatrices[i];
@ -1228,10 +1253,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (int p = 0; p < WebpConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
byte prob = WebpLookupTables.CoeffsUpdateProba[t, b, c, 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) ? this.bitReader.ReadValue(8)
: WebpLookupTables.DefaultCoeffsProba[t, b, c, p]); : 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) private static Vp8Io InitializeVp8Io(Vp8Decoder dec, Vp8PictureHeader pictureHeader)
{ {
var io = default(Vp8Io); Vp8Io io = default;
io.Width = (int)pictureHeader.Width; io.Width = (int)pictureHeader.Width;
io.Height = (int)pictureHeader.Height; io.Height = (int)pictureHeader.Height;
io.UseScaling = false; io.UseScaling = false;
@ -1311,7 +1335,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz) private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz)
{ {
nzCoeffs <<= 2; 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; return nzCoeffs;
} }
@ -1337,6 +1373,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
[MethodImpl(InliningOptions.ShortMethod)] [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; WebpImageInfo webpInfo = null;
var features = new WebpFeatures(); WebpFeatures features = new();
switch (chunkType) switch (chunkType)
{ {
case WebpChunkType.Vp8: 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); 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; 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. 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; imageFrame = currentFrame;
} }
@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
int frameY = (int)(frameData.Y * 2); int frameY = (int)(frameData.Y * 2);
int frameWidth = (int)frameData.Width; int frameWidth = (int)frameData.Width;
int frameHeight = (int)frameData.Height; 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) if (frameData.DisposalMethod is AnimationDisposalMethod.Dispose)
{ {
@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
using Buffer2D<TPixel> decodedImage = this.DecodeImageData<TPixel>(frameData, webpInfo); 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) 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="meta">The metadata.</param>
/// <param name="duration">The frame duration.</param> /// <param name="duration">The frame duration.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetadata(ImageFrameMetadata meta, uint duration) private static void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
{ {
WebpFrameMetadata frameMetadata = meta.GetWebpMetadata(); WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
frameMetadata.FrameDuration = duration; frameMetadata.FrameDuration = duration;
@ -248,19 +248,19 @@ namespace SixLabors.ImageSharp.Formats.Webp
private Buffer2D<TPixel> DecodeImageData<TPixel>(AnimationFrameData frameData, WebpImageInfo webpInfo) private Buffer2D<TPixel> DecodeImageData<TPixel>(AnimationFrameData frameData, WebpImageInfo webpInfo)
where TPixel : unmanaged, IPixel<TPixel> 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 try
{ {
Buffer2D<TPixel> pixelBufferDecoded = decodedImage.Frames.RootFrame.PixelBuffer; Buffer2D<TPixel> pixelBufferDecoded = decodedImage.Frames.RootFrame.PixelBuffer;
if (webpInfo.IsLossless) 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); losslessDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height);
} }
else 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); lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.alphaData);
} }
@ -278,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
} }
/// <summary> /// <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> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="decodedImage">The decoded image.</param> /// <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="frameY">The frame y coordinate.</param>
/// <param name="frameWidth">The width of the frame.</param> /// <param name="frameWidth">The width of the frame.</param>
/// <param name="frameHeight">The height 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> where TPixel : unmanaged, IPixel<TPixel>
{ {
Buffer2D<TPixel> imageFramePixels = imageFrame.PixelBuffer; Buffer2D<TPixel> imageFramePixels = imageFrame.PixelBuffer;
@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
return; 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); Buffer2DRegion<TPixel> pixelRegion = imageFrame.PixelBuffer.GetRegion(interest);
TPixel backgroundPixel = backgroundColor.ToPixel<TPixel>(); TPixel backgroundPixel = backgroundColor.ToPixel<TPixel>();
pixelRegion.Fill(backgroundPixel); pixelRegion.Fill(backgroundPixel);
@ -354,25 +354,25 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <returns>Animation frame data.</returns> /// <returns>Animation frame data.</returns>
private AnimationFrameData ReadFrameHeader(BufferedReadStream stream) 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. // 3 bytes for the X coordinate of the upper left corner of the frame.
data.X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer); X = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer),
// 3 bytes for the Y coordinate of the upper left corner of the frame. // 3 bytes for the Y coordinate of the upper left corner of the frame.
data.Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer); Y = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer),
// Frame width Minus One. // Frame width Minus One.
data.Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1; Width = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1,
// Frame height Minus One. // Frame height Minus One.
data.Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1; Height = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer) + 1,
// Frame duration. // Frame duration.
data.Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer); Duration = WebpChunkParsingUtils.ReadUnsignedInt24Bit(stream, this.buffer)
};
byte flags = (byte)stream.ReadByte(); byte flags = (byte)stream.ReadByte();
data.DisposalMethod = (flags & 1) == 1 ? AnimationDisposalMethod.Dispose : AnimationDisposalMethod.DoNotDispose; 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; public int HeaderSize => 12;
/// <inheritdoc /> /// <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> /// <summary>
/// Checks, if the header starts with a valid RIFF FourCC. /// Checks, if the header starts with a valid RIFF FourCC.
/// </summary> /// </summary>
/// <param name="header">The header bytes.</param> /// <param name="header">The header bytes.</param>
/// <returns>True, if its a valid RIFF FourCC.</returns> /// <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> /// <summary>
/// Checks if 'WEBP' is present in the header. /// Checks if 'WEBP' is present in the header.
/// </summary> /// </summary>
/// <param name="header">The header bytes.</param> /// <param name="header">The header bytes.</param>
/// <returns>True, if its a webp file.</returns> /// <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; } public Vp8FrameHeader Vp8FrameHeader { get; set; }
/// <summary> /// <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> /// </summary>
public Vp8LBitReader Vp8LBitReader { get; set; } = null; public Vp8LBitReader Vp8LBitReader { get; set; }
/// <summary> /// <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> /// </summary>
public Vp8BitReader Vp8BitReader { get; set; } = null; public Vp8BitReader Vp8BitReader { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()

2
src/ImageSharp/IO/BufferedReadStream.cs

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

4
src/ImageSharp/IO/ChunkedMemoryStream.cs

@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.IO
this.isDisposed = true; this.isDisposed = true;
if (disposing) if (disposing)
{ {
this.ReleaseMemoryChunks(this.memoryChunk); ReleaseMemoryChunks(this.memoryChunk);
} }
this.memoryChunk = null; 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) while (chunk != null)
{ {

17
src/ImageSharp/Image.FromStream.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -546,7 +547,7 @@ namespace SixLabors.ImageSharp
} }
// We want to be able to load images from things like HttpContext.Request.Body // 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); stream.CopyTo(memoryStream, configuration.StreamProcessingBufferSize);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -562,6 +563,7 @@ namespace SixLabors.ImageSharp
/// <param name="action">The action to perform.</param> /// <param name="action">The action to perform.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The <see cref="Task{T}"/>.</returns> /// <returns>The <see cref="Task{T}"/>.</returns>
/// <exception cref="NotSupportedException">Cannot read from the stream.</exception>
internal static async Task<T> WithSeekableStreamAsync<T>( internal static async Task<T> WithSeekableStreamAsync<T>(
DecoderOptions options, DecoderOptions options,
Stream stream, Stream stream,
@ -577,19 +579,16 @@ namespace SixLabors.ImageSharp
} }
Configuration configuration = options.Configuration; 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 // 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 // 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. // 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); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false);
memoryStream.Position = 0; memoryStream.Position = 0;
@ -599,12 +598,12 @@ namespace SixLabors.ImageSharp
[DoesNotReturn] [DoesNotReturn]
private static void ThrowNotLoaded(DecoderOptions options) private static void ThrowNotLoaded(DecoderOptions options)
{ {
var sb = new StringBuilder(); StringBuilder sb = new();
sb.AppendLine("Image cannot be loaded. Available decoders:"); sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in options.Configuration.ImageFormatsManager.ImageDecoders) 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()); throw new UnknownImageFormatException(sb.ToString());

16
src/ImageSharp/Image.WrapMemory.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(metadata, nameof(metadata)); 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"); 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); return new Image<TPixel>(configuration, memorySource, width, height, metadata);
} }
@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp
Guard.NotNull(metadata, nameof(metadata)); 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"); 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); return new Image<TPixel>(configuration, memorySource, width, height, metadata);
} }
@ -231,11 +231,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata)); 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"); 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); return new Image<TPixel>(configuration, memorySource, width, height, metadata);
} }
@ -329,11 +329,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata)); 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"); 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); return new Image<TPixel>(configuration, memorySource, width, height, metadata);
} }
@ -422,9 +422,9 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata)); 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); return new Image<TPixel>(configuration, memorySource, width, height, metadata);
} }

11
src/ImageSharp/ImageExtensions.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -106,12 +107,12 @@ namespace SixLabors.ImageSharp
if (encoder is null) 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:"); sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) 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()); throw new NotSupportedException(sb.ToString());
@ -150,12 +151,12 @@ namespace SixLabors.ImageSharp
if (encoder is null) 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:"); sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) 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()); throw new NotSupportedException(sb.ToString());
@ -182,7 +183,7 @@ namespace SixLabors.ImageSharp
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
using var stream = new MemoryStream(); using MemoryStream stream = new();
source.Save(stream, format); source.Save(stream, format);
// Always available. // 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. // Licensed under the Six Labors Split License.
using System; using System;
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Memory.Internals
// A Monitor to wait/signal when we are low on memory. // A Monitor to wait/signal when we are low on memory.
private static object lowMemoryMonitor; private static object lowMemoryMonitor;
public static readonly UnmanagedMemoryHandle NullHandle = default; public static readonly UnmanagedMemoryHandle NullHandle;
private IntPtr handle; private IntPtr handle;
private int lengthInBytes; private int lengthInBytes;
@ -80,24 +80,17 @@ namespace SixLabors.ImageSharp.Memory.Internals
{ {
handle = Marshal.AllocHGlobal(lengthInBytes); handle = Marshal.AllocHGlobal(lengthInBytes);
} }
catch (OutOfMemoryException) catch (OutOfMemoryException) when (counter < MaxAllocationAttempts)
{ {
// We are low on memory, but expect some memory to be freed soon. // We are low on memory, but expect some memory to be freed soon.
// Block the thread & retry to avoid OOM. // Block the thread & retry to avoid OOM.
if (counter < MaxAllocationAttempts) counter++;
{ Interlocked.Increment(ref totalOomRetries);
counter++;
Interlocked.Increment(ref totalOomRetries); Interlocked.CompareExchange(ref lowMemoryMonitor, new object(), null);
Monitor.Enter(lowMemoryMonitor);
Interlocked.CompareExchange(ref lowMemoryMonitor, new object(), null); Monitor.Wait(lowMemoryMonitor, millisecondsTimeout: 1);
Monitor.Enter(lowMemoryMonitor); Monitor.Exit(lowMemoryMonitor);
Monitor.Wait(lowMemoryMonitor, millisecondsTimeout: 1);
Monitor.Exit(lowMemoryMonitor);
}
else
{
throw;
}
} }
} }

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

Loading…
Cancel
Save