Browse Source

Merge branch 'master' into tiff-codec

pull/1330/head
James Jackson-South 7 years ago
parent
commit
904f5be194
  1. 3
      .gitmodules
  2. 5
      ImageSharp.sln
  3. 103
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  4. 47
      src/ImageSharp/Advanced/AotCompilerTools.cs
  5. 8
      src/ImageSharp/ColorSpaces/CieLab.cs
  6. 5
      src/ImageSharp/ColorSpaces/CieLch.cs
  7. 8
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  8. 8
      src/ImageSharp/ColorSpaces/CieLuv.cs
  9. 2
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  10. 7
      src/ImageSharp/ColorSpaces/CieXyy.cs
  11. 7
      src/ImageSharp/ColorSpaces/CieXyz.cs
  12. 8
      src/ImageSharp/ColorSpaces/Cmyk.cs
  13. 10
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  14. 10
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs
  15. 5
      src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs
  16. 7
      src/ImageSharp/ColorSpaces/Hsl.cs
  17. 7
      src/ImageSharp/ColorSpaces/Hsv.cs
  18. 5
      src/ImageSharp/ColorSpaces/HunterLab.cs
  19. 7
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  20. 7
      src/ImageSharp/ColorSpaces/Lms.cs
  21. 7
      src/ImageSharp/ColorSpaces/Rgb.cs
  22. 7
      src/ImageSharp/ColorSpaces/YCbCr.cs
  23. 5
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  24. 2
      src/ImageSharp/Common/Helpers/TestHelpers.cs
  25. 9
      src/ImageSharp/Common/Helpers/TolerantMath.cs
  26. 37
      src/ImageSharp/Common/Helpers/Vector4Utils.cs
  27. 2
      src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs
  28. 6
      src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
  29. 5
      src/ImageSharp/Common/Tuples/Octet.cs
  30. 22
      src/ImageSharp/Common/Tuples/Vector4Pair.cs
  31. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  32. 36
      src/ImageSharp/Formats/Bmp/BmpConstants.cs
  33. 2
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  34. 584
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  35. 50
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  36. 326
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  37. 51
      src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs
  38. 11
      src/ImageSharp/Formats/Bmp/BmpMetaData.cs
  39. 31
      src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs
  40. 5
      src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
  41. 19
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  42. 19
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  43. 8
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  44. 12
      src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs
  45. 12
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  46. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  47. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  48. 14
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  49. 28
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  50. 14
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  51. 22
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  52. 45
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  53. 23
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs
  54. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  55. 13
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  56. 15
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  57. 3
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs
  58. 19
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  59. 3
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs
  60. 5
      src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  61. 27
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  62. 37
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  63. 5
      src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs
  64. 3
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  65. 3
      src/ImageSharp/Formats/Png/PngConstants.cs
  66. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  67. 41
      src/ImageSharp/ImageSharp.csproj
  68. 2
      src/ImageSharp/Memory/RowInterval.cs
  69. 14
      src/ImageSharp/MetaData/ImageProperty.cs
  70. 6
      src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
  71. 6
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  72. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
  73. 20
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  74. 21
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  75. 11
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  76. 8
      src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs
  77. 11
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs
  78. 5
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs
  79. 5
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs
  80. 17
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs
  81. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs
  82. 11
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs
  83. 5
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs
  84. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs
  85. 31
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs
  86. 31
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs
  87. 28
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs
  88. 28
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs
  89. 17
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs
  90. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs
  91. 13
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs
  92. 18
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs
  93. 10
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs
  94. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs
  95. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs
  96. 11
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs
  97. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs
  98. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs
  99. 17
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs
  100. 8
      src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs

3
.gitmodules

@ -2,3 +2,6 @@
path = tests/Images/External
url = https://github.com/SixLabors/Imagesharp.Tests.Images.git
branch = master
[submodule "standards"]
path = standards
url = https://github.com/SixLabors/Standards

5
ImageSharp.sln

@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
standards\.editorconfig = standards\.editorconfig
.travis.yml = .travis.yml
appveyor.yml = appveyor.yml
.github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md
@ -14,12 +14,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md
ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md
run-tests.ps1 = run-tests.ps1
standards\SixLabors.ruleset = standards\SixLabors.ruleset
standards\stylecop.json = standards\stylecop.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}"

103
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -1,54 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<AssemblyTitle>SixLabors.ImageSharp.Drawing</AssemblyTitle>
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>SixLabors and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<LangVersion>7.3</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>
<PackageId>SixLabors.ImageSharp.Drawing</PackageId>
<PackageTags>Image Draw Shape Path Font</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/SixLabors/ImageSharp</PackageProjectUrl>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/SixLabors/ImageSharp</RepositoryUrl>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<DebugType Condition="$(codecov) != ''">full</DebugType>
<DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-dev000119" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000102" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<AssemblyTitle>SixLabors.ImageSharp.Drawing</AssemblyTitle>
<Authors>SixLabors and contributors</Authors>
<Company>Six Labors</Company>
<Copyright>Copyright (c) Six Labors and contributors.</Copyright>
<Product>SixLabors.ImageSharp</Product>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<NeutralLanguage>en</NeutralLanguage>
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks>
<LangVersion>7.3</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>
<PackageId>SixLabors.ImageSharp.Drawing</PackageId>
<PackageTags>Image Draw Shape Path Font</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.128.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/SixLabors/ImageSharp</PackageProjectUrl>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/SixLabors/ImageSharp</RepositoryUrl>
<DebugType Condition="$(codecov) != ''">full</DebugType>
<DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\standards\stylecop.json" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta0008" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0008" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\standards\SixLabors.ruleset</CodeAnalysisRuleSet>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

47
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -1,6 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -15,6 +18,17 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
public static class AotCompilerTools
{
static AotCompilerTools()
{
System.Runtime.CompilerServices.Unsafe.SizeOf<long>();
System.Runtime.CompilerServices.Unsafe.SizeOf<short>();
System.Runtime.CompilerServices.Unsafe.SizeOf<float>();
System.Runtime.CompilerServices.Unsafe.SizeOf<double>();
System.Runtime.CompilerServices.Unsafe.SizeOf<byte>();
System.Runtime.CompilerServices.Unsafe.SizeOf<Block8x8>();
System.Runtime.CompilerServices.Unsafe.SizeOf<Vector4>();
}
/// <summary>
/// Seeds the compiler using the given pixel format.
/// </summary>
@ -27,6 +41,13 @@ namespace SixLabors.ImageSharp.Advanced
AotCompileWuQuantizer<TPixel>();
AotCompileDithering<TPixel>();
System.Runtime.CompilerServices.Unsafe.SizeOf<TPixel>();
AotCodec<TPixel>(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder());
AotCodec<TPixel>(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder());
AotCodec<TPixel>(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder());
AotCodec<TPixel>(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder());
// TODO: Do the discovery work to figure out what works and what doesn't.
}
@ -99,5 +120,31 @@ namespace SixLabors.ImageSharp.Advanced
TPixel pixel = default;
test.Dither<TPixel>(new ImageFrame<TPixel>(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
}
/// <summary>
/// This method pre-seeds the decoder and encoder for a given pixel format in the AoT compiler for iOS.
/// </summary>
/// <param name="decoder">The image decoder to seed.</param>
/// <param name="encoder">The image encoder to seed.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCodec<TPixel>(IImageDecoder decoder, IImageEncoder encoder)
where TPixel : struct, IPixel<TPixel>
{
try
{
decoder.Decode<TPixel>(Configuration.Default, null);
}
catch
{
}
try
{
encoder.Encode<TPixel>(null, null);
}
catch
{
}
}
}
}

8
src/ImageSharp/ColorSpaces/CieLab.cs

@ -118,13 +118,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})");

5
src/ImageSharp/ColorSpaces/CieLch.cs

@ -122,10 +122,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
return HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
}
/// <inheritdoc/>

8
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.C.GetHashCode());
hash = HashHelpers.Combine(hash, this.H.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})");

8
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -119,13 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.U.GetHashCode());
hash = HashHelpers.Combine(hash, this.V.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})");

2
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc />
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode());
public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})");

7
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Yl.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Yl);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})");

7
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -86,12 +86,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.X.GetHashCode();
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.Z.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})");

8
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -90,13 +90,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.C.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
hash = HashHelpers.Combine(hash, this.Y.GetHashCode());
return HashHelpers.Combine(hash, this.K.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.C, this.M, this.Y, this.K);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})");

10
src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs

@ -86,14 +86,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hashCode = this.R.GetHashCode();
hashCode = (hashCode * 397) ^ this.G.GetHashCode();
return (hashCode * 397) ^ this.B.GetHashCode();
}
}
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
}
}

10
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces.Companding;
@ -57,10 +58,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = base.GetHashCode();
return HashHelpers.Combine(hash, this.Gamma.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(
this.WhitePoint,
this.ChromaticityCoordinates,
this.Gamma);
}
}

5
src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
{
/// <summary>
@ -76,8 +78,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.WhitePoint.GetHashCode();
return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode());
return HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates);
}
}
}

7
src/ImageSharp/ColorSpaces/Hsl.cs

@ -84,12 +84,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.L.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})");

7
src/ImageSharp/ColorSpaces/Hsv.cs

@ -82,12 +82,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.H.GetHashCode();
hash = HashHelpers.Combine(hash, this.S.GetHashCode());
return HashHelpers.Combine(hash, this.V.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})");

5
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -119,10 +119,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.A.GetHashCode());
hash = HashHelpers.Combine(hash, this.B.GetHashCode());
return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode());
return HashCode.Combine(this.L, this.A, this.B, this.WhitePoint);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -126,12 +126,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");

7
src/ImageSharp/ColorSpaces/Lms.cs

@ -87,12 +87,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.L.GetHashCode();
hash = HashHelpers.Combine(hash, this.M.GetHashCode());
return HashHelpers.Combine(hash, this.S.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})");

7
src/ImageSharp/ColorSpaces/Rgb.cs

@ -147,12 +147,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B);
/// <inheritdoc/>
public override int GetHashCode()
{
int hash = this.R.GetHashCode();
hash = HashHelpers.Combine(hash, this.G.GetHashCode());
return HashHelpers.Combine(hash, this.B.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})");

7
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -83,12 +83,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
int hash = this.Y.GetHashCode();
hash = HashHelpers.Combine(hash, this.Cb.GetHashCode());
return HashHelpers.Combine(hash, this.Cr.GetHashCode());
}
public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr);
/// <inheritdoc/>
public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})");

5
src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

2
src/ImageSharp/Common/Helpers/TestHelpers.cs

@ -1,4 +1,4 @@
// Copyright(c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Common.Helpers

9
src/ImageSharp/Common/Helpers/TolerantMath.cs

@ -16,6 +16,13 @@ namespace SixLabors.ImageSharp
private readonly double negEpsilon;
/// <summary>
/// A read-only default instance for <see cref="TolerantMath"/> using 1e-8 as epsilon.
/// It is a field so it can be passed as an 'in' parameter.
/// Does not necessarily fit all use cases!
/// </summary>
public static readonly TolerantMath Default = new TolerantMath(1e-8);
public TolerantMath(double epsilon)
{
DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon));
@ -24,8 +31,6 @@ namespace SixLabors.ImageSharp
this.negEpsilon = -epsilon;
}
public static TolerantMath Default { get; } = new TolerantMath(1e-8);
/// <summary>
/// <paramref name="a"/> == 0
/// </summary>

37
src/ImageSharp/Common/Helpers/Vector4Utils.cs

@ -5,6 +5,7 @@ using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp
{
@ -70,5 +71,41 @@ namespace SixLabors.ImageSharp
UnPremultiply(ref v);
}
}
/// <summary>
/// Transforms a vector by the given matrix.
/// </summary>
/// <param name="vector">The source vector.</param>
/// <param name="matrix">The transformation matrix.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
{
float x = vector.X;
float y = vector.Y;
float z = vector.Z;
float w = vector.W;
vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
}
/// <summary>
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>
/// </summary>
/// <param name="vectors">The span of vectors</param>
/// <param name="matrix">The transformation matrix.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void Transform(Span<Vector4> vectors, ref ColorMatrix matrix)
{
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
Transform(ref v, ref matrix);
}
}
}
}

2
src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs

@ -1,4 +1,4 @@
// Copyright(c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks;

6
src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs

@ -1,4 +1,4 @@
// Copyright(c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action<RowInterval, Memory<T>> body)
where T : struct
where T : unmanaged
{
int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask);
@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
Rectangle rectangle,
Configuration configuration,
Action<RowInterval, Memory<T>> body)
where T : struct
where T : unmanaged
{
IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body);
}

5
src/ImageSharp/Common/Tuples/Octet.cs

@ -1,4 +1,7 @@
using System.Runtime.CompilerServices;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Tuples

22
src/ImageSharp/Common/Tuples/Vector4Pair.cs

@ -1,4 +1,7 @@
using System.Numerics;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -37,12 +40,11 @@ namespace SixLabors.ImageSharp.Tuples
this.B += other.B;
}
/// <summary>
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4!
/// TODO: Move it somewhere else.
/// <summary>.
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscalePreAvx2()
internal void RoundAndDownscalePreAvx2(float downscaleFactor)
{
ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A);
a = a.FastRound();
@ -50,8 +52,8 @@ namespace SixLabors.ImageSharp.Tuples
ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B);
b = b.FastRound();
// Downscale by 1/255
var scale = new Vector4(1 / 255f);
// Downscale by 1/factor
var scale = new Vector4(1 / downscaleFactor);
this.A *= scale;
this.B *= scale;
}
@ -61,14 +63,14 @@ namespace SixLabors.ImageSharp.Tuples
/// TODO: Move it somewhere else.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RoundAndDownscaleAvx2()
internal void RoundAndDownscaleAvx2(float downscaleFactor)
{
ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this);
Vector<float> v = self;
v = v.FastRound();
// Downscale by 1/255
v *= new Vector<float>(1 / 255f);
// Downscale by 1/factor
v *= new Vector<float>(1 / downscaleFactor);
self = v;
}

2
src/ImageSharp/Formats/Bmp/BmpCompression.cs

@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// If the first byte is zero, the record has different meanings, depending
/// on the second byte. If the second byte is zero, it is the end of the row,
/// if it is one, it is the end of the image.
/// Not supported at the moment.
/// </summary>
RLE8 = 1,
@ -42,7 +41,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Each image row has a multiple of four elements. If the
/// row has less elements, zeros will be added at the right side.
/// Not supported at the moment.
/// </summary>
BitFields = 3,

36
src/ImageSharp/Formats/Bmp/BmpConstants.cs

@ -19,5 +19,41 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The list of file extensions that equate to a bmp.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "bm", "bmp", "dip" };
/// <summary>
/// Valid magic bytes markers identifying a Bitmap file.
/// </summary>
internal static class TypeMarkers
{
/// <summary>
/// Single-image BMP file that may have been created under Windows or OS/2.
/// </summary>
public const int Bitmap = 0x4D42;
/// <summary>
/// OS/2 Bitmap Array.
/// </summary>
public const int BitmapArray = 0x4142;
/// <summary>
/// OS/2 Color Icon.
/// </summary>
public const int ColorIcon = 0x4943;
/// <summary>
/// OS/2 Color Pointer.
/// </summary>
public const int ColorPointer = 0x5043;
/// <summary>
/// OS/2 Icon.
/// </summary>
public const int Icon = 0x4349;
/// <summary>
/// OS/2 Pointer.
/// </summary>
public const int Pointer = 0x5450;
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <item>JPG</item>
/// <item>PNG</item>
/// <item>RLE4</item>
/// <item>RLE8</item>
/// <item>BitFields</item>
/// </list>
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.

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

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Memory;
@ -14,7 +16,7 @@ using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Performs the bmp decoding operation.
/// Performs the bitmap decoding operation.
/// </summary>
/// <remarks>
/// A useful decoding source example can be found at <see href="https://dxr.mozilla.org/mozilla-central/source/image/decoders/nsBMPDecoder.cpp"/>
@ -22,19 +24,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal sealed class BmpDecoderCore
{
/// <summary>
/// The mask for the red part of the color for 16 bit rgb bitmaps.
/// The default mask for the red part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16RMask = 0x7C00;
private const int DefaultRgb16RMask = 0x7C00;
/// <summary>
/// The mask for the green part of the color for 16 bit rgb bitmaps.
/// The default mask for the green part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16GMask = 0x3E0;
private const int DefaultRgb16GMask = 0x3E0;
/// <summary>
/// The mask for the blue part of the color for 16 bit rgb bitmaps.
/// The default mask for the blue part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16BMask = 0x1F;
private const int DefaultRgb16BMask = 0x1F;
/// <summary>
/// RLE8 flag value that indicates following byte has special meaning.
@ -62,10 +64,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private Stream stream;
/// <summary>
/// The metadata
/// The metadata.
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// The bmp specific metadata.
/// </summary>
private BmpMetaData bmpMetaData;
/// <summary>
/// The file header containing general information.
/// TODO: Why is this not used? We advance the stream but do not use the values parsed.
@ -85,7 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Initializes a new instance of the <see cref="BmpDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The options</param>
/// <param name="options">The options.</param>
public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options)
{
this.configuration = configuration;
@ -108,7 +115,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
try
{
this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);
@ -119,7 +126,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
case BmpCompression.RGB:
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
{
this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
else
{
this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
}
}
else if (this.infoHeader.BitsPerPixel == 24)
{
@ -137,16 +151,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel,
bytesPerColorMapEntry,
inverted);
}
break;
case BmpCompression.RLE8:
this.ReadRle8(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
case BmpCompression.RLE4:
this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);
break;
case BmpCompression.BitFields:
this.ReadBitFields(pixels, inverted);
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");
break;
}
return image;
@ -198,30 +222,66 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Performs final shifting from a 5bit value to an 8bit one.
/// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask
/// which will be used to determine which bits belong to that channel.
/// </summary>
/// <param name="value">The masked and shifted value</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadBitFields<TPixel>(Buffer2D<TPixel> pixels, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(
pixels,
this.infoHeader.Width,
this.infoHeader.Height,
inverted,
this.infoHeader.RedMask,
this.infoHeader.GreenMask,
this.infoHeader.BlueMask);
}
else
{
this.ReadRgb32BitFields(
pixels,
this.infoHeader.Width,
this.infoHeader.Height,
inverted,
this.infoHeader.RedMask,
this.infoHeader.GreenMask,
this.infoHeader.BlueMask,
this.infoHeader.AlphaMask);
}
}
/// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 data.
/// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data.
/// Compressed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// Compressed RLE4 stream is uncompressed by <see cref="UncompressRle4(int, Span{byte})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compression">The compression type. Either RLE4 or RLE8.</param>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRle8<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
private void ReadRle<TPixel>(BmpCompression compression, Buffer2D<TPixel> pixels, byte[] colors, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
TPixel color = default;
using (Buffer2D<byte> buffer = this.memoryAllocator.Allocate2D<byte>(width, height, AllocationOptions.Clean))
{
this.UncompressRle8(width, buffer.GetSpan());
if (compression == BmpCompression.RLE8)
{
this.UncompressRle8(width, buffer.GetSpan());
}
else
{
this.UncompressRle4(width, buffer.GetSpan());
}
for (int y = 0; y < height; y++)
{
@ -239,12 +299,122 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Produce uncompressed bitmap data from RLE8 stream
/// Produce uncompressed bitmap data from a RLE4 stream.
/// </summary>
/// <remarks>
/// RLE8 is a 2-byte run-length encoding
/// <br/>If first byte is 0, the second byte may have special meaning
/// <br/>Otherwise, first byte is the length of the run and second byte is the color for the run
/// RLE4 is a 2-byte run-length encoding.
/// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte contains two color indexes.
/// </remarks>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
private void UncompressRle4(int w, Span<byte> buffer)
{
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2];
#endif
int count = 0;
while (count < buffer.Length)
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
}
if (cmd[0] == RleCommand)
{
switch (cmd[1])
{
case RleEndOfBitmap:
return;
case RleEndOfLine:
int extra = count % w;
if (extra > 0)
{
count += w - extra;
}
break;
case RleDelta:
int dx = this.stream.ReadByte();
int dy = this.stream.ReadByte();
count += (w * dy) + dx;
break;
default:
// If the second byte > 2, we are in 'absolute mode'.
// The second byte contains the number of color indexes that follow.
int max = cmd[1];
int bytesToRead = (max + 1) / 2;
byte[] run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length);
int idx = 0;
for (int i = 0; i < max; i++)
{
byte twoPixels = run[idx];
if (i % 2 == 0)
{
byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
buffer[count++] = leftPixel;
}
else
{
byte rightPixel = (byte)(twoPixels & 0xF);
buffer[count++] = rightPixel;
idx++;
}
}
// Absolute mode data is aligned to two-byte word-boundary
int padding = bytesToRead & 1;
this.stream.Skip(padding);
break;
}
}
else
{
int max = cmd[0];
// The second byte contains two color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
byte twoPixels = cmd[1];
byte rightPixel = (byte)(twoPixels & 0xF);
byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
for (int idx = 0; idx < max; idx++)
{
if (idx % 2 == 0)
{
buffer[count] = leftPixel;
}
else
{
buffer[count] = rightPixel;
}
count++;
}
}
}
}
/// <summary>
/// Produce uncompressed bitmap data from a RLE8 stream.
/// </summary>
/// <remarks>
/// RLE8 is a 2-byte run-length encoding.
/// <br/>If first byte is 0, the second byte may have special meaning.
/// <br/>Otherwise, the first byte is the length of the run and second byte is the color for the run.
/// </remarks>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
@ -261,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
throw new Exception("Failed to read 2 bytes from stream");
BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
}
if (cmd[0] == RleCommand)
@ -329,18 +499,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param>
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
/// <param name="bytesPerColorMapEntry">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.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bits, bool inverted)
private void ReadRgbPalette<TPixel>(Buffer2D<TPixel> pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
// Pixels per byte (bits per pixel)
int ppb = 8 / bits;
int ppb = 8 / bitsPerPixel;
int arrayWidth = (width + ppb - 1) / ppb;
// Bit mask
int mask = 0xFF >> (8 - bits);
int mask = 0xFF >> (8 - bitsPerPixel);
// Rows are aligned on 4 byte boundaries
int padding = arrayWidth % 4;
@ -366,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int colOffset = x * ppb;
for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++)
{
int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4;
int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry;
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref colors[colorIndex]));
pixelRow[newX] = color;
@ -379,20 +551,32 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Reads the 16 bit color palette from the stream
/// Reads the 16 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
/// <param name="redMask">The bitmask for the red channel.</param>
/// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param>
private void ReadRgb16<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask = DefaultRgb16RMask, int greenMask = DefaultRgb16GMask, int blueMask = DefaultRgb16BMask)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 2);
int stride = (width * 2) + padding;
TPixel color = default;
int rightShiftRedMask = CalculateRightShift((uint)redMask);
int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
// Each color channel contains either 5 or 6 Bits values.
int redMaskBits = CountBits((uint)redMask);
int greenMaskBits = CountBits((uint)greenMask);
int blueMaskBits = CountBits((uint)blueMask);
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
@ -406,10 +590,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
short temp = BitConverter.ToInt16(buffer.Array, offset);
var rgb = new Rgb24(
GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10),
GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5),
GetBytesFrom5BitValue(temp & Rgb16BMask));
// Rescale values, so the values range from 0 to 255.
int r = (redMaskBits == 5) ? GetBytesFrom5BitValue((temp & redMask) >> rightShiftRedMask) : GetBytesFrom6BitValue((temp & redMask) >> rightShiftRedMask);
int g = (greenMaskBits == 5) ? GetBytesFrom5BitValue((temp & greenMask) >> rightShiftGreenMask) : GetBytesFrom6BitValue((temp & greenMask) >> rightShiftGreenMask);
int b = (blueMaskBits == 5) ? GetBytesFrom5BitValue((temp & blueMask) >> rightShiftBlueMask) : GetBytesFrom6BitValue((temp & blueMask) >> rightShiftBlueMask);
var rgb = new Rgb24((byte)r, (byte)g, (byte)b);
color.FromRgb24(rgb);
pixelRow[x] = color;
@ -420,7 +605,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Reads the 24 bit color palette from the stream
/// Performs final shifting from a 5bit value to an 8bit one.
/// </summary>
/// <param name="value">The masked and shifted value.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2));
/// <summary>
/// Performs final shifting from a 6bit value to an 8bit one.
/// </summary>
/// <param name="value">The masked and shifted value.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4));
/// <summary>
/// Reads the 24 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
@ -449,14 +650,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
/// <summary>
/// Reads the 32 bit color palette from the stream
/// Reads the 32 bit color palette from the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
private void ReadRgb32Fast<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
@ -477,6 +678,228 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
/// <summary>
/// Reads the 32 bit color palette from the stream, checking the alpha component of each pixel.
/// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
private void ReadRgb32Slow<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted)
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding))
using (IMemoryOwner<Bgra32> bgraRow = this.memoryAllocator.Allocate<Bgra32>(width))
{
Span<Bgra32> bgraRowSpan = bgraRow.GetSpan();
long currentPosition = this.stream.Position;
bool hasAlpha = false;
// Loop though the rows checking each pixel. We start by assuming it's
// an BGR0 image. If we hit a non-zero alpha value, then we know it's
// actually a BGRA image, and change tactics accordingly.
for (int y = 0; y < height; y++)
{
this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
row.GetSpan(),
bgraRowSpan,
width);
// Check each pixel in the row to see if it has an alpha value.
for (int x = 0; x < width; x++)
{
Bgra32 bgra = bgraRowSpan[x];
if (bgra.A > 0)
{
hasAlpha = true;
break;
}
}
if (hasAlpha)
{
break;
}
}
// Reset our stream for a second pass.
this.stream.Position = currentPosition;
// Process the pixels in bulk taking the raw alpha component value.
if (hasAlpha)
{
for (int y = 0; y < height; y++)
{
this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(
this.configuration,
row.GetSpan(),
pixelSpan,
width);
}
return;
}
// Slow path. We need to set each alpha component value to fully opaque.
for (int y = 0; y < height; y++)
{
this.stream.Read(row);
PixelOperations<Bgra32>.Instance.FromBgra32Bytes(
this.configuration,
row.GetSpan(),
bgraRowSpan,
width);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelSpan = pixels.GetRowSpan(newY);
for (int x = 0; x < width; x++)
{
Bgra32 bgra = bgraRowSpan[x];
bgra.A = byte.MaxValue;
ref TPixel pixel = ref pixelSpan[x];
pixel.FromBgra32(bgra);
}
}
}
}
/// <summary>
/// Decode an 32 Bit Bitmap containing a bitmask for each color channel.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The output pixel buffer containing the decoded image.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
/// <param name="redMask">The bitmask for the red channel.</param>
/// <param name="greenMask">The bitmask for the green channel.</param>
/// <param name="blueMask">The bitmask for the blue channel.</param>
/// <param name="alphaMask">The bitmask for the alpha channel.</param>
private void ReadRgb32BitFields<TPixel>(Buffer2D<TPixel> pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask)
where TPixel : struct, IPixel<TPixel>
{
TPixel color = default;
int padding = CalculatePadding(width, 4);
int stride = (width * 4) + padding;
int rightShiftRedMask = CalculateRightShift((uint)redMask);
int rightShiftGreenMask = CalculateRightShift((uint)greenMask);
int rightShiftBlueMask = CalculateRightShift((uint)blueMask);
int rightShiftAlphaMask = CalculateRightShift((uint)alphaMask);
int bitsRedMask = CountBits((uint)redMask);
int bitsGreenMask = CountBits((uint)greenMask);
int bitsBlueMask = CountBits((uint)blueMask);
int bitsAlphaMask = CountBits((uint)alphaMask);
float invMaxValueRed = 1.0f / (0xFFFFFFFF >> (32 - bitsRedMask));
float invMaxValueGreen = 1.0f / (0xFFFFFFFF >> (32 - bitsGreenMask));
float invMaxValueBlue = 1.0f / (0xFFFFFFFF >> (32 - bitsBlueMask));
uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask);
float invMaxValueAlpha = 1.0f / maxValueAlpha;
bool unusualBitMask = false;
if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8)
{
unusualBitMask = true;
}
using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
this.stream.Read(buffer.Array, 0, stride);
int newY = Invert(y, height, inverted);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
int offset = 0;
for (int x = 0; x < width; x++)
{
uint temp = BitConverter.ToUInt32(buffer.Array, offset);
if (unusualBitMask)
{
uint r = (uint)(temp & redMask) >> rightShiftRedMask;
uint g = (uint)(temp & greenMask) >> rightShiftGreenMask;
uint b = (uint)(temp & blueMask) >> rightShiftBlueMask;
float alpha = alphaMask != 0 ? invMaxValueAlpha * ((uint)(temp & alphaMask) >> rightShiftAlphaMask) : 1.0f;
var vector4 = new Vector4(
r * invMaxValueRed,
g * invMaxValueGreen,
b * invMaxValueBlue,
alpha);
color.FromVector4(vector4);
}
else
{
byte r = (byte)((temp & redMask) >> rightShiftRedMask);
byte g = (byte)((temp & greenMask) >> rightShiftGreenMask);
byte b = (byte)((temp & blueMask) >> rightShiftBlueMask);
byte a = alphaMask != 0 ? (byte)((temp & alphaMask) >> rightShiftAlphaMask) : (byte)255;
color.FromRgba32(new Rgba32(r, g, b, a));
}
pixelRow[x] = color;
offset += 4;
}
}
}
}
/// <summary>
/// Calculates the necessary right shifts for a given color bitmask (the 0 bits to the right).
/// </summary>
/// <param name="n">The color bit mask.</param>
/// <returns>Number of bits to shift right.</returns>
private static int CalculateRightShift(uint n)
{
int count = 0;
while (n > 0)
{
if ((1 & n) == 0)
{
count++;
}
else
{
break;
}
n >>= 1;
}
return count;
}
/// <summary>
/// Counts none zero bits.
/// </summary>
/// <param name="n">A color mask.</param>
/// <returns>The none zero bits.</returns>
private static int CountBits(uint n)
{
int count = 0;
while (n != 0)
{
count++;
n &= n - 1;
}
return count;
}
/// <summary>
/// Reads the <see cref="BmpInfoHeader"/> from the stream.
/// </summary>
@ -492,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize)
{
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'.");
}
int skipAmount = 0;
@ -505,19 +928,64 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// read the rest of the header
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2;
if (headerSize == BmpInfoHeader.CoreSize)
{
// 12 bytes
infoHeaderType = BmpInfoHeaderType.WinVersion2;
this.infoHeader = BmpInfoHeader.ParseCore(buffer);
}
else if (headerSize >= BmpInfoHeader.Size)
else if (headerSize == BmpInfoHeader.Os22ShortSize)
{
// >= 40 bytes
this.infoHeader = BmpInfoHeader.Parse(buffer);
// 16 bytes
infoHeaderType = BmpInfoHeaderType.Os2Version2Short;
this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer);
}
else if (headerSize == BmpInfoHeader.SizeV3)
{
// == 40 bytes
infoHeaderType = BmpInfoHeaderType.WinVersion3;
this.infoHeader = BmpInfoHeader.ParseV3(buffer);
// if the info header is BMP version 3 and the compression type is BITFIELDS,
// color masks for each color channel follow the info header.
if (this.infoHeader.Compression == BmpCompression.BitFields)
{
byte[] bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan<byte>();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
}
}
else if (headerSize == BmpInfoHeader.AdobeV3Size)
{
// == 52 bytes
infoHeaderType = BmpInfoHeaderType.AdobeVersion3;
this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: false);
}
else if (headerSize == BmpInfoHeader.AdobeV3WithAlphaSize)
{
// == 56 bytes
infoHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha;
this.infoHeader = BmpInfoHeader.ParseAdobeV3(buffer, withAlpha: true);
}
else if (headerSize == BmpInfoHeader.Os2v2Size)
{
// == 64 bytes
infoHeaderType = BmpInfoHeaderType.Os2Version2;
this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer);
}
else if (headerSize >= BmpInfoHeader.SizeV4)
{
// >= 108 bytes
infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5;
this.infoHeader = BmpInfoHeader.ParseV4(buffer);
}
else
{
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'.");
}
// Resolution is stored in PPM.
@ -540,13 +1008,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.metaData = meta;
short bitsPerPixel = this.infoHeader.BitsPerPixel;
BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
this.bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance);
this.bmpMetaData.InfoHeaderType = infoHeaderType;
// We can only encode at these bit rates so far.
if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24)
|| bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32))
{
bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
this.bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
}
// skip the remaining header because we can't read those parts
@ -566,12 +1035,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
{
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'.");
}
}
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> and <see cref="BmpInfoHeader"/> from the stream and sets the corresponding fields.
/// </summary>
private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
/// <returns>Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps
/// the bytes per color palette entry's can be 3 bytes instead of 4.</returns>
private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
this.stream = stream;
@ -591,6 +1067,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
int colorMapSize = -1;
int bytesPerColorMapEntry = 4;
if (this.infoHeader.ClrUsed == 0)
{
@ -598,12 +1075,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
|| this.infoHeader.BitsPerPixel == 4
|| this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel) * 4;
int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize;
int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel);
bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth;
colorMapSize = colorMapSizeBytes;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
}
palette = null;
@ -613,7 +1093,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// 256 * 4
if (colorMapSize > 1024)
{
throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
palette = new byte[colorMapSize];
@ -622,6 +1102,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
this.infoHeader.VerifyDimensions();
return bytesPerColorMapEntry;
}
}
}

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

@ -23,6 +23,26 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private int padding;
/// <summary>
/// The mask for the alpha channel of the color for a 32 bit rgba bitmaps.
/// </summary>
private const int Rgba32AlphaMask = 0xFF << 24;
/// <summary>
/// The mask for the red part of the color for a 32 bit rgba bitmaps.
/// </summary>
private const int Rgba32RedMask = 0xFF << 16;
/// <summary>
/// The mask for the green part of the color for a 32 bit rgba bitmaps.
/// </summary>
private const int Rgba32GreenMask = 0xFF << 8;
/// <summary>
/// The mask for the blue part of the color for a 32 bit rgba bitmaps.
/// </summary>
private const int Rgba32BlueMask = 0xFF;
private readonly MemoryAllocator memoryAllocator;
private Configuration configuration;
@ -92,8 +112,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
}
int infoHeaderSize = BmpInfoHeader.SizeV4;
var infoHeader = new BmpInfoHeader(
headerSize: BmpInfoHeader.Size,
headerSize: infoHeaderSize,
height: image.Height,
width: image.Width,
bitsPerPixel: bpp,
@ -102,26 +123,37 @@ namespace SixLabors.ImageSharp.Formats.Bmp
clrUsed: 0,
clrImportant: 0,
xPelsPerMeter: hResolution,
yPelsPerMeter: vResolution);
yPelsPerMeter: vResolution)
{
RedMask = Rgba32RedMask,
GreenMask = Rgba32GreenMask,
BlueMask = Rgba32BlueMask,
Compression = BmpCompression.BitFields
};
if (this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
{
infoHeader.AlphaMask = Rgba32AlphaMask;
}
var fileHeader = new BmpFileHeader(
type: 19778, // BM
fileSize: 54 + infoHeader.ImageSize,
type: BmpConstants.TypeMarkers.Bitmap,
fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
reserved: 0,
offset: 54);
offset: BmpFileHeader.Size + infoHeaderSize);
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[40];
Span<byte> buffer = stackalloc byte[infoHeaderSize];
#else
byte[] buffer = new byte[40];
byte[] buffer = new byte[infoHeaderSize];
#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size);
infoHeader.WriteTo(buffer);
infoHeader.WriteV4Header(buffer);
stream.Write(buffer, 0, 40);
stream.Write(buffer, 0, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame);

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

@ -17,19 +17,44 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal struct BmpInfoHeader
{
/// <summary>
/// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file.
/// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
/// </summary>
public const int Size = 40;
public const int CoreSize = 12;
/// <summary>
/// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
/// Defines the size of the short variant of the OS22XBITMAPHEADER data structure in the bitmap file.
/// </summary>
public const int CoreSize = 12;
public const int Os22ShortSize = 16;
/// <summary>
/// Defines the size of the BITMAPINFOHEADER (BMP Version 3) data structure in the bitmap file.
/// </summary>
public const int SizeV3 = 40;
/// <summary>
/// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
/// </summary>
public const int AdobeV3Size = 52;
/// <summary>
/// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks (including the alpha channel) are part of the info header instead of following it.
/// </summary>
public const int AdobeV3WithAlphaSize = 56;
/// <summary>
/// Size of a IBM OS/2 2.x bitmap header.
/// </summary>
public const int Os2v2Size = 64;
/// <summary>
/// Defines the size of the BITMAPINFOHEADER (BMP Version 4) data structure in the bitmap file.
/// </summary>
public const int SizeV4 = 108;
/// <summary>
/// Defines the size of the biggest supported header data structure in the bitmap file.
/// </summary>
public const int MaxHeaderSize = Size;
public const int MaxHeaderSize = SizeV4;
/// <summary>
/// Defines the size of the <see cref="HeaderSize"/> field.
@ -47,7 +72,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int xPelsPerMeter = 0,
int yPelsPerMeter = 0,
int clrUsed = 0,
int clrImportant = 0)
int clrImportant = 0,
int redMask = 0,
int greenMask = 0,
int blueMask = 0,
int alphaMask = 0,
int csType = 0,
int redX = 0,
int redY = 0,
int redZ = 0,
int greenX = 0,
int greenY = 0,
int greenZ = 0,
int blueX = 0,
int blueY = 0,
int blueZ = 0,
int gammeRed = 0,
int gammeGreen = 0,
int gammeBlue = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
@ -60,10 +102,27 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.YPelsPerMeter = yPelsPerMeter;
this.ClrUsed = clrUsed;
this.ClrImportant = clrImportant;
this.RedMask = redMask;
this.GreenMask = greenMask;
this.BlueMask = blueMask;
this.AlphaMask = alphaMask;
this.CsType = csType;
this.RedX = redX;
this.RedY = redY;
this.RedZ = redZ;
this.GreenX = greenX;
this.GreenY = greenY;
this.GreenZ = greenZ;
this.BlueX = blueX;
this.BlueY = blueY;
this.BlueZ = blueZ;
this.GammaRed = gammeRed;
this.GammaGreen = gammeGreen;
this.GammaBlue = gammeBlue;
}
/// <summary>
/// Gets or sets the size of this header
/// Gets or sets the size of this header.
/// </summary>
public int HeaderSize { get; set; }
@ -125,25 +184,94 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public int ClrImportant { get; set; }
/// <summary>
/// Parses the full BITMAPINFOHEADER header (40 bytes).
/// Gets or sets red color mask. This is used with the BITFIELDS decoding.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx"/>
public static BmpInfoHeader Parse(ReadOnlySpan<byte> data)
{
if (data.Length != Size)
{
throw new ArgumentException(nameof(data), $"Must be {Size} bytes. Was {data.Length} bytes.");
}
public int RedMask { get; set; }
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
}
/// <summary>
/// Gets or sets green color mask. This is used with the BITFIELDS decoding.
/// </summary>
public int GreenMask { get; set; }
/// <summary>
/// Gets or sets blue color mask. This is used with the BITFIELDS decoding.
/// </summary>
public int BlueMask { get; set; }
/// <summary>
/// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
/// Gets or sets alpha color mask. This is not used yet.
/// </summary>
/// <param name="data">The data to parse,</param>
public int AlphaMask { get; set; }
/// <summary>
/// Gets or sets the Color space type. Not used yet.
/// </summary>
public int CsType { get; set; }
/// <summary>
/// Gets or sets the X coordinate of red endpoint. Not used yet.
/// </summary>
public int RedX { get; set; }
/// <summary>
/// Gets or sets the Y coordinate of red endpoint. Not used yet.
/// </summary>
public int RedY { get; set; }
/// <summary>
/// Gets or sets the Z coordinate of red endpoint. Not used yet.
/// </summary>
public int RedZ { get; set; }
/// <summary>
/// Gets or sets the X coordinate of green endpoint. Not used yet.
/// </summary>
public int GreenX { get; set; }
/// <summary>
/// Gets or sets the Y coordinate of green endpoint. Not used yet.
/// </summary>
public int GreenY { get; set; }
/// <summary>
/// Gets or sets the Z coordinate of green endpoint. Not used yet.
/// </summary>
public int GreenZ { get; set; }
/// <summary>
/// Gets or sets the X coordinate of blue endpoint. Not used yet.
/// </summary>
public int BlueX { get; set; }
/// <summary>
/// Gets or sets the Y coordinate of blue endpoint. Not used yet.
/// </summary>
public int BlueY { get; set; }
/// <summary>
/// Gets or sets the Z coordinate of blue endpoint. Not used yet.
/// </summary>
public int BlueZ { get; set; }
/// <summary>
/// Gets or sets the Gamma red coordinate scale value. Not used yet.
/// </summary>
public int GammaRed { get; set; }
/// <summary>
/// Gets or sets the Gamma green coordinate scale value. Not used yet.
/// </summary>
public int GammaGreen { get; set; }
/// <summary>
/// Gets or sets the Gamma blue coordinate scale value. Not used yet.
/// </summary>
public int GammaBlue { get; set; }
/// <summary>
/// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/>
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
@ -156,7 +284,161 @@ namespace SixLabors.ImageSharp.Formats.Bmp
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
}
public unsafe void WriteTo(Span<byte> buffer)
/// <summary>
/// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height
/// are 4 bytes instead of 2, resulting in 16 bytes total.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns>
/// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
}
/// <summary>
/// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes).
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/>
public static BmpInfoHeader ParseV3(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)));
}
/// <summary>
/// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
/// 52 bytes without the alpha mask, 56 bytes with the alpha mask.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <param name="withAlpha">Indicates, if the alpha bitmask is present.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://forums.adobe.com/message/3272950#3272950"/>
public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan<byte> data, bool withAlpha = true)
{
return new BmpInfoHeader(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)),
redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)),
greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0);
}
/// <summary>
/// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are
/// very similar to the Bitmap v3 header. The other 24 bytes are ignored, but they do not hold any
/// useful information for decoding the image.
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan<byte> data)
{
var infoHeader = new BmpInfoHeader(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4));
// The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
// Map the OS/2 value to the windows values.
switch (compression)
{
case 0:
infoHeader.Compression = BmpCompression.RGB;
break;
case 1:
infoHeader.Compression = BmpCompression.RLE8;
break;
case 2:
infoHeader.Compression = BmpCompression.RLE4;
break;
default:
BmpThrowHelper.ThrowImageFormatException($"Compression type is not supported. ImageSharp only supports uncompressed, RLE4 and RLE8.");
break;
}
infoHeader.ImageSize = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4));
infoHeader.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4));
infoHeader.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4));
infoHeader.ClrUsed = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4));
infoHeader.ClrImportant = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4));
// The following 24 bytes of the header are omitted.
return infoHeader;
}
/// <summary>
/// Parses the full BMP Version 4 BITMAPINFOHEADER header (108 bytes).
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/>
public static BmpInfoHeader ParseV4(ReadOnlySpan<byte> data)
{
if (data.Length != SizeV4)
{
throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
}
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];
}
/// <summary>
/// Writes a bitmap version 3 (Microsoft Windows NT) header to a buffer (40 bytes).
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public void WriteV3Header(Span<byte> buffer)
{
buffer.Clear();
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), SizeV3);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), this.Width);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), this.Height);
BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), this.Planes);
BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), this.BitsPerPixel);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)this.Compression);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), this.ImageSize);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), this.XPelsPerMeter);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), this.YPelsPerMeter);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), this.ClrUsed);
BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), this.ClrImportant);
}
/// <summary>
/// Writes a complete Bitmap V4 header to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public unsafe void WriteV4Header(Span<byte> buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As<byte, BmpInfoHeader>(ref MemoryMarshal.GetReference(buffer));

51
src/ImageSharp/Formats/Bmp/BmpInfoHeaderType.cs

@ -0,0 +1,51 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Enum value for the different bitmap info header types. The enum value is the number of bytes for the specific bitmap header.
/// </summary>
public enum BmpInfoHeaderType
{
/// <summary>
/// Bitmap Core or BMP Version 2 header (Microsoft Windows 2.x).
/// </summary>
WinVersion2 = 12,
/// <summary>
/// Short variant of the OS/2 Version 2 bitmap header.
/// </summary>
Os2Version2Short = 16,
/// <summary>
/// BMP Version 3 header (Microsoft Windows 3.x or Microsoft Windows NT).
/// </summary>
WinVersion3 = 40,
/// <summary>
/// Adobe variant of the BMP Version 3 header.
/// </summary>
AdobeVersion3 = 52,
/// <summary>
/// Adobe variant of the BMP Version 3 header with an alpha mask.
/// </summary>
AdobeVersion3WithAlpha = 56,
/// <summary>
/// BMP Version 2.x header (IBM OS/2 2.x).
/// </summary>
Os2Version2 = 64,
/// <summary>
/// BMP Version 4 header (Microsoft Windows 95).
/// </summary>
WinVersion4 = 108,
/// <summary>
/// BMP Version 5 header (Windows NT 5.0, 98 or later).
/// </summary>
WinVersion5 = 124,
}
}

11
src/ImageSharp/Formats/Bmp/BmpMetaData.cs

@ -19,7 +19,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Initializes a new instance of the <see cref="BmpMetaData"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private BmpMetaData(BmpMetaData other) => this.BitsPerPixel = other.BitsPerPixel;
private BmpMetaData(BmpMetaData other)
{
this.BitsPerPixel = other.BitsPerPixel;
this.InfoHeaderType = other.InfoHeaderType;
}
/// <summary>
/// Gets or sets the bitmap info header type.
/// </summary>
public BmpInfoHeaderType InfoHeaderType { get; set; }
/// <summary>
/// Gets or sets the number of bits per pixel.

31
src/ImageSharp/Formats/Bmp/BmpThrowHelper.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Bmp
{
internal static class BmpThrowHelper
{
/// <summary>
/// Cold path optimization for throwing <see cref="ImageFormatException"/>-s
/// </summary>
/// <param name="errorMessage">The error message for the exception.</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowImageFormatException(string errorMessage)
{
throw new ImageFormatException(errorMessage);
}
/// <summary>
/// Cold path optimization for throwing <see cref="NotSupportedException"/>-s
/// </summary>
/// <param name="errorMessage">The error message for the exception.</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowNotSupportedException(string errorMessage)
{
throw new NotSupportedException(errorMessage);
}
}
}

5
src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Gif
{

19
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -9,10 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary>
/// Transpose the block into the destination block.
/// </summary>
@ -94,10 +91,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>
public void NormalizeColorsInplace()
public void NormalizeColorsInplace(float maximum)
{
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
this.V1L = Vector4.Clamp(this.V1L + COff4, CMin4, CMax4);
@ -120,10 +121,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
Vector<float> off = new Vector<float>(128f);
Vector<float> max = new Vector<float>(255F);
Vector<float> off = new Vector<float>((float)Math.Ceiling(maximum/2));
Vector<float> max = new Vector<float>(maximum);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);

19
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -11,6 +11,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -22,10 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
private static readonly Vector4 CMin4 = new Vector4(0F);
private static readonly Vector4 CMax4 = new Vector4(255F);
private static readonly Vector4 COff4 = new Vector4(128F);
/// <summary>
/// Transpose the block into the destination block.
/// </summary>
@ -59,10 +56,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Level shift by +128, clip to [0, 255]
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>
public void NormalizeColorsInplace()
public void NormalizeColorsInplace(float maximum)
{
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4((float)Math.Ceiling(maximum/2));
<#
PushIndent(" ");
@ -83,10 +84,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// AVX2-only variant for executing <see cref="NormalizeColorsInplace"/> and <see cref="RoundInplace"/> in one step.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2()
public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
Vector<float> off = new Vector<float>(128f);
Vector<float> max = new Vector<float>(255F);
Vector<float> off = new Vector<float>((float)Math.Ceiling(maximum/2));
Vector<float> max = new Vector<float>(maximum);
<#
for (int i = 0; i < 8; i++)

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

@ -467,17 +467,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Level shift by +128, clip to [0..255], 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>
public void NormalizeColorsAndRoundInplace()
public void NormalizeColorsAndRoundInplace(float maximum)
{
if (SimdUtils.IsAvx2CompatibleArchitecture)
{
this.NormalizeColorsAndRoundInplaceAvx2();
this.NormalizeColorsAndRoundInplaceAvx2(maximum);
}
else
{
this.NormalizeColorsInplace();
this.NormalizeColorsInplace(maximum);
this.RoundInplace();
}
}

12
src/ImageSharp/Formats/Jpeg/Components/Decoder/AdobeMarker.cs

@ -100,13 +100,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <inheritdoc/>
public override int GetHashCode()
{
return HashHelpers.Combine(
this.DCTEncodeVersion.GetHashCode(),
HashHelpers.Combine(
this.APP14Flags0.GetHashCode(),
HashHelpers.Combine(
this.APP14Flags1.GetHashCode(),
this.ColorTransform.GetHashCode())));
return HashCode.Combine(
this.DCTEncodeVersion,
this.APP14Flags0,
this.APP14Flags1,
this.ColorTransform);
}
}
}

12
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromCmyk : JpegColorConverter
{
public FromCmyk()
: base(JpegColorSpace.Cmyk)
public FromCmyk(int precision)
: base(JpegColorSpace.Cmyk, precision)
{
}
@ -25,14 +25,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++)
{
float c = cVals[i];
float m = mVals[i];
float y = yVals[i];
float k = kVals[i] / 255F;
float k = kVals[i] / this.MaximumValue;
v.X = c * k;
v.Y = m * k;

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -12,14 +12,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromGrayscale : JpegColorConverter
{
public FromGrayscale()
: base(JpegColorSpace.Grayscale)
public FromGrayscale(int precision)
: base(JpegColorSpace.Grayscale, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromRgb : JpegColorConverter
{
public FromRgb()
: base(JpegColorSpace.RGB)
public FromRgb(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
@ -24,7 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++)
{

14
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -10,17 +10,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrBasic : JpegColorConverter
{
public FromYCbCrBasic()
: base(JpegColorSpace.YCbCr)
public FromYCbCrBasic(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
ConvertCore(values, result);
ConvertCore(values, result, this.MaximumValue, this.HalfValue);
}
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;
@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var scale = new Vector4(1 / maxValue, 1 / maxValue, 1 / maxValue, 1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
float cb = cbVals[i] - 128F;
float cr = crVals[i] - 128F;
float cb = cbVals[i] - halfValue;
float cr = crVals[i] - halfValue;
v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero);
v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero);

28
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -14,8 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrSimd : JpegColorConverter
{
public FromYCbCrSimd()
: base(JpegColorSpace.YCbCr)
public FromYCbCrSimd(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
@ -25,16 +25,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
/// <summary>
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!");
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector4(-128f);
var chromaOffset = new Vector4(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
// y = yVals[i];
Vector4Pair y = Unsafe.Add(ref yBase, i);
// cb = cbVals[i] - 128F;
// cb = cbVals[i] - halfValue);
Vector4Pair cb = Unsafe.Add(ref cbBase, i);
cb.AddInplace(chromaOffset);
// cr = crVals[i] - 128F;
// cr = crVals[i] - halfValue;
Vector4Pair cr = Unsafe.Add(ref crBase, i);
cr.AddInplace(chromaOffset);
@ -90,15 +90,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
if (Vector<float>.Count == 4)
{
// TODO: Find a way to properly run & test this path on AVX2 PC-s! (Have I already mentioned that Vector<T> is terrible?)
r.RoundAndDownscalePreAvx2();
g.RoundAndDownscalePreAvx2();
b.RoundAndDownscalePreAvx2();
r.RoundAndDownscalePreAvx2(maxValue);
g.RoundAndDownscalePreAvx2(maxValue);
b.RoundAndDownscalePreAvx2(maxValue);
}
else if (SimdUtils.IsAvx2CompatibleArchitecture)
{
r.RoundAndDownscaleAvx2();
g.RoundAndDownscaleAvx2();
b.RoundAndDownscaleAvx2();
r.RoundAndDownscaleAvx2(maxValue);
g.RoundAndDownscaleAvx2(maxValue);
b.RoundAndDownscaleAvx2(maxValue);
}
else
{

14
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -15,8 +15,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYCbCrSimdAvx2 : JpegColorConverter
{
public FromYCbCrSimdAvx2()
: base(JpegColorSpace.YCbCr)
public FromYCbCrSimdAvx2(int precision)
: base(JpegColorSpace.YCbCr, precision)
{
}
@ -28,16 +28,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
int simdCount = result.Length - remainder;
if (simdCount > 0)
{
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount));
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue);
}
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder));
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue);
}
/// <summary>
/// SIMD convert using buffers of sizes divisible by 8.
/// </summary>
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector4Octet resultBase =
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result));
var chromaOffset = new Vector<float>(-128f);
var chromaOffset = new Vector<float>(-halfValue);
// Walking 8 elements at one step:
int n = result.Length / 8;
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg);
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb);
var scale = new Vector<float>(1 / 255f);
var scale = new Vector<float>(1 / maxValue);
for (int i = 0; i < n; i++)
{

22
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
internal class FromYccK : JpegColorConverter
{
public FromYccK()
: base(JpegColorSpace.Ycck)
public FromYccK(int precision)
: base(JpegColorSpace.Ycck, precision)
{
}
@ -25,18 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
for (int i = 0; i < result.Length; i++)
{
float y = yVals[i];
float cb = cbVals[i] - 128F;
float cr = crVals[i] - 128F;
float k = kVals[i] / 255F;
float cb = cbVals[i] - this.HalfValue;
float cr = crVals[i] - this.HalfValue;
float k = kVals[i] / this.MaximumValue;
v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.X = (this.MaximumValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k;
v.Y = (this.MaximumValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k;
v.Z = (this.MaximumValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k;
v.W = 1F;
v *= scale;

45
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -3,12 +3,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Tuples;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
@ -22,15 +20,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
private static readonly JpegColorConverter[] Converters =
{
GetYCbCrConverter(), new FromYccK(), new FromCmyk(), new FromGrayscale(), new FromRgb()
// 8-bit converters
GetYCbCrConverter(8),
new FromYccK(8),
new FromCmyk(8),
new FromGrayscale(8),
new FromRgb(8),
// 12-bit converters
GetYCbCrConverter(12),
new FromYccK(12),
new FromCmyk(12),
new FromGrayscale(12),
new FromRgb(12),
};
/// <summary>
/// Initializes a new instance of the <see cref="JpegColorConverter"/> class.
/// </summary>
protected JpegColorConverter(JpegColorSpace colorSpace)
protected JpegColorConverter(JpegColorSpace colorSpace, int precision)
{
this.ColorSpace = colorSpace;
this.Precision = precision;
this.MaximumValue = (float)Math.Pow(2, precision) - 1;
this.HalfValue = (float)Math.Ceiling(this.MaximumValue / 2);
}
/// <summary>
@ -38,12 +51,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
public JpegColorSpace ColorSpace { get; }
/// <summary>
/// Gets the Precision of this converter in bits.
/// </summary>
public int Precision { get; }
/// <summary>
/// Gets the maximum value of a sample
/// </summary>
private float MaximumValue { get; }
/// <summary>
/// Gets the half of the maximum value of a sample
/// </summary>
private float HalfValue { get; }
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> corresponding to the given <see cref="JpegColorSpace"/>
/// </summary>
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace)
public static JpegColorConverter GetConverter(JpegColorSpace colorSpace, float precision)
{
JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace);
JpegColorConverter converter = Array.Find(Converters, c => c.ColorSpace == colorSpace
&& c.Precision == precision);
if (converter is null)
{
@ -63,8 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.
/// </summary>
private static JpegColorConverter GetYCbCrConverter() =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd();
private static JpegColorConverter GetYCbCrConverter(int precision) =>
FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2(precision) : new FromYCbCrSimd(precision);
/// <summary>
/// A stack-only struct to reference the input buffers using <see cref="ReadOnlySpan{T}"/>-s.

23
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs

@ -50,8 +50,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <param name="values">The huffman values</param>
public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
{
const int Length = 257;
using (IMemoryOwner<short> huffcode = memoryAllocator.Allocate<short>(Length))
// We do some bounds checks in the code here to protect against AccessViolationExceptions
const int HuffCodeLength = 257;
const int MaxSizeLength = HuffCodeLength - 1;
using (IMemoryOwner<short> huffcode = memoryAllocator.Allocate<short>(HuffCodeLength))
{
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());
ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
@ -63,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (short i = 1; i < 17; i++)
{
byte length = Unsafe.Add(ref codeLengthsRef, i);
for (short j = 0; j < length; j++)
for (short j = 0; j < length && x < MaxSizeLength; j++)
{
Unsafe.Add(ref sizesRef, x++) = i;
}
@ -84,7 +86,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref valOffsetRef, k) = (int)(si - code);
if (Unsafe.Add(ref sizesRef, si) == k)
{
while (Unsafe.Add(ref sizesRef, si) == k)
while (Unsafe.Add(ref sizesRef, si) == k && si < HuffCodeLength)
{
Unsafe.Add(ref huffcodeRef, si++) = (short)code++;
}
@ -100,19 +102,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// Generate non-spec lookup tables to speed up decoding.
const int FastBits = ScanDecoder.FastBits;
ref byte fastRef = ref this.Lookahead[0];
Unsafe.InitBlockUnaligned(ref fastRef, 0xFF, 1 << FastBits); // Flag for non-accelerated
ref byte lookaheadRef = ref this.Lookahead[0];
const uint MaxFastLength = 1 << FastBits;
Unsafe.InitBlockUnaligned(ref lookaheadRef, 0xFF, MaxFastLength); // Flag for non-accelerated
for (int i = 0; i < si; i++)
{
int size = Unsafe.Add(ref sizesRef, i);
if (size <= FastBits)
{
int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
int m = 1 << (FastBits - size);
for (int l = 0; l < m; l++)
int huffCode = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size);
int max = 1 << (FastBits - size);
for (int left = 0; left < max; left++)
{
Unsafe.Add(ref fastRef, c + l) = (byte)i;
Unsafe.Add(ref lookaheadRef, huffCode + left) = (byte)i;
}
}
}

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

@ -29,10 +29,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary>
JpegColorSpace ColorSpace { get; }
/// <summary>
/// Gets the number of bits used for precision.
/// </summary>
int Precision { get; }
/// <summary>
/// Gets the components.
/// </summary>
IEnumerable<IJpegComponent> Components { get; }
IJpegComponent[] Components { get; }
/// <summary>
/// Gets the quantization tables, in zigzag order.

13
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -112,13 +112,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <inheritdoc/>
public override int GetHashCode()
{
return HashHelpers.Combine(
this.MajorVersion.GetHashCode(),
HashHelpers.Combine(
this.MinorVersion.GetHashCode(),
HashHelpers.Combine(
this.DensityUnits.GetHashCode(),
HashHelpers.Combine(this.XDensity, this.YDensity))));
return HashCode.Combine(
this.MajorVersion,
this.MinorVersion,
this.DensityUnits,
this.XDensity,
this.YDensity);
}
}
}

15
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Primitives;
@ -38,6 +39,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// </summary>
private Size subSamplingDivisors;
/// <summary>
/// Defines the maximum value derived from the bitdepth
/// </summary>
private int maximumValue;
/// <summary>
/// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct.
/// </summary>
@ -48,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int qtIndex = component.QuantizationTableIndex;
this.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]);
this.subSamplingDivisors = component.SubSamplingDivisors;
this.maximumValue = (int)Math.Pow(2, decoder.Precision) - 1;
this.SourceBlock = default;
this.WorkspaceBlock1 = default;
@ -58,14 +65,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Processes 'sourceBlock' producing Jpeg color channel values from spectral values:
/// - Dequantize
/// - Applying IDCT
/// - Level shift by +128, clip to [0, 255]
/// - Level shift by +maximumValue/2, clip to [0, maximumValue]
/// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary>
/// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param>
/// <param name="maximumValue">The maximum value derived from the bitdepth.</param>
public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock,
in BufferArea<float> destArea)
in BufferArea<float> destArea,
float maximumValue)
{
ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref sourceBlock);
@ -78,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// To conform better to libjpeg we actually NEED TO loose precision here.
// This is because they store blocks as Int16 between all the operations.
// To be "more accurate", we need to emulate this by rounding!
this.WorkspaceBlock1.NormalizeColorsAndRoundInplace();
this.WorkspaceBlock1.NormalizeColorsAndRoundInplace(maximumValue);
this.WorkspaceBlock1.CopyTo(destArea, this.subSamplingDivisors.Width, this.subSamplingDivisors.Height);
}

3
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs

@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
public void CopyBlocksToColorBuffer()
{
var blockPp = new JpegBlockPostProcessor(this.ImagePostProcessor.RawJpeg, this.Component);
float maximumValue = (float)Math.Pow(2, this.ImagePostProcessor.RawJpeg.Precision) - 1;
for (int y = 0; y < this.BlockRowsPerStep; y++)
{
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.blockAreaSize.Width,
this.blockAreaSize.Height);
blockPp.ProcessBlockColorsInto(ref block, destArea);
blockPp.ProcessBlockColorsInto(ref block, destArea, maximumValue);
}
}

19
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs

@ -3,7 +3,6 @@
using System;
using System.Buffers;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -57,14 +56,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
this.configuration = configuration;
this.RawJpeg = rawJpeg;
IJpegComponent c0 = rawJpeg.Components.First();
IJpegComponent c0 = rawJpeg.Components[0];
this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep;
this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep);
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray();
this.ComponentProcessors = new JpegComponentPostProcessor[rawJpeg.Components.Length];
for (int i = 0; i < rawJpeg.Components.Length; i++)
{
this.ComponentProcessors[i] = new JpegComponentPostProcessor(memoryAllocator, this, rawJpeg.Components[i]);
}
this.rgbaBuffer = memoryAllocator.Allocate<Vector4>(rawJpeg.ImageSizeInPixels.Width);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace);
this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace, rawJpeg.Precision);
}
/// <summary>
@ -152,7 +157,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep);
Buffer2D<float>[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray();
var buffers = new Buffer2D<float>[this.ComponentProcessors.Length];
for (int i = 0; i < this.ComponentProcessors.Length; i++)
{
buffers[i] = this.ComponentProcessors[i].ColorBuffer;
}
for (int yy = this.PixelRowCounter; yy < maxY; yy++)
{

3
src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs

@ -474,7 +474,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Unsafe.Add(ref blockDataRef, zig) = (short)this.ExtendReceive(s);
}
}
} while (k < 64);
}
while (k < 64);
}
private void DecodeBlockProgressiveDC(

5
src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;

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

@ -3,7 +3,6 @@
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@ -33,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// The only supported precision
/// </summary>
public const int SupportedPrecision = 8;
private readonly int[] supportedPrecisions = { 8, 12 };
/// <summary>
/// The global configuration
@ -137,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Gets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel => this.ComponentCount * SupportedPrecision;
public int BitsPerPixel => this.ComponentCount * this.Frame.Precision;
/// <summary>
/// Gets the input stream.
@ -160,13 +159,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <inheritdoc/>
public JpegColorSpace ColorSpace { get; private set; }
/// <inheritdoc/>
public int Precision { get; private set; }
/// <summary>
/// Gets the components.
/// </summary>
public JpegComponent[] Components => this.Frame.Components;
/// <inheritdoc/>
IEnumerable<IJpegComponent> IRawJpegData.Components => this.Components;
IJpegComponent[] IRawJpegData.Components => this.Components;
/// <inheritdoc/>
public Block8x8F[] QuantizationTables { get; private set; }
@ -553,12 +555,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (this.exifData is null)
{
// The first 6 bytes (Exif00) will be skipped, because this is Jpeg specific
this.exifData = profile.Skip(Exif00).ToArray();
this.exifData = profile.AsSpan(Exif00).ToArray();
}
else
{
// If the EXIF information exceeds 64K, it will be split over multiple APP1 markers
this.ExtendProfile(ref this.exifData, profile.Skip(Exif00).ToArray());
this.ExtendProfile(ref this.exifData, profile.AsSpan(Exif00).ToArray());
}
}
}
@ -720,12 +722,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.InputStream.Read(this.temp, 0, remaining);
// We only support 8-bit precision.
if (this.temp[0] != SupportedPrecision)
// We only support 8-bit and 12-bit precision.
if (!this.supportedPrecisions.Contains(this.temp[0]))
{
throw new ImageFormatException("Only 8-Bit precision supported.");
throw new ImageFormatException("Only 8-Bit and 12-Bit precision supported.");
}
this.Precision = this.temp[0];
this.Frame = new JpegFrame
{
Extended = frameMarker.Marker == JpegConstants.Markers.SOF1,
@ -855,6 +859,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </summary>
private void ProcessStartOfScanMarker()
{
if (this.Frame is null)
{
throw new ImageFormatException("No readable SOFn (Start Of Frame) marker found.");
}
int selectorsCount = this.InputStream.ReadByte();
for (int i = 0; i < selectorsCount; i++)
{

37
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -547,7 +547,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteDefineHuffmanTables(int componentCount)
{
// Table identifiers.
Span<byte> headers = stackalloc byte[] { 0x00, 0x10, 0x01, 0x11 };
Span<byte> headers = stackalloc byte[]
{
0x00,
0x10,
0x01,
0x11
};
int markerlen = 2;
HuffmanSpec[] specs = HuffmanSpec.TheHuffmanSpecs;
@ -786,16 +792,37 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteStartOfFrame(int width, int height, int componentCount)
{
// "default" to 4:2:0
Span<byte> subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
Span<byte> chroma = stackalloc byte[] { 0x00, 0x01, 0x01 };
Span<byte> subsamples = stackalloc byte[]
{
0x22,
0x11,
0x11
};
Span<byte> chroma = stackalloc byte[]
{
0x00,
0x01,
0x01
};
switch (this.subsample)
{
case JpegSubsample.Ratio444:
subsamples = stackalloc byte[] { 0x11, 0x11, 0x11 };
subsamples = stackalloc byte[]
{
0x11,
0x11,
0x11
};
break;
case JpegSubsample.Ratio420:
subsamples = stackalloc byte[] { 0x22, 0x11, 0x11 };
subsamples = stackalloc byte[]
{
0x22,
0x11,
0x11
};
break;
}

5
src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.MetaData;

3
src/ImageSharp/Formats/Png/Filters/UpFilter.cs

@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
// Up(x) = Raw(x) - Prior(x)
resultBaseRef = 2;
for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */) {
for (int x = 0; x < scanline.Length; /* Note: ++x happens in the body to avoid one add operation */)
{
byte scan = Unsafe.Add(ref scanBaseRef, x);
byte above = Unsafe.Add(ref prevBaseRef, x);
++x;

3
src/ImageSharp/Formats/Png/PngConstants.cs

@ -26,7 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "png" };
public static readonly byte[] HeaderBytes = {
public static readonly byte[] HeaderBytes =
{
0x89, // Set the high bit.
0x50, // P
0x4E, // N

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

@ -237,7 +237,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
// Use the metadata to determine what quantization depth to use if no quantizer has been set.
if (this.quantizer == null)
if (this.quantizer is null)
{
this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
}

41
src/ImageSharp/ImageSharp.csproj

@ -1,10 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A cross-platform library for the processing of image files; written in C#</Description>
<AssemblyTitle>SixLabors.ImageSharp</AssemblyTitle>
<Authors>Six Labors and contributors</Authors>
<Company>Six Labors</Company>
<Copyright>Copyright (c) Six Labors and contributors.</Copyright>
<Product>SixLabors.ImageSharp</Product>
<Description>A cross-platform library for the processing of image files; written in C#</Description>
<NeutralLanguage>en</NeutralLanguage>
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard2.0;netcoreapp2.1;net472</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@ -16,39 +21,27 @@
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/SixLabors/ImageSharp</RepositoryUrl>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<DebugType Condition="$(codecov) != ''">full</DebugType>
<DebugType Condition="$(codecov) == ''">portable</DebugType>
<DebugSymbols>True</DebugSymbols>
<Features>IOperation</Features>
<LangVersion>Latest</LangVersion>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' OR '$(TargetFramework)' == 'net472' ">
<DefineConstants>$(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\standards\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61" PrivateAssets="All" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0007" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-dev000089" />
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta007">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
@ -56,14 +49,16 @@
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\ImageSharp.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSet>..\..\standards\SixLabors.ruleset</CodeAnalysisRuleSet>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
@ -126,6 +121,7 @@
<Generator>TextTemplatingFileGenerator</Generator>
</None>
</ItemGroup>
<ItemGroup>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime>
@ -203,6 +199,7 @@
<DependentUpon>PorterDuffFunctions.Generated.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>

2
src/ImageSharp/Memory/RowInterval.cs

@ -1,4 +1,4 @@
// Copyright(c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;

14
src/ImageSharp/MetaData/ImageProperty.cs

@ -99,19 +99,7 @@ namespace SixLabors.ImageSharp.MetaData
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
public override int GetHashCode()
{
unchecked
{
int hashCode = this.Name.GetHashCode();
if (this.Value != null)
{
hashCode = (hashCode * 397) ^ this.Value.GetHashCode();
}
return hashCode;
}
}
public override int GetHashCode() => HashCode.Combine(this.Name, this.Value);
/// <summary>
/// Returns the fully qualified type name of this instance.

6
src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs

@ -5,14 +5,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifConstants
{
public static readonly byte[] LittleEndianByteOrderMarker = {
public static readonly byte[] LittleEndianByteOrderMarker =
{
(byte)'I',
(byte)'I',
0x2A,
0x00,
};
public static readonly byte[] BigEndianByteOrderMarker = {
public static readonly byte[] BigEndianByteOrderMarker =
{
(byte)'M',
(byte)'M',
0x00,

6
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -27,9 +27,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
public ExifReader(byte[] exifData)
{
DebugGuard.NotNull(exifData, nameof(exifData));
this.exifData = exifData;
this.exifData = exifData ?? throw new ArgumentNullException(nameof(exifData));
}
private delegate TDataType ConverterMethod<TDataType>(ReadOnlySpan<byte> data);
@ -374,7 +372,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private void AddInvalidTag(ExifTag tag)
{
if (this.invalidTags == null)
if (this.invalidTags is null)
{
this.invalidTags = new List<ExifTag>();
}

2
src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs

@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
LensMake,
LensModel,
LensSerialNumber
};
};
/// <summary>
/// The collection of GPS tags

20
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -198,7 +198,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
/// <inheritdoc/>
public override int GetHashCode() => this.GetHashCode(this);
public override int GetHashCode()
{
return HashCode.Combine(this.Tag, this.DataType, this.Value);
}
/// <inheritdoc/>
public override string ToString()
@ -714,20 +717,5 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
throw new NotSupportedException();
}
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <param name="exif">
/// The instance of <see cref="ExifValue"/> to return the hash code for.
/// </param>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
private int GetHashCode(ExifValue exif)
{
int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode();
return hashCode ^ exif.Value?.GetHashCode() ?? hashCode;
}
}
}

21
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs

@ -154,18 +154,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = (int)this.Type;
hashCode = (hashCode * 397) ^ this.G.GetHashCode();
hashCode = (hashCode * 397) ^ this.A.GetHashCode();
hashCode = (hashCode * 397) ^ this.B.GetHashCode();
hashCode = (hashCode * 397) ^ this.C.GetHashCode();
hashCode = (hashCode * 397) ^ this.D.GetHashCode();
hashCode = (hashCode * 397) ^ this.E.GetHashCode();
hashCode = (hashCode * 397) ^ this.F.GetHashCode();
return hashCode;
}
return HashCode.Combine(
this.Type,
this.G.GetHashCode(),
this.A.GetHashCode(),
this.B.GetHashCode(),
this.C.GetHashCode(),
this.D.GetHashCode(),
this.E.GetHashCode(),
this.F.GetHashCode());
}
}
}

11
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs

@ -73,13 +73,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hashCode = (int)this.CurveType;
hashCode = (hashCode * 397) ^ (this.XyzValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.ResponseArrays?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.CurveType,
this.XyzValues,
this.ResponseArrays);
}
private bool EqualsResponseArray(IccResponseCurve other)

8
src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs

@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (int)this.Signature * 397;
}
}
public override int GetHashCode() => this.Signature.GetHashCode();
}
}

11
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs

@ -110,13 +110,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (int)this.ColorantType;
hashCode = (hashCode * 397) ^ (this.ChannelValues?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.ColorantType,
this.ChannelValues);
}
private static double[][] GetColorantArray(IccColorantEncoding colorantType)

5
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs

@ -70,10 +70,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.ColorantNumber?.GetHashCode() ?? 0);
}
return HashCode.Combine(this.Signature, this.ColorantNumber);
}
}
}

5
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs

@ -72,10 +72,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.ColorantData?.GetHashCode() ?? 0);
}
return HashCode.Combine(this.Signature, this.ColorantData);
}
}
}

17
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs

@ -121,16 +121,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (this.PostScriptProductName?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.RenderingIntent0Crd?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.RenderingIntent1Crd?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.RenderingIntent2Crd?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.RenderingIntent3Crd?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.PostScriptProductName,
this.RenderingIntent0Crd,
this.RenderingIntent1Crd,
this.RenderingIntent2Crd,
this.RenderingIntent3Crd);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs

@ -116,12 +116,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.CurveData?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.CurveData);
}
}

11
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs

@ -91,13 +91,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (this.Data?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ this.IsAscii.GetHashCode();
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.Data,
this.IsAscii);
}
}
}

5
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs

@ -66,10 +66,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ this.Value.GetHashCode();
}
return HashCode.Combine(this.Signature, this.Value);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs

@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}

31
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs

@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
internal sealed class IccLut16TagDataEntry : IccTagDataEntry, IEquatable<IccLut16TagDataEntry>
{
private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
private static readonly float[,] IdentityMatrix =
{
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
};
/// <summary>
/// Initializes a new instance of the <see cref="IccLut16TagDataEntry"/> class.
@ -62,17 +67,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
: base(IccTypeSignature.Lut16, tagSignature)
{
Guard.NotNull(matrix, nameof(matrix));
Guard.NotNull(inputValues, nameof(inputValues));
Guard.NotNull(clutValues, nameof(clutValues));
Guard.NotNull(outputValues, nameof(outputValues));
bool is3By3 = matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3;
Guard.IsTrue(is3By3, nameof(matrix), "Matrix must have a size of three by three");
this.Matrix = this.CreateMatrix(matrix);
this.InputValues = inputValues;
this.ClutValues = clutValues;
this.OutputValues = outputValues;
this.InputValues = inputValues ?? throw new ArgumentNullException(nameof(inputValues));
this.ClutValues = clutValues ?? throw new ArgumentNullException(nameof(clutValues));
this.OutputValues = outputValues ?? throw new ArgumentNullException(nameof(outputValues));
Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size");
Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size");
@ -143,15 +145,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.Matrix.GetHashCode();
hashCode = (hashCode * 397) ^ (this.InputValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.OutputValues?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.Matrix,
this.InputValues,
this.ClutValues,
this.OutputValues);
}
private Matrix4x4 CreateMatrix(float[,] matrix)

31
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs

@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
internal sealed class IccLut8TagDataEntry : IccTagDataEntry, IEquatable<IccLut8TagDataEntry>
{
private static readonly float[,] IdentityMatrix = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
private static readonly float[,] IdentityMatrix =
{
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
};
/// <summary>
/// Initializes a new instance of the <see cref="IccLut8TagDataEntry"/> class.
@ -62,17 +67,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
: base(IccTypeSignature.Lut8, tagSignature)
{
Guard.NotNull(matrix, nameof(matrix));
Guard.NotNull(inputValues, nameof(inputValues));
Guard.NotNull(clutValues, nameof(clutValues));
Guard.NotNull(outputValues, nameof(outputValues));
bool is3By3 = matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3;
Guard.IsTrue(is3By3, nameof(matrix), "Matrix must have a size of three by three");
this.Matrix = this.CreateMatrix(matrix);
this.InputValues = inputValues;
this.ClutValues = clutValues;
this.OutputValues = outputValues;
this.InputValues = inputValues ?? throw new ArgumentNullException(nameof(inputValues));
this.ClutValues = clutValues ?? throw new ArgumentNullException(nameof(clutValues));
this.OutputValues = outputValues ?? throw new ArgumentNullException(nameof(outputValues));
Guard.IsTrue(this.InputChannelCount == clutValues.InputChannelCount, nameof(clutValues), "Input channel count does not match the CLUT size");
Guard.IsTrue(this.OutputChannelCount == clutValues.OutputChannelCount, nameof(clutValues), "Output channel count does not match the CLUT size");
@ -146,15 +148,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.Matrix.GetHashCode();
hashCode = (hashCode * 397) ^ (this.InputValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.OutputValues?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.Matrix,
this.InputValues,
this.ClutValues,
this.OutputValues);
}
private Matrix4x4 CreateMatrix(float[,] matrix)

28
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs

@ -183,19 +183,21 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.InputChannelCount;
hashCode = (hashCode * 397) ^ this.OutputChannelCount;
hashCode = (hashCode * 397) ^ this.Matrix3x3.GetHashCode();
hashCode = (hashCode * 397) ^ this.Matrix3x1.GetHashCode();
hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveB?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveM?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveA?.GetHashCode() ?? 0);
return hashCode;
}
#pragma warning disable SA1129 // Do not use default value type constructor
var hashCode = new HashCode();
#pragma warning restore SA1129 // Do not use default value type constructor
hashCode.Add(this.Signature);
hashCode.Add(this.InputChannelCount);
hashCode.Add(this.OutputChannelCount);
hashCode.Add(this.Matrix3x3);
hashCode.Add(this.Matrix3x1);
hashCode.Add(this.ClutValues);
hashCode.Add(this.CurveB);
hashCode.Add(this.CurveM);
hashCode.Add(this.CurveA);
return hashCode.ToHashCode();
}
private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves)

28
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs

@ -183,19 +183,21 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.InputChannelCount;
hashCode = (hashCode * 397) ^ this.OutputChannelCount;
hashCode = (hashCode * 397) ^ this.Matrix3x3.GetHashCode();
hashCode = (hashCode * 397) ^ this.Matrix3x1.GetHashCode();
hashCode = (hashCode * 397) ^ (this.ClutValues?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveB?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveM?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.CurveA?.GetHashCode() ?? 0);
return hashCode;
}
#pragma warning disable SA1129 // Do not use default value type constructor
var hashCode = new HashCode();
#pragma warning restore SA1129 // Do not use default value type constructor
hashCode.Add(this.Signature);
hashCode.Add(this.InputChannelCount);
hashCode.Add(this.OutputChannelCount);
hashCode.Add(this.Matrix3x3);
hashCode.Add(this.Matrix3x1);
hashCode.Add(this.ClutValues);
hashCode.Add(this.CurveB);
hashCode.Add(this.CurveM);
hashCode.Add(this.CurveA);
return hashCode.ToHashCode();
}
private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves)

17
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs

@ -106,16 +106,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (int)this.Observer;
hashCode = (hashCode * 397) ^ this.XyzBacking.GetHashCode();
hashCode = (hashCode * 397) ^ (int)this.Geometry;
hashCode = (hashCode * 397) ^ this.Flare.GetHashCode();
hashCode = (hashCode * 397) ^ (int)this.Illuminant;
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.Observer,
this.XyzBacking,
this.Geometry,
this.Flare,
this.Illuminant);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs

@ -66,12 +66,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Texts?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Texts);
}
}

13
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs

@ -90,14 +90,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.InputChannelCount;
hashCode = (hashCode * 397) ^ this.OutputChannelCount;
hashCode = (hashCode * 397) ^ (this.Data?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.InputChannelCount,
this.OutputChannelCount,
this.Data);
}
}
}

18
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs

@ -83,6 +83,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
if (colors.Length > 0)
{
coordinateCount = colors[0].DeviceCoordinates?.Length ?? 0;
Guard.IsFalse(colors.Any(t => (t.DeviceCoordinates?.Length ?? 0) != coordinateCount), nameof(colors), "Device coordinate count must be the same for all colors");
}
@ -154,16 +155,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.CoordinateCount;
hashCode = (hashCode * 397) ^ (this.Prefix?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.Suffix?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ this.VendorFlags;
hashCode = (hashCode * 397) ^ (this.Colors?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.CoordinateCount,
this.Prefix,
this.Suffix,
this.VendorFlags,
this.Colors);
}
}
}

10
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
public IccParametricCurveTagDataEntry(IccParametricCurve curve, IccProfileTag tagSignature)
: base(IccTypeSignature.ParametricCurve, tagSignature)
{
this.Curve = curve;
this.Curve = curve ?? throw new ArgumentNullException(nameof(curve));
}
/// <summary>
@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Curve?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Curve);
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs

@ -67,12 +67,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Descriptions?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Descriptions);
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs

@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Data?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Data);
}
}

11
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs

@ -83,13 +83,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ this.ChannelCount.GetHashCode();
hashCode = (hashCode * 397) ^ (this.Curves?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.ChannelCount,
this.Curves);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs

@ -77,13 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (int)this.Flags;
hashCode = (hashCode * 397) ^ (this.Channels?.GetHashCode() ?? 0);
return hashCode;
}
return HashCode.Combine(this.Signature, this.Flags, this.Channels);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs

@ -65,12 +65,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.SignatureData?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.SignatureData);
}
}

17
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs

@ -166,16 +166,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hashCode = base.GetHashCode();
hashCode = (hashCode * 397) ^ (this.Ascii?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.Unicode?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (this.ScriptCode?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (int)this.UnicodeLanguageCode;
hashCode = (hashCode * 397) ^ this.ScriptCodeCode.GetHashCode();
return hashCode;
}
return HashCode.Combine(
this.Signature,
this.Ascii,
this.Unicode,
this.ScriptCode,
this.UnicodeLanguageCode,
this.ScriptCodeCode);
}
}
}

8
src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs

@ -64,12 +64,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (this.Text?.GetHashCode() ?? 0);
}
}
public override int GetHashCode() => HashCode.Combine(this.Signature, this.Text);
}
}

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

Loading…
Cancel
Save