Browse Source

Merge branch 'master' into js/projective-transforms

af/merge-core
James Jackson-South 8 years ago
parent
commit
78d8138873
  1. 32
      README.md
  2. 35
      appveyor.yml
  3. 14
      build.ps1
  4. 67
      run-tests.ps1
  5. 8
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  6. 2
      src/ImageSharp.Drawing/Processing/Drawing/DrawRectangleExtensions.cs
  7. 4
      src/ImageSharp.Drawing/Processing/Drawing/FillRectangleExtensions.cs
  8. 2
      src/ImageSharp/ColorSpaces/CieLab.cs
  9. 7
      src/ImageSharp/ColorSpaces/CieLch.cs
  10. 7
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  11. 7
      src/ImageSharp/ColorSpaces/CieLuv.cs
  12. 7
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  13. 7
      src/ImageSharp/ColorSpaces/CieXyy.cs
  14. 7
      src/ImageSharp/ColorSpaces/CieXyz.cs
  15. 7
      src/ImageSharp/ColorSpaces/Cmyk.cs
  16. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs
  17. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs
  18. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs
  19. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs
  20. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs
  21. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs
  22. 3
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs
  23. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs
  24. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs
  25. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs
  26. 9
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs
  27. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs
  28. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs
  29. 7
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs
  30. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs
  31. 7
      src/ImageSharp/ColorSpaces/Hsl.cs
  32. 7
      src/ImageSharp/ColorSpaces/Hsv.cs
  33. 7
      src/ImageSharp/ColorSpaces/HunterLab.cs
  34. 7
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  35. 7
      src/ImageSharp/ColorSpaces/Lms.cs
  36. 7
      src/ImageSharp/ColorSpaces/Rgb.cs
  37. 7
      src/ImageSharp/ColorSpaces/YCbCr.cs
  38. 33
      src/ImageSharp/Common/Extensions/ByteExtensions.cs
  39. 28
      src/ImageSharp/Common/Helpers/Guard.cs
  40. 4
      src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
  41. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  42. 2
      src/ImageSharp/Formats/Bmp/BmpConstants.cs
  43. 4
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  44. 16
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  45. 4
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  46. 17
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  47. 28
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  48. 10
      src/ImageSharp/Formats/Gif/GifConstants.cs
  49. 4
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  50. 4
      src/ImageSharp/Formats/Gif/PackedField.cs
  51. 19
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  52. 12
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  53. 7
      src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
  54. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
  55. 11
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  56. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs
  57. 11
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  58. 9
      src/ImageSharp/Formats/Png/PngChunk.cs
  59. 152
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  60. 122
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  61. 48
      src/ImageSharp/Formats/Png/PngHeader.cs
  62. 25
      src/ImageSharp/Formats/Png/Zlib/Adler32.cs
  63. 23
      src/ImageSharp/Formats/Png/Zlib/Crc32.cs
  64. 22
      src/ImageSharp/Formats/Png/Zlib/IChecksum.cs
  65. 2
      src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs
  66. 1
      src/ImageSharp/IImageFrameCollection.cs
  67. 84
      src/ImageSharp/IO/BigEndianBitConverter.cs
  68. 277
      src/ImageSharp/IO/EndianBinaryReader.cs
  69. 188
      src/ImageSharp/IO/EndianBinaryWriter.cs
  70. 61
      src/ImageSharp/IO/EndianBitConverter.Conversion.cs
  71. 143
      src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs
  72. 137
      src/ImageSharp/IO/EndianBitConverter.GetBytes.cs
  73. 139
      src/ImageSharp/IO/EndianBitConverter.ToType.cs
  74. 127
      src/ImageSharp/IO/EndianBitConverter.cs
  75. 81
      src/ImageSharp/IO/LittleEndianBitConverter.cs
  76. 8
      src/ImageSharp/Image.FromStream.cs
  77. 16
      src/ImageSharp/Image.LoadPixelData.cs
  78. 24
      src/ImageSharp/ImageExtensions.cs
  79. 7
      src/ImageSharp/ImageFrame.LoadPixelData.cs
  80. 36
      src/ImageSharp/ImageFrame{TPixel}.cs
  81. 4
      src/ImageSharp/ImageSharp.csproj
  82. 2
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  83. 52
      src/ImageSharp/Memory/SpanHelper.cs
  84. 25
      src/ImageSharp/MetaData/ImageProperty.cs
  85. 50
      src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
  86. 60
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  87. 394
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  88. 281
      src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
  89. 191
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  90. 410
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  91. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  92. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  93. 25
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs
  94. 6
      src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs
  95. 2
      src/ImageSharp/PixelAccessor{TPixel}.cs
  96. 18
      src/ImageSharp/PixelFormats/Alpha8.cs
  97. 20
      src/ImageSharp/PixelFormats/Argb32.cs
  98. 20
      src/ImageSharp/PixelFormats/Bgr24.cs
  99. 16
      src/ImageSharp/PixelFormats/Bgr565.cs
  100. 19
      src/ImageSharp/PixelFormats/Bgra32.cs

32
README.md

@ -1,16 +1,21 @@
<h1 align="center">
<img src="https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-256.png" alt="ImageSharp" width="175"/>
<br>
ImageSharp
<br>
<br>
<a href="https://raw.githubusercontent.com/SixLabors/ImageSharp/master/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="GitHub license" data-canonical-src="https://img.shields.io/badge/license-Apache%202-blue.svg" style="max-width:100%;"></a>
<a href="https://gitter.im/ImageSharp/General?utm_source=badge&amp;utm_medium=badge&amp;utm_campaign=pr-badge&amp;utm_content=badge"><img src="https://badges.gitter.im/Join%20Chat.svg" alt="Gitter" data-canonical-src="https://badges.gitter.im/Join%20Chat.svg" style="max-width:100%;"></a>
<a href="https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&amp;text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&amp;url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&amp;via=sixlabors"><img src="https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter" alt="Twitter" data-canonical-src="https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter" style="max-width:100%;"></a>
<a href="#backers"><img src="https://opencollective.com/imagesharp/backers/badge.svg" alt="OpenCollective" data-canonical-src="https://opencollective.com/imagesharp/backers/badge.svg" style="max-width:100%;"></a>
<a href="#sponsors"><img src="https://opencollective.com/imagesharp/sponsors/badge.svg" alt="OpenCollective" data-canonical-src="https://opencollective.com/imagesharp/sponsors/badge.svg" style="max-width:100%;"></a>
<img src="https://raw.githubusercontent.com/SixLabors/Branding/master/icons/imagesharp/sixlabors.imagesharp.512.png" alt="SixLabors.ImageSharp" width="256"/>
<br/>
SixLabors.ImageSharp
</h1>
<div align="center">
[![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/SixLabors/ImageSharp/master/LICENSE)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ImageSharp/General?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors)
[![OpenCollective](https://opencollective.com/imagesharp/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/imagesharp/sponsors/badge.svg)](#sponsors)
</div>
### **ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API.
Designed to democratize image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
@ -21,7 +26,7 @@ Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and emb
### Installation
Install stable releases via Nuget;evelopment releases are available via MyGet.
Install stable releases via Nuget; development releases are available via MyGet.
| Package Name | Release (NuGet) | Nightly (MyGet) |
|--------------------------------|-----------------|-----------------|
@ -51,7 +56,8 @@ The **ImageSharp** library is made up of multiple packages:
### Questions?
Do you have questions? We are happy to help! Please [join our gitter channel](https://gitter.im/ImageSharp/General), or ask them on [stackoverflow](https://stackoverflow.com) using the `ImageSharp` tag.
- Do you have questions? We are happy to help! Please [join our gitter channel](https://gitter.im/ImageSharp/General), or ask them on [stackoverflow](https://stackoverflow.com) using the `ImageSharp` tag. **Do not** open issues for questions!
- Please read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening issues or pull requests!
### API
@ -122,7 +128,7 @@ git clone https://github.com/SixLabors/ImageSharp
### How can you help?
Please... Spread the word, contribute algorithms, submit performance improvements, unit tests, no input is too little.
Please... Spread the word, contribute algorithms, submit performance improvements, unit tests, no input is too little. Make sure to read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening a PR.
### The ImageSharp Team

35
appveyor.yml

@ -4,6 +4,39 @@ image: Visual Studio 2017
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true
environment:
matrix:
- target_framework: net471
is_32bit: True
- target_framework: net462
is_32bit: False
- target_framework: net462
is_32bit: True
- target_framework: net471
is_32bit: False
- target_framework: netcoreapp2.0
is_32bit: False
#- target_framework: netcoreapp2.0 # As far as I understand, 32 bit test execution is not supported by "dotnet xunit"
# is_32bit: True
#- target_framework: mono
# is_32bit: False
#- target_framework: mono
# is_32bit: True
#- target_framework: net47
# is_32bit: False
#- target_framework: net47
# is_32bit: True
install:
- ps: |
if ($env:target_framework -eq "mono") {
if ($env:is_32bit -eq "True") {
cinst mono --x86
} else {
cinst mono
}
}
before_build:
- git submodule -q update --init
- cmd: dotnet --version
@ -12,7 +45,7 @@ build_script:
- cmd: build.cmd
test_script:
- tests\CodeCoverage\CodeCoverage.cmd
- ps: .\run-tests.ps1 $env:target_framework $env:is_32bit
after_test:
- cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%APPVEYOR_BUILD_VERSION%.nupkg"

14
build.ps1

@ -100,9 +100,17 @@ dotnet build -c Release /p:packageversion=$version
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
if ( $env:CI -ne "True") {
dotnet test ./tests/ImageSharp.Tests/ImageSharp.Tests.csproj --no-build -c Release
}
#
# TODO: DO WE NEED TO RUN TESTS IMPLICITLY?
#
# if ( $env:CI -ne "True") {
# cd ./tests/ImageSharp.Tests/
# dotnet xunit -nobuild -c Release -f netcoreapp2.0 --fx-version 2.0.0
# ./RunExtendedTests.cmd
# cd ../..
# }
#
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
Write-Host "Packaging projects"

67
run-tests.ps1

@ -0,0 +1,67 @@
param(
[string]$targetFramework,
[string]$is32Bit = "False"
)
if (!$targetFramework){
Write-Host "run-tests.ps1 ERROR: targetFramework is undefined!"
exit 1
}
function VerifyPath($path, $errorMessage) {
if (!(Test-Path -Path $path)) {
Write-Host "run-tests.ps1 $errorMessage `n $xunitRunnerPath"
exit 1
}
}
if ( ($targetFramework -eq "netcoreapp2.0") -and ($env:CI -eq "True") -and ($is32Bit -ne "True")) {
# We execute CodeCoverage.cmd only for one specific job on CI (netcoreapp2.0 + 64bit )
$testRunnerCmd = ".\tests\CodeCoverage\CodeCoverage.cmd"
}
elseif ($targetFramework -eq "mono") {
$testDllPath = "$PSScriptRoot\tests\ImageSharp.Tests\bin\Release\net462\SixLabors.ImageSharp.Tests.dll"
VerifyPath($testDllPath, "test dll missing:")
$xunitRunnerPath = "${env:HOMEPATH}\.nuget\packages\xunit.runner.console\2.3.1\tools\net452\"
VerifyPath($xunitRunnerPath, "xunit console runner is missing on path:")
cd "$xunitRunnerPath"
if ($is32Bit -ne "True") {
$monoPath = "${env:PROGRAMFILES}\Mono\bin\mono.exe"
}
else {
$monoPath = "${env:ProgramFiles(x86)}\Mono\bin\mono.exe"
}
VerifyPath($monoPath, "mono runtime missing:")
$testRunnerCmd = "& `"${monoPath}`" .\xunit.console.exe `"${testDllPath}`""
}
else {
cd .\tests\ImageSharp.Tests
$xunitArgs = "-nobuild -c Release -framework $targetFramework"
if ($targetFramework -eq "netcoreapp2.0") {
# There were issues matching the correct installed runtime if we do not specify it explicitly:
$xunitArgs += " --fx-version 2.0.0"
}
if ($is32Bit -eq "True") {
$xunitArgs += " -x86"
}
$testRunnerCmd = "dotnet xunit $xunitArgs"
}
Write-Host "running:"
Write-Host $testRunnerCmd
Write-Host "..."
Invoke-Expression $testRunnerCmd
cd $PSScriptRoot
exit $LASTEXITCODE

8
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -11,7 +11,7 @@
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>
<PackageId>SixLabors.ImageSharp.Drawing</PackageId>
<PackageTags>Image Draw Shape Path Font</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png</PackageIconUrl>
<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>
@ -36,9 +36,9 @@
<ProjectReference Include="..\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0004" />
<PackageReference Include="SixLabors.Shapes.Text" Version="1.0.0-beta0004" />
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>All</PrivateAssets>

2
src/ImageSharp.Drawing/Processing/Drawing/DrawRectangleExtensions.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Draw<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IPen<TPixel> pen, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Draw(options, pen, new RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height));
=> source.Draw(options, pen, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Draws the outline of the rectangle with the provided pen.

4
src/ImageSharp.Drawing/Processing/Drawing/FillRectangleExtensions.cs

@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, GraphicsOptions options, IBrush<TPixel> brush, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(options, brush, new RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height));
=> source.Fill(options, brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Drawing
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> Fill<TPixel>(this IImageProcessingContext<TPixel> source, IBrush<TPixel> brush, RectangleF shape)
where TPixel : struct, IPixel<TPixel>
=> source.Fill(brush, new RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height));
=> source.Fill(brush, new RectangularPolygon(shape.X, shape.Y, shape.Width, shape.Height));
/// <summary>
/// Flood fills the image in the shape of the provided rectangle with the specified brush.

2
src/ImageSharp/ColorSpaces/CieLab.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
/// </summary>
public static readonly CieLab Empty = default(CieLab);
public static readonly CieLab Empty = default;
/// <summary>
/// The backing vector for SIMD support.

7
src/ImageSharp/ColorSpaces/CieLch.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLch)
{
return this.Equals((CieLch)obj);
}
return false;
return obj is CieLch other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLchuv)
{
return this.Equals((CieLchuv)obj);
}
return false;
return obj is CieLchuv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -196,12 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLuv)
{
return this.Equals((CieLuv)obj);
}
return false;
return obj is CieLuv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -132,12 +132,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyChromaticityCoordinates)
{
return this.Equals((CieXyChromaticityCoordinates)obj);
}
return false;
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyy)
{
return this.Equals((CieXyy)obj);
}
return false;
return obj is CieXyy other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyz)
{
return this.Equals((CieXyz)obj);
}
return false;
return obj is CieXyz other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Cmyk)
{
return this.Equals((Cmyk)obj);
}
return false;
return obj is Cmyk other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
float l = input.L, a = input.A, b = input.B;
float fy = (l + 16) / 116F;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce
{
@ -44,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieLch input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch Convert(CieLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, a = input.A, b = input.B;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieLchuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv Convert(CieLuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, a = input.U, b = input.V;

3
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs

@ -3,7 +3,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.ColorSpaces;
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce
{
@ -16,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
float l = input.L, u = input.U, v = input.V;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap
/// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy
/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf
/// </remarks>
public static class LmsAdaptationMatrix
internal static class LmsAdaptationMatrix
{
/// <summary>
/// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65)

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public class GammaCompanding : ICompanding
internal class GammaCompanding : ICompanding
{
/// <summary>
/// Initializes a new instance of the <see cref="GammaCompanding"/> class.

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public class LCompanding : ICompanding
internal class LCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

9
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="IRgbWorkingSpace"/>.
/// </summary>
internal struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RgbPrimariesChromaticityCoordinates)
{
return this.Equals((RgbPrimariesChromaticityCoordinates)obj);
}
return false;
return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs

@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <see href="http://en.wikipedia.org/wiki/Rec._2020"/>
/// For 10-bits, companding is identical to <see cref="Rec709Companding"/>
/// </remarks>
public class Rec2020Companding : ICompanding
internal class Rec2020Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <remarks>
/// http://en.wikipedia.org/wiki/Rec._709
/// </remarks>
public class Rec709Companding : ICompanding
internal class Rec709Companding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

7
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs

@ -73,12 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RgbWorkingSpace)
{
return this.Equals((RgbWorkingSpace)obj);
}
return false;
return obj is RgbWorkingSpace other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
/// </remarks>
public class SRgbCompanding : ICompanding
internal class SRgbCompanding : ICompanding
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

7
src/ImageSharp/ColorSpaces/Hsl.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Hsl)
{
return this.Equals((Hsl)obj);
}
return false;
return obj is Hsl other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Hsv.cs

@ -202,12 +202,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Hsv)
{
return this.Equals((Hsv)obj);
}
return false;
return obj is Hsv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -190,12 +190,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is HunterLab)
{
return this.Equals((HunterLab)obj);
}
return false;
return obj is HunterLab other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -182,12 +182,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is LinearRgb)
{
return this.Equals((LinearRgb)obj);
}
return false;
return obj is LinearRgb other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Lms.cs

@ -149,12 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Lms)
{
return this.Equals((Lms)obj);
}
return false;
return obj is Lms other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Rgb.cs

@ -204,12 +204,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Rgb)
{
return this.Equals((Rgb)obj);
}
return false;
return obj is Rgb other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -152,12 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is YCbCr)
{
return this.Equals((YCbCr)obj);
}
return false;
return obj is YCbCr other && this.Equals(other);
}
/// <inheritdoc/>

33
src/ImageSharp/Common/Extensions/ByteExtensions.cs

@ -12,39 +12,6 @@ namespace SixLabors.ImageSharp
/// </summary>
internal static class ByteExtensions
{
/// <summary>
/// Optimized <see cref="T:byte[]"/> reversal algorithm.
/// </summary>
/// <param name="source">The byte array.</param>
public static void ReverseBytes(this byte[] source)
{
ReverseBytes(source, 0, source.Length);
}
/// <summary>
/// Optimized <see cref="T:byte[]"/> reversal algorithm.
/// </summary>
/// <param name="source">The byte array.</param>
/// <param name="index">The index.</param>
/// <param name="length">The length.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ReverseBytes(this byte[] source, int index, int length)
{
Guard.NotNull(source, nameof(source));
int i = index;
int j = index + length - 1;
while (i < j)
{
byte temp = source[i];
source[i] = source[j];
source[j] = temp;
i++;
j--;
}
}
/// <summary>
/// Returns a reference to the given position of the array unsafe casted to <see cref="ImageSharp.PixelFormats.Rgb24"/>.
/// </summary>

28
src/ImageSharp/Common/Helpers/Guard.cs

@ -230,18 +230,36 @@ namespace SixLabors.ImageSharp
}
/// <summary>
/// Verifies, that the `target` span has the length of 'minSpan', or longer.
/// Verifies, that the `source` span has the length of 'minSpan', or longer.
/// </summary>
/// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="target">The target span.</param>
/// <param name="source">The source span.</param>
/// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is true
/// <paramref name="source"/> is true
/// </exception>
public static void MustBeSizedAtLeast<T>(ReadOnlySpan<T> source, int minLength, string parameterName)
{
if (source.Length < minLength)
{
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
}
}
/// <summary>
/// Verifies, that the `source` span has the length of 'minSpan', or longer.
/// </summary>
/// <typeparam name="T">The element type of the spans</typeparam>
/// <param name="source">The target span.</param>
/// <param name="minLength">The minimum length.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <exception cref="ArgumentException">
/// <paramref name="source"/> is true
/// </exception>
public static void MustBeSizedAtLeast<T>(Span<T> target, int minLength, string parameterName)
public static void MustBeSizedAtLeast<T>(Span<T> source, int minLength, string parameterName)
{
if (target.Length < minLength)
if (source.Length < minLength)
{
throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName);
}

4
src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs

@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// 32 bits per pixel. Each pixel consists of 4 bytes.
/// </summary>
Pixel32 = 4,
Pixel32 = 4
}
}
}

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

@ -58,4 +58,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
PNG = 5
}
}
}

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

@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "bm", "bmp", "dip" };
}
}
}

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

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
}
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Identify(stream);
}

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

@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
using (Buffer2D<byte> buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
{
this.UncompressRle8(width, buffer.Span);
@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
using (IManagedByteBuffer buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
@ -581,13 +581,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader
{
Type = BitConverter.ToInt16(data, 0),
FileSize = BitConverter.ToInt32(data, 2),
Reserved = BitConverter.ToInt32(data, 6),
Offset = BitConverter.ToInt32(data, 10)
};
this.fileHeader = new BmpFileHeader(
type: BitConverter.ToInt16(data, 0),
fileSize: BitConverter.ToInt32(data, 2),
reserved: BitConverter.ToInt32(data, 6),
offset: BitConverter.ToInt32(data, 10));
}
/// <summary>

4
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -28,4 +26,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
encoder.Encode(image, stream);
}
}
}
}

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

@ -55,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
BmpInfoHeader infoHeader = new BmpInfoHeader
var infoHeader = new BmpInfoHeader
{
HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
Height = image.Height,
@ -69,12 +69,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ClrImportant = 0
};
BmpFileHeader fileHeader = new BmpFileHeader
{
Type = 19778, // BM
Offset = 54,
FileSize = 54 + infoHeader.ImageSize
};
var fileHeader = new BmpFileHeader(
type: 19778, // BM
offset: 54,
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader);
@ -92,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </param>
private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader)
private static void WriteHeader(EndianBinaryWriter writer, in BmpFileHeader fileHeader)
{
writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize);

28
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -13,35 +13,43 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
/// </remarks>
internal sealed class BmpFileHeader
internal readonly struct BmpFileHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.
/// </summary>
public const int Size = 14;
public BmpFileHeader(short type, int fileSize, int reserved, int offset)
{
this.Type = type;
this.FileSize = fileSize;
this.Reserved = reserved;
this.Offset = offset;
}
/// <summary>
/// Gets or sets the Bitmap identifier.
/// Gets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x4D
/// (Hex code points for B and M)
/// </summary>
public short Type { get; set; }
public short Type { get; }
/// <summary>
/// Gets or sets the size of the bitmap file in bytes.
/// Gets the size of the bitmap file in bytes.
/// </summary>
public int FileSize { get; set; }
public int FileSize { get; }
/// <summary>
/// Gets or sets any reserved data; actual value depends on the application
/// Gets any reserved data; actual value depends on the application
/// that creates the image.
/// </summary>
public int Reserved { get; set; }
public int Reserved { get; }
/// <summary>
/// Gets or sets the offset, i.e. starting address, of the byte where
/// Gets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found.
/// </summary>
public int Offset { get; set; }
public int Offset { get; }
}
}
}

10
src/ImageSharp/Formats/Gif/GifConstants.cs

@ -21,6 +21,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public const string FileVersion = "89a";
/// <summary>
/// The ASCII encoded bytes used to identify the GIF file.
/// </summary>
internal static readonly byte[] MagicNumber = Encoding.UTF8.GetBytes(FileType + FileVersion);
/// <summary>
/// The extension block introducer <value>!</value>.
/// </summary>
@ -41,6 +46,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public const string ApplicationIdentification = "NETSCAPE2.0";
/// <summary>
/// The ASCII encoded application identification bytes.
/// </summary>
internal static readonly byte[] ApplicationIdentificationBytes = Encoding.UTF8.GetBytes(ApplicationIdentification);
/// <summary>
/// The application block size.
/// </summary>

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

@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteHeader(EndianBinaryWriter writer)
{
writer.Write((GifConstants.FileType + GifConstants.FileVersion).ToCharArray());
writer.Write(GifConstants.MagicNumber);
}
/// <summary>
@ -213,7 +213,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
writer.Write(this.buffer, 0, 3);
writer.Write(GifConstants.ApplicationIdentification.ToCharArray()); // NETSCAPE2.0
writer.Write(GifConstants.ApplicationIdentificationBytes); // NETSCAPE2.0
writer.Write((byte)3); // Application block length
writer.Write((byte)1); // Data sub-block index (always 1)

4
src/ImageSharp/Formats/Gif/PackedField.cs

@ -169,9 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public override bool Equals(object obj)
{
PackedField? field = obj as PackedField?;
return this.Byte == field?.Byte;
return obj is PackedField other && this.Equals(other);
}
/// <inheritdoc/>

19
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -241,19 +241,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
bld.Append('[');
var sb = new StringBuilder();
sb.Append('[');
for (int i = 0; i < Size; i++)
{
bld.Append(this[i]);
sb.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
sb.Append(',');
}
}
bld.Append(']');
return bld.ToString();
sb.Append(']');
return sb.ToString();
}
/// <inheritdoc />
@ -273,12 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is Block8x8 && this.Equals((Block8x8)obj);
return obj is Block8x8 other && this.Equals(other);
}
/// <inheritdoc />

12
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -496,19 +496,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
bld.Append('[');
var sb = new StringBuilder();
sb.Append('[');
for (int i = 0; i < Size; i++)
{
bld.Append(this[i]);
sb.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
sb.Append(',');
}
}
bld.Append(']');
return bld.ToString();
sb.Append(']');
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs

@ -94,12 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is AdobeMarker && this.Equals((AdobeMarker)obj);
return obj is AdobeMarker other && this.Equals(other);
}
/// <inheritdoc/>

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
}
}
marker = default(JFifMarker);
marker = default;
return false;
}
@ -104,12 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is JFifMarker && this.Equals((JFifMarker)obj);
return obj is JFifMarker other && this.Equals(other);
}
/// <inheritdoc/>

11
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -427,10 +427,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (this.isExif)
{
ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag)
? ((Rational)horizonalTag.Value).ToDouble()
: 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{

6
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Represents a jpeg file marker
/// </summary>
internal struct PdfJsFileMarker
internal readonly struct PdfJsFileMarker
{
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsFileMarker"/> struct.
@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
/// <summary>
/// Gets or sets a value indicating whether the current marker is invalid
/// Gets a value indicating whether the current marker is invalid
/// </summary>
public bool Invalid { get; set; }
public bool Invalid { get; }
/// <summary>
/// Gets the position of the marker within a stream

11
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -380,10 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
if (this.isExif)
{
ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
? ((Rational)horizontalTag.Value).ToDouble()
: 0;
double verticalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{

9
src/ImageSharp/Formats/Png/PngChunk.cs

@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngChunk
{
public PngChunk(int length)
{
this.Length = length;
}
/// <summary>
/// Gets or sets the length.
/// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length
/// </summary>
public int Length { get; set; }
public int Length { get; }
/// <summary>
/// Gets or sets the chunk type as string with 4 chars.

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

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -216,19 +217,18 @@ namespace SixLabors.ImageSharp.Formats.Png
{
using (var deframeStream = new ZlibInflateStream(this.currentStream))
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
if (image == null)
@ -236,23 +236,23 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image);
}
deframeStream.AllocateNewBytes(currentChunk.Length);
deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length);
byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length);
byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -262,10 +262,10 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
if (chunk.Data != null)
{
currentChunk.Data.Dispose();
currentChunk.Data = null;
chunk.Data.Dispose();
chunk.Data = null;
}
}
}
@ -296,25 +296,24 @@ namespace SixLabors.ImageSharp.Formats.Png
this.currentStream.Skip(8);
try
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk);
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -324,9 +323,9 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
if (chunk.Data != null)
{
ArrayPool<byte>.Shared.Return(currentChunk.Data.Array);
ArrayPool<byte>.Shared.Return(chunk.Data.Array);
}
}
}
@ -337,7 +336,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.previousScanline?.Dispose();
}
if (this.header == null)
if (this.header.Width == 0 && this.header.Height == 0)
{
throw new ImageFormatException("PNG Image does not contain a header chunk");
}
@ -348,13 +347,12 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Converts a byte array to a new array where each value in the original array is represented by the specified number of bits.
/// </summary>
/// <param name="source">The bytes to convert from. Cannot be null.</param>
/// <param name="source">The bytes to convert from. Cannot be empty.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="Span{Byte}"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <returns>The resulting <see cref="ReadOnlySpan{Byte}"/> array.</returns>
/// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
private static Span<byte> ToArrayByBitsLength(Span<byte> source, int bytesPerScanline, int bits)
private static ReadOnlySpan<byte> ToArrayByBitsLength(ReadOnlySpan<byte> source, int bytesPerScanline, int bits)
{
Guard.MustBeGreaterThan(source.Length, 0, nameof(source));
Guard.MustBeGreaterThan(bits, 0, nameof(bits));
@ -414,14 +412,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="metadata">The metadata to read to.</param>
/// <param name="data">The data containing physical data.</param>
private void ReadPhysicalChunk(ImageMetaData metadata, byte[] data)
private void ReadPhysicalChunk(ImageMetaData metadata, ReadOnlySpan<byte> data)
{
data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4);
// 39.3700787 = inches in a meter.
metadata.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d;
metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d;
metadata.HorizontalResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)) / 39.3700787d;
metadata.VerticalResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)) / 39.3700787d;
}
/// <summary>
@ -671,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
this.ProcessInterlacedDefilteredScanline(this.scanline.Span, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);
this.SwapBuffers();
@ -699,20 +694,20 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(byte[] defilteredScanline, ImageFrame<TPixel> pixels)
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1, defilteredScanline.Length - 1);
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
Span<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = 0; x < this.header.Width; x++)
{
@ -796,10 +791,10 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
Span<Rgb24> rgb24Span = scanlineBuffer.NonPortableCast<byte, Rgb24>();
ReadOnlySpan<Rgb24> rgb24Span = scanlineBuffer.NonPortableCast<byte, Rgb24>();
for (int x = 0; x < this.header.Width; x++)
{
ref Rgb24 rgb24 = ref rgb24Span[x];
ref readonly Rgb24 rgb24 = ref rgb24Span[x];
var rgba32 = default(Rgba32);
rgba32.Rgb = rgb24;
rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255);
@ -840,7 +835,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="target">The target buffer</param>
/// <param name="length">The target length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void From16BitTo8Bit(Span<byte> source, Span<byte> target, int length)
private void From16BitTo8Bit(ReadOnlySpan<byte> source, Span<byte> target, int length)
{
for (int i = 0, j = 0; i < length; i++, j += 2)
{
@ -881,10 +876,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The type of pixel we are expanding to</typeparam>
/// <param name="defilteredScanline">The scanline</param>
/// <param name="row">Thecurrent output image row</param>
private void ProcessScanlineFromPalette<TPixel>(Span<byte> defilteredScanline, Span<TPixel> row)
private void ProcessScanlineFromPalette<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> row)
where TPixel : struct, IPixel<TPixel>
{
Span<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth);
byte[] pal = this.palette;
var color = default(TPixel);
@ -931,19 +926,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="rowSpan">The current image row.</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(byte[] defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
// Trim the first marker byte from the buffer
var scanlineBuffer = new Span<byte>(defilteredScanline, 1, defilteredScanline.Length - 1);
ReadOnlySpan<byte> scanlineBuffer = defilteredScanline.Slice(1, defilteredScanline.Length - 1);
switch (this.pngColorType)
{
case PngColorType.Grayscale:
int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1);
Span<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++)
{
@ -976,7 +971,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Palette:
Span<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
ReadOnlySpan<byte> newScanline = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth);
var rgba = default(Rgba32);
if (this.paletteAlpha != null && this.paletteAlpha.Length > 0)
@ -1159,22 +1154,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Reads a header chunk from the data.
/// </summary>
/// <param name="data">The <see cref="T:byte[]"/> containing data.</param>
private void ReadHeaderChunk(byte[] data)
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
private void ReadHeaderChunk(ReadOnlySpan<byte> data)
{
this.header = new PngHeader();
data.ReverseBytes(0, 4);
data.ReverseBytes(4, 4);
this.header.Width = BitConverter.ToInt32(data, 0);
this.header.Height = BitConverter.ToInt32(data, 4);
this.header.BitDepth = data[8];
this.header.ColorType = (PngColorType)data[9];
this.header.CompressionMethod = data[10];
this.header.FilterMethod = data[11];
this.header.InterlaceMethod = (PngInterlaceMode)data[12];
this.header = new PngHeader(
width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
bitDepth: data[8],
colorType: (PngColorType)data[9],
compressionMethod: data[10],
filterMethod: data[11],
interlaceMethod: (PngInterlaceMode)data[12]);
}
/// <summary>
@ -1214,36 +1204,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// The <see cref="PngChunk"/>.
/// </returns>
private PngChunk ReadChunk()
private bool TryReadChunk(out PngChunk chunk)
{
var chunk = new PngChunk();
this.ReadChunkLength(chunk);
int length = this.ReadChunkLength();
if (chunk.Length == -1)
if (length == -1)
{
chunk = default;
// IEND
return null;
return false;
}
chunk = new PngChunk(length);
if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position)
{
// Not a valid chunk so we skip back all but one of the four bytes we have just read.
// That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3;
return chunk;
return true;
}
this.ReadChunkType(chunk);
if (chunk.Type == PngChunkTypes.Data)
{
return chunk;
return true;
}
this.ReadChunkData(chunk);
this.ReadChunkCrc(chunk);
return chunk;
return true;
}
/// <summary>
@ -1256,18 +1250,17 @@ namespace SixLabors.ImageSharp.Formats.Png
private void ReadChunkCrc(PngChunk chunk)
{
int numBytes = this.currentStream.Read(this.crcBuffer, 0, 4);
if (numBytes >= 1 && numBytes <= 3)
{
throw new ImageFormatException("Image stream is not valid!");
}
this.crcBuffer.ReverseBytes();
chunk.Crc = BitConverter.ToUInt32(this.crcBuffer, 0);
chunk.Crc = BinaryPrimitives.ReadUInt32BigEndian(this.crcBuffer);
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
this.crc.Update(chunk.Data.Array, 0, chunk.Length);
this.crc.Update(new ReadOnlySpan<byte>(chunk.Data.Array, 0, chunk.Length));
if (this.crc.Value != chunk.Crc && IsCriticalChunk(chunk))
{
@ -1321,22 +1314,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Calculates the length of the given chunk.
/// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
private void ReadChunkLength(PngChunk chunk)
private int ReadChunkLength()
{
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes < 4)
{
chunk.Length = -1;
return;
return -1;
}
this.chunkLengthBuffer.ReverseBytes();
chunk.Length = BitConverter.ToInt32(this.chunkLengthBuffer, 0);
return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
}
/// <summary>

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

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
@ -35,6 +37,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly byte[] chunkDataBuffer = new byte[16];
/// <summary>
/// Reusable buffer for writing int data.
/// </summary>
private readonly byte[] intBuffer = new byte[4];
/// <summary>
/// Reusable crc for validating chunks.
/// </summary>
@ -205,16 +212,14 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerPixel = this.CalculateBytesPerPixel();
var header = new PngHeader
{
Width = image.Width,
Height = image.Height,
ColorType = this.pngColorType,
BitDepth = this.bitDepth,
FilterMethod = 0, // None
CompressionMethod = 0,
InterlaceMethod = 0
};
var header = new PngHeader(
width: image.Width,
height: image.Height,
colorType: this.pngColorType,
bitDepth: this.bitDepth,
filterMethod: 0, // None
compressionMethod: 0,
interlaceMethod: 0);
this.WriteHeaderChunk(stream, header);
@ -243,52 +248,12 @@ namespace SixLabors.ImageSharp.Formats.Png
this.paeth?.Dispose();
}
/// <summary>
/// Writes an integer to the byte array.
/// </summary>
/// <param name="data">The <see cref="T:byte[]"/> containing image data.</param>
/// <param name="offset">The amount to offset by.</param>
/// <param name="value">The value to write.</param>
private static void WriteInteger(byte[] data, int offset, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
buffer.ReverseBytes();
Buffer.BlockCopy(buffer, 0, data, offset, 4);
}
/// <summary>
/// Writes an integer to the stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private static void WriteInteger(Stream stream, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
buffer.ReverseBytes();
stream.Write(buffer, 0, 4);
}
/// <summary>
/// Writes an unsigned integer to the stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private static void WriteInteger(Stream stream, uint value)
{
byte[] buffer = BitConverter.GetBytes(value);
buffer.ReverseBytes();
stream.Write(buffer, 0, 4);
}
/// <summary>
/// Collects a row of grayscale pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The image row span.</param>
private void CollectGrayscaleBytes<TPixel>(Span<TPixel> rowSpan)
private void CollectGrayscaleBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
byte[] rawScanlineArray = this.rawScanline.Array;
@ -323,7 +288,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The row span.</param>
private void CollecTPixelBytes<TPixel>(Span<TPixel> rowSpan)
private void CollecTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
if (this.bytesPerPixel == 4)
@ -344,7 +309,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="rowSpan">The row span.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(Span<TPixel> rowSpan, int row)
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.pngColorType)
@ -448,10 +413,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
private void WriteHeaderChunk(Stream stream, PngHeader header)
private void WriteHeaderChunk(Stream stream, in PngHeader header)
{
WriteInteger(this.chunkDataBuffer, 0, header.Width);
WriteInteger(this.chunkDataBuffer, 4, header.Height);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), header.Width);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), header.Height);
this.chunkDataBuffer[8] = header.BitDepth;
this.chunkDataBuffer[9] = (byte)header.ColorType;
@ -469,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
/// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, PngHeader header, QuantizedFrame<TPixel> quantized)
private void WritePaletteChunk<TPixel>(Stream stream, in PngHeader header, QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
@ -535,8 +500,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D);
int dpmY = (int)Math.Round(image.MetaData.VerticalResolution * 39.3700787D);
WriteInteger(this.chunkDataBuffer, 0, dpmX);
WriteInteger(this.chunkDataBuffer, 4, dpmY);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), dpmX);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), dpmY);
this.chunkDataBuffer[8] = 1;
@ -552,14 +517,10 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (this.writeGamma)
{
int gammaValue = (int)(this.gamma * 100000F);
// 4-byte unsigned integer of gamma * 100,000.
uint gammaValue = (uint)(this.gamma * 100_000F);
byte[] size = BitConverter.GetBytes(gammaValue);
this.chunkDataBuffer[0] = size[3];
this.chunkDataBuffer[1] = size[2];
this.chunkDataBuffer[2] = size[1];
this.chunkDataBuffer[3] = size[0];
BinaryPrimitives.WriteUInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), gammaValue);
this.WriteChunk(stream, PngChunkTypes.Gamma, this.chunkDataBuffer, 0, 4);
}
@ -591,15 +552,14 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] buffer;
int bufferLength;
MemoryStream memoryStream = null;
try
using (var memoryStream = new MemoryStream())
{
memoryStream = new MemoryStream();
using (var deflateStream = new ZlibDeflateStream(memoryStream, this.compressionLevel))
{
for (int y = 0; y < this.height; y++)
{
IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y);
IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y).AsReadOnlySpan(), y);
deflateStream.Write(r.Array, 0, resultLength);
IManagedByteBuffer temp = this.rawScanline;
@ -611,10 +571,6 @@ namespace SixLabors.ImageSharp.Formats.Png
buffer = memoryStream.ToArray();
bufferLength = buffer.Length;
}
finally
{
memoryStream?.Dispose();
}
// Store the chunks in repeated 64k blocks.
// This reduces the memory load for decoding the image for many decoders.
@ -668,7 +624,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="length">The of the data to write.</param>
private void WriteChunk(Stream stream, string type, byte[] data, int offset, int length)
{
WriteInteger(stream, length);
BinaryPrimitives.WriteInt32BigEndian(this.intBuffer, length);
stream.Write(this.intBuffer, 0, 4); // write the length
this.chunkTypeBuffer[0] = (byte)type[0];
this.chunkTypeBuffer[1] = (byte)type[1];
@ -677,20 +635,20 @@ namespace SixLabors.ImageSharp.Formats.Png
stream.Write(this.chunkTypeBuffer, 0, 4);
if (data != null)
{
stream.Write(data, offset, length);
}
this.crc.Reset();
this.crc.Update(this.chunkTypeBuffer);
if (data != null && length > 0)
{
this.crc.Update(data, offset, length);
stream.Write(data, offset, length);
this.crc.Update(new ReadOnlySpan<byte>(data, offset, length));
}
WriteInteger(stream, (uint)this.crc.Value);
BinaryPrimitives.WriteUInt32BigEndian(this.intBuffer, (uint)this.crc.Value);
stream.Write(this.intBuffer, 0, 4); // write the crc
}
}
}

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

@ -6,55 +6,73 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Represents the png header chunk.
/// </summary>
internal sealed class PngHeader
internal readonly struct PngHeader
{
public PngHeader(
int width,
int height,
byte bitDepth,
PngColorType colorType,
byte compressionMethod,
byte filterMethod,
PngInterlaceMode interlaceMethod)
{
this.Width = width;
this.Height = height;
this.BitDepth = bitDepth;
this.ColorType = colorType;
this.CompressionMethod = compressionMethod;
this.FilterMethod = filterMethod;
this.InterlaceMethod = interlaceMethod;
}
/// <summary>
/// Gets or sets the dimension in x-direction of the image in pixels.
/// Gets the dimension in x-direction of the image in pixels.
/// </summary>
public int Width { get; set; }
public int Width { get; }
/// <summary>
/// Gets or sets the dimension in y-direction of the image in pixels.
/// Gets the dimension in y-direction of the image in pixels.
/// </summary>
public int Height { get; set; }
public int Height { get; }
/// <summary>
/// Gets or sets the bit depth.
/// Gets the bit depth.
/// Bit depth is a single-byte integer giving the number of bits per sample
/// or per palette index (not per pixel). Valid values are 1, 2, 4, 8, and 16,
/// although not all values are allowed for all color types.
/// </summary>
public byte BitDepth { get; set; }
public byte BitDepth { get; }
/// <summary>
/// Gets or sets the color type.
/// Gets the color type.
/// Color type is a integer that describes the interpretation of the
/// image data. Color type codes represent sums of the following values:
/// 1 (palette used), 2 (color used), and 4 (alpha channel used).
/// </summary>
public PngColorType ColorType { get; set; }
public PngColorType ColorType { get; }
/// <summary>
/// Gets or sets the compression method.
/// Gets the compression method.
/// Indicates the method used to compress the image data. At present,
/// only compression method 0 (deflate/inflate compression with a sliding
/// window of at most 32768 bytes) is defined.
/// </summary>
public byte CompressionMethod { get; set; }
public byte CompressionMethod { get; }
/// <summary>
/// Gets or sets the preprocessing method.
/// Gets the preprocessing method.
/// Indicates the preprocessing method applied to the image
/// data before compression. At present, only filter method 0
/// (adaptive filtering with five basic filter types) is defined.
/// </summary>
public byte FilterMethod { get; set; }
public byte FilterMethod { get; }
/// <summary>
/// Gets or sets the transmission order.
/// Gets the transmission order.
/// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </summary>
public PngInterlaceMode InterlaceMethod { get; set; }
public PngInterlaceMode InterlaceMethod { get; }
}
}

25
src/ImageSharp/Formats/Png/Zlib/Adler32.cs

@ -113,30 +113,15 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(byte[] buffer)
public void Update(ReadOnlySpan<byte> data)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
this.Update(buffer, 0, buffer.Length);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(byte[] buffer, int offset, int count)
{
DebugGuard.NotNull(buffer, nameof(buffer));
DebugGuard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset));
DebugGuard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count));
DebugGuard.MustBeLessThan(offset, buffer.Length, nameof(offset));
DebugGuard.MustBeLessThanOrEqualTo(offset + count, buffer.Length, nameof(count));
// (By Per Bothner)
uint s1 = this.checksum & 0xFFFF;
uint s2 = this.checksum >> 16;
int count = data.Length;
int offset = 0;
while (count > 0)
{
// We can defer the modulo operation:
@ -151,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
count -= n;
while (--n >= 0)
{
s1 = s1 + (uint)(buffer[offset++] & 0xff);
s1 = s1 + (uint)(data[offset++] & 0xff);
s2 = s2 + s1;
}

23
src/ImageSharp/Formats/Png/Zlib/Crc32.cs

@ -137,30 +137,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(byte[] buffer)
public void Update(ReadOnlySpan<byte> data)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
this.Update(buffer, 0, buffer.Length);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(byte[] buffer, int offset, int count)
{
DebugGuard.NotNull(buffer, nameof(buffer));
DebugGuard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count));
DebugGuard.MustBeGreaterThanOrEqualTo(offset, 0, nameof(offset));
DebugGuard.MustBeLessThanOrEqualTo(offset + count, buffer.Length, nameof(count));
this.crc ^= CrcSeed;
while (--count >= 0)
for (int i = 0; i < data.Length; i++)
{
this.crc = CrcTable[(this.crc ^ buffer[offset++]) & 0xFF] ^ (this.crc >> 8);
this.crc = CrcTable[(this.crc ^ data[i]) & 0xFF] ^ (this.crc >> 8);
}
this.crc ^= CrcSeed;

22
src/ImageSharp/Formats/Png/Zlib/IChecksum.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Png.Zlib
{
/// <summary>
@ -34,25 +36,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
void Update(int value);
/// <summary>
/// Updates the data checksum with the bytes taken from the array.
/// Updates the data checksum with the bytes taken from the span.
/// </summary>
/// <param name="buffer">
/// <param name="data">
/// buffer an array of bytes
/// </param>
void Update(byte[] buffer);
/// <summary>
/// Adds the byte array to the data checksum.
/// </summary>
/// <param name = "buffer">
/// The buffer which contains the data
/// </param>
/// <param name = "offset">
/// The offset in the buffer where the data starts
/// </param>
/// <param name = "count">
/// the number of data bytes to add.
/// </param>
void Update(byte[] buffer, int offset, int count);
void Update(ReadOnlySpan<byte> data);
}
}

2
src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
public override void Write(byte[] buffer, int offset, int count)
{
this.deflateStream.Write(buffer, offset, count);
this.adler32.Update(buffer, offset, count);
this.adler32.Update(new ReadOnlySpan<byte>(buffer, offset, count));
}
/// <inheritdoc/>

1
src/ImageSharp/IImageFrameCollection.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;

84
src/ImageSharp/IO/BigEndianBitConverter.cs

@ -1,84 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Implementation of EndianBitConverter which converts to/from big-endian byte arrays.
/// </summary>
internal sealed class BigEndianBitConverter : EndianBitConverter
{
/// <inheritdoc/>
public override Endianness Endianness
{
get { return Endianness.BigEndian; }
}
/// <inheritdoc/>
public override bool IsLittleEndian
{
get { return false; }
}
/// <inheritdoc/>
public override void CopyBytes(short value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 2);
buffer[index] = (byte)(value >> 8);
buffer[index + 1] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(int value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 4);
buffer[index] = (byte)(value >> 24);
buffer[index + 1] = (byte)(value >> 16);
buffer[index + 2] = (byte)(value >> 8);
buffer[index + 3] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(long value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 8);
buffer[index] = (byte)(value >> 56);
buffer[index + 1] = (byte)(value >> 48);
buffer[index + 2] = (byte)(value >> 40);
buffer[index + 3] = (byte)(value >> 32);
buffer[index + 4] = (byte)(value >> 24);
buffer[index + 5] = (byte)(value >> 16);
buffer[index + 6] = (byte)(value >> 8);
buffer[index + 7] = (byte)value;
}
/// <inheritdoc/>
public override short ToInt16(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 2);
return (short)((value[startIndex] << 8) | value[startIndex + 1]);
}
/// <inheritdoc/>
public override int ToInt32(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 4);
return (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
}
/// <inheritdoc/>
public override long ToInt64(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 8);
long p1 = (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
long p2 = (value[startIndex + 4] << 24) | (value[startIndex + 5] << 16) | (value[startIndex + 6] << 8) | value[startIndex + 7];
return (p2 & 0xFFFFFFFF) | (p1 << 32);
}
}
}

277
src/ImageSharp/IO/EndianBinaryReader.cs

@ -2,57 +2,32 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Text;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BinaryReader"/>, but with either endianness, depending on the <see cref="EndianBitConverter"/> it is constructed with.
/// Equivalent of <see cref="BinaryReader"/>, but with either endianness.
/// No data is buffered in the reader; the client may seek within the stream at will.
/// </summary>
internal class EndianBinaryReader : IDisposable
{
/// <summary>
/// Decoder to use for string conversions.
/// </summary>
private readonly Decoder decoder;
/// <summary>
/// Buffer used for temporary storage before conversion into primitives
/// </summary>
private readonly byte[] storageBuffer = new byte[16];
/// <summary>
/// Buffer used for temporary storage when reading a single character
/// </summary>
private readonly char[] charBuffer = new char[1];
/// <summary>
/// Minimum number of bytes used to encode a character
/// </summary>
private readonly int minBytesPerChar;
/// <summary>
/// Whether or not this reader has been disposed yet.
/// </summary>
private bool disposed;
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class.
/// Equivalent of <see cref="System.IO.BinaryWriter"/>, but with either endianness, depending on
/// the EndianBitConverter it is constructed with.
/// The endianness used to read data
/// </summary>
/// <param name="endianness">
/// Endianness to use when reading data
/// </param>
/// <param name="stream">
/// Stream to read data from
/// </param>
public EndianBinaryReader(Endianness endianness, Stream stream)
: this(endianness, stream, Encoding.UTF8)
{
}
private readonly Endianness endianness;
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class.
@ -61,40 +36,20 @@ namespace SixLabors.ImageSharp.IO
/// </summary>
/// <param name="endianness">Endianness to use when reading data</param>
/// <param name="stream">Stream to read data from</param>
/// <param name="encoding">Encoding to use when reading character data</param>
public EndianBinaryReader(Endianness endianness, Stream stream, Encoding encoding)
public EndianBinaryReader(Endianness endianness, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoding, nameof(encoding));
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream isn't readable");
this.BaseStream = stream;
this.BitConverter = EndianBitConverter.GetConverter(endianness);
this.Encoding = encoding;
this.decoder = encoding.GetDecoder();
this.minBytesPerChar = 1;
if (encoding is UnicodeEncoding)
{
this.minBytesPerChar = 2;
}
this.endianness = endianness;
}
/// <summary>
/// Gets the encoding used to read strings
/// </summary>
public Encoding Encoding { get; }
/// <summary>
/// Gets the underlying stream of the EndianBinaryReader.
/// </summary>
public Stream BaseStream { get; }
/// <summary>
/// Gets the bit converter used to read values from the stream.
/// </summary>
internal EndianBitConverter BitConverter { get; }
/// <summary>
/// Closes the reader, including the underlying stream.
/// </summary>
@ -141,7 +96,8 @@ namespace SixLabors.ImageSharp.IO
public bool ReadBoolean()
{
this.ReadInternal(this.storageBuffer, 1);
return this.BitConverter.ToBoolean(this.storageBuffer, 0);
return this.storageBuffer[0] != 0;
}
/// <summary>
@ -152,7 +108,10 @@ namespace SixLabors.ImageSharp.IO
public short ReadInt16()
{
this.ReadInternal(this.storageBuffer, 2);
return this.BitConverter.ToInt16(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadInt16BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadInt16LittleEndian(this.storageBuffer);
}
/// <summary>
@ -163,7 +122,10 @@ namespace SixLabors.ImageSharp.IO
public int ReadInt32()
{
this.ReadInternal(this.storageBuffer, 4);
return this.BitConverter.ToInt32(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadInt32BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadInt32LittleEndian(this.storageBuffer);
}
/// <summary>
@ -174,7 +136,10 @@ namespace SixLabors.ImageSharp.IO
public long ReadInt64()
{
this.ReadInternal(this.storageBuffer, 8);
return this.BitConverter.ToInt64(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadInt64BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadInt64LittleEndian(this.storageBuffer);
}
/// <summary>
@ -185,7 +150,10 @@ namespace SixLabors.ImageSharp.IO
public ushort ReadUInt16()
{
this.ReadInternal(this.storageBuffer, 2);
return this.BitConverter.ToUInt16(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadUInt16BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadUInt16LittleEndian(this.storageBuffer);
}
/// <summary>
@ -196,7 +164,10 @@ namespace SixLabors.ImageSharp.IO
public uint ReadUInt32()
{
this.ReadInternal(this.storageBuffer, 4);
return this.BitConverter.ToUInt32(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadUInt32BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadUInt32LittleEndian(this.storageBuffer);
}
/// <summary>
@ -207,7 +178,10 @@ namespace SixLabors.ImageSharp.IO
public ulong ReadUInt64()
{
this.ReadInternal(this.storageBuffer, 8);
return this.BitConverter.ToUInt64(this.storageBuffer, 0);
return (this.endianness == Endianness.BigEndian)
? BinaryPrimitives.ReadUInt64BigEndian(this.storageBuffer)
: BinaryPrimitives.ReadUInt64LittleEndian(this.storageBuffer);
}
/// <summary>
@ -215,10 +189,11 @@ namespace SixLabors.ImageSharp.IO
/// for this reader. 4 bytes are read.
/// </summary>
/// <returns>The floating point value read</returns>
public float ReadSingle()
public unsafe float ReadSingle()
{
this.ReadInternal(this.storageBuffer, 4);
return this.BitConverter.ToSingle(this.storageBuffer, 0);
int intValue = this.ReadInt32();
return *((float*)&intValue);
}
/// <summary>
@ -226,107 +201,11 @@ namespace SixLabors.ImageSharp.IO
/// for this reader. 8 bytes are read.
/// </summary>
/// <returns>The floating point value read</returns>
public double ReadDouble()
public unsafe double ReadDouble()
{
this.ReadInternal(this.storageBuffer, 8);
return this.BitConverter.ToDouble(this.storageBuffer, 0);
}
long value = this.ReadInt64();
/// <summary>
/// Reads a decimal value from the stream, using the bit converter
/// for this reader. 16 bytes are read.
/// </summary>
/// <returns>The decimal value read</returns>
public decimal ReadDecimal()
{
this.ReadInternal(this.storageBuffer, 16);
return this.BitConverter.ToDecimal(this.storageBuffer, 0);
}
/// <summary>
/// Reads a single character from the stream, using the character encoding for
/// this reader. If no characters have been fully read by the time the stream ends,
/// -1 is returned.
/// </summary>
/// <returns>The character read, or -1 for end of stream.</returns>
public int Read()
{
int charsRead = this.Read(this.charBuffer, 0, 1);
if (charsRead == 0)
{
return -1;
}
else
{
return this.charBuffer[0];
}
}
/// <summary>
/// Reads the specified number of characters into the given buffer, starting at
/// the given index.
/// </summary>
/// <param name="data">The buffer to copy data into</param>
/// <param name="index">The first index to copy data into</param>
/// <param name="count">The number of characters to read</param>
/// <returns>The number of characters actually read. This will only be less than
/// the requested number of characters if the end of the stream is reached.
/// </returns>
public int Read(char[] data, int index, int count)
{
this.CheckDisposed();
Guard.NotNull(this.storageBuffer, nameof(this.storageBuffer));
Guard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index));
Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count));
Guard.IsFalse(count + index > data.Length, nameof(data.Length), "Not enough space in buffer for specified number of characters starting at specified index.");
int read = 0;
bool firstTime = true;
// Use the normal buffer if we're only reading a small amount, otherwise
// use at most 4K at a time.
byte[] byteBuffer = this.storageBuffer;
if (byteBuffer.Length < count * this.minBytesPerChar)
{
byteBuffer = new byte[4096];
}
while (read < count)
{
int amountToRead;
// First time through we know we haven't previously read any data
if (firstTime)
{
amountToRead = count * this.minBytesPerChar;
firstTime = false;
}
else
{
// After that we can only assume we need to fully read 'chars left -1' characters
// and a single byte of the character we may be in the middle of
amountToRead = ((count - read - 1) * this.minBytesPerChar) + 1;
}
if (amountToRead > byteBuffer.Length)
{
amountToRead = byteBuffer.Length;
}
int bytesRead = this.TryReadInternal(byteBuffer, amountToRead);
if (bytesRead == 0)
{
return read;
}
int decoded = this.decoder.GetChars(byteBuffer, 0, bytesRead, data, index);
read += decoded;
index += decoded;
}
return read;
return *((double*)&value);
}
/// <summary>
@ -411,84 +290,6 @@ namespace SixLabors.ImageSharp.IO
return ret;
}
/// <summary>
/// Reads a 7-bit encoded integer from the stream. This is stored with the least significant
/// information first, with 7 bits of information per byte of value, and the top
/// bit as a continuation flag. This method is not affected by the endianness
/// of the bit converter.
/// </summary>
/// <returns>The 7-bit encoded integer read from the stream.</returns>
public int Read7BitEncodedInt()
{
this.CheckDisposed();
int ret = 0;
for (int shift = 0; shift < 35; shift += 7)
{
int b = this.BaseStream.ReadByte();
if (b == -1)
{
throw new EndOfStreamException();
}
ret = ret | ((b & 0x7f) << shift);
if ((b & 0x80) == 0)
{
return ret;
}
}
// Still haven't seen a byte with the high bit unset? Dodgy data.
throw new IOException("Invalid 7-bit encoded integer in stream.");
}
/// <summary>
/// Reads a 7-bit encoded integer from the stream. This is stored with the most significant
/// information first, with 7 bits of information per byte of value, and the top
/// bit as a continuation flag. This method is not affected by the endianness
/// of the bit converter.
/// </summary>
/// <returns>The 7-bit encoded integer read from the stream.</returns>
public int ReadBigEndian7BitEncodedInt()
{
this.CheckDisposed();
int ret = 0;
for (int i = 0; i < 5; i++)
{
int b = this.BaseStream.ReadByte();
if (b == -1)
{
throw new EndOfStreamException();
}
ret = (ret << 7) | (b & 0x7f);
if ((b & 0x80) == 0)
{
return ret;
}
}
// Still haven't seen a byte with the high bit unset? Dodgy data.
throw new IOException("Invalid 7-bit encoded integer in stream.");
}
/// <summary>
/// Reads a length-prefixed string from the stream, using the encoding for this reader.
/// A 7-bit encoded integer is first read, which specifies the number of bytes
/// to read from the stream. These bytes are then converted into a string with
/// the encoding for this reader.
/// </summary>
/// <returns>The string read from the stream.</returns>
public string ReadString()
{
int bytesToRead = this.Read7BitEncodedInt();
byte[] data = new byte[bytesToRead];
this.ReadInternal(data, bytesToRead);
return this.Encoding.GetString(data, 0, data.Length);
}
/// <summary>
/// Disposes of the underlying stream.
/// </summary>

188
src/ImageSharp/IO/EndianBinaryWriter.cs

@ -2,14 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using System.Text;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BinaryWriter"/>, but with either endianness, depending on
/// the <see cref="EndianBitConverter"/> it is constructed with.
/// Equivalent of <see cref="BinaryWriter"/>, but with either endianness
/// </summary>
internal class EndianBinaryWriter : IDisposable
{
@ -19,61 +18,35 @@ namespace SixLabors.ImageSharp.IO
private readonly byte[] buffer = new byte[16];
/// <summary>
/// Buffer used for Write(char)
/// The endianness used to write the data
/// </summary>
private readonly char[] charBuffer = new char[1];
private readonly Endianness endianness;
/// <summary>
/// Whether or not this writer has been disposed yet.
/// </summary>
private bool disposed;
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryWriter"/> class
/// with the given bit converter, writing to the given stream, using UTF-8 encoding.
/// </summary>
/// <param name="endianness">Endianness to use when writing data</param>
/// <param name="stream">Stream to write data to</param>
public EndianBinaryWriter(Endianness endianness, Stream stream)
: this(endianness, stream, Encoding.UTF8)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryWriter"/> class
/// with the given bit converter, writing to the given stream, using the given encoding.
/// </summary>
/// <param name="endianness">Endianness to use when writing data</param>
/// <param name="stream">Stream to write data to</param>
/// <param name="encoding">
/// Encoding to use when writing character data
/// </param>
public EndianBinaryWriter(Endianness endianness, Stream stream, Encoding encoding)
public EndianBinaryWriter(Endianness endianness, Stream stream)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(stream, nameof(encoding));
Guard.IsTrue(stream.CanWrite, nameof(stream), "Stream isn't writable");
this.BaseStream = stream;
this.BitConverter = EndianBitConverter.GetConverter(endianness);
this.Encoding = encoding;
this.endianness = endianness;
}
/// <summary>
/// Gets the encoding used to write strings
/// </summary>
public Encoding Encoding { get; }
/// <summary>
/// Gets the underlying stream of the EndianBinaryWriter.
/// </summary>
public Stream BaseStream { get; }
/// <summary>
/// Gets the bit converter used to write values to the stream
/// </summary>
internal EndianBitConverter BitConverter { get; }
/// <summary>
/// Closes the writer, including the underlying stream.
/// </summary>
@ -108,7 +81,8 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(bool value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
this.buffer[0] = value ? (byte)1 : (byte)0;
this.WriteInternal(this.buffer, 1);
}
@ -119,7 +93,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(short value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteInt16BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteInt16LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 2);
}
@ -130,7 +112,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(int value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteInt32BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 4);
}
@ -141,7 +131,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(long value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteInt64BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteInt64LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 8);
}
@ -152,7 +150,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(ushort value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 2);
}
@ -163,7 +169,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(uint value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 4);
}
@ -174,7 +188,15 @@ namespace SixLabors.ImageSharp.IO
/// <param name="value">The value to write</param>
public void Write(ulong value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
if (this.endianness == Endianness.BigEndian)
{
BinaryPrimitives.WriteUInt64BigEndian(this.buffer, value);
}
else
{
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, value);
}
this.WriteInternal(this.buffer, 8);
}
@ -183,10 +205,9 @@ namespace SixLabors.ImageSharp.IO
/// for this writer. 4 bytes are written.
/// </summary>
/// <param name="value">The value to write</param>
public void Write(float value)
public unsafe void Write(float value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
this.WriteInternal(this.buffer, 4);
this.Write(*((int*)&value));
}
/// <summary>
@ -194,21 +215,9 @@ namespace SixLabors.ImageSharp.IO
/// for this writer. 8 bytes are written.
/// </summary>
/// <param name="value">The value to write</param>
public void Write(double value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
this.WriteInternal(this.buffer, 8);
}
/// <summary>
/// Writes a decimal value to the stream, using the bit converter for this writer.
/// 16 bytes are written.
/// </summary>
/// <param name="value">The value to write</param>
public void Write(decimal value)
public unsafe void Write(double value)
{
this.BitConverter.CopyBytes(value, this.buffer, 0);
this.WriteInternal(this.buffer, 16);
this.Write(*((long*)&value));
}
/// <summary>
@ -255,71 +264,6 @@ namespace SixLabors.ImageSharp.IO
this.BaseStream.Write(value, offset, count);
}
/// <summary>
/// Writes a single character to the stream, using the encoding for this writer.
/// </summary>
/// <param name="value">The value to write</param>
public void Write(char value)
{
this.charBuffer[0] = value;
this.Write(this.charBuffer);
}
/// <summary>
/// Writes an array of characters to the stream, using the encoding for this writer.
/// </summary>
/// <param name="value">An array containing the characters to write</param>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(char[] value)
{
Guard.NotNull(value, nameof(value));
this.CheckDisposed();
byte[] data = this.Encoding.GetBytes(value, 0, value.Length);
this.WriteInternal(data, data.Length);
}
/// <summary>
/// Writes a length-prefixed string to the stream, using the encoding for this writer.
/// </summary>
/// <param name="value">The value to write. Must not be null.</param>
/// <exception cref="ArgumentNullException">value is null</exception>
public void Write(string value)
{
Guard.NotNull(value, nameof(value));
this.CheckDisposed();
byte[] data = this.Encoding.GetBytes(value);
this.Write7BitEncodedInt(data.Length);
this.WriteInternal(data, data.Length);
}
/// <summary>
/// Writes a 7-bit encoded integer from the stream. This is stored with the least significant
/// information first, with 7 bits of information per byte of value, and the top
/// bit as a continuation flag.
/// </summary>
/// <param name="value">The 7-bit encoded integer to write to the stream</param>
public void Write7BitEncodedInt(int value)
{
this.CheckDisposed();
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value), "Value must be greater than or equal to 0.");
}
int index = 0;
while (value >= 128)
{
this.buffer[index++] = (byte)((value & 0x7f) | 0x80);
value = value >> 7;
index++;
}
this.buffer[index++] = (byte)value;
this.BaseStream.Write(this.buffer, 0, index);
}
/// <summary>
/// Disposes of the underlying stream.
/// </summary>

61
src/ImageSharp/IO/EndianBitConverter.Conversion.cs

@ -1,61 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Converts the specified double-precision floating point number to a
/// 64-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 64-bit signed integer whose value is equivalent to value.</returns>
public unsafe long DoubleToInt64Bits(double value)
{
return *((long*)&value);
}
/// <summary>
/// Converts the specified 64-bit signed integer to a double-precision
/// floating point number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A double-precision floating point number whose value is equivalent to value.</returns>
public unsafe double Int64BitsToDouble(long value)
{
return *((double*)&value);
}
/// <summary>
/// Converts the specified single-precision floating point number to a
/// 32-bit signed integer. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A 32-bit signed integer whose value is equivalent to value.</returns>
public unsafe int SingleToInt32Bits(float value)
{
return *((int*)&value);
}
/// <summary>
/// Converts the specified 32-bit signed integer to a single-precision floating point
/// number. Note: the endianness of this converter does not
/// affect the returned value.
/// </summary>
/// <param name="value">The number to convert. </param>
/// <returns>A single-precision floating point number whose value is equivalent to value.</returns>
public unsafe float Int32BitsToSingle(int value)
{
return *((float*)&value);
}
}
}

143
src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs

@ -1,143 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Copies the specified 16-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(short value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 32-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(int value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 64-bit signed integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public abstract void CopyBytes(long value, byte[] buffer, int index);
/// <summary>
/// Copies the specified 16-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ushort value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((short)value), buffer, index);
}
/// <summary>
/// Copies the specified 32-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(uint value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((int)value), buffer, index);
}
/// <summary>
/// Copies the specified 64-bit unsigned integer value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(ulong value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((long)value), buffer, index);
}
/// <summary>
/// Copies the specified Boolean value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(bool value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 1);
buffer[index] = value ? (byte)1 : (byte)0;
}
/// <summary>
/// Copies the specified Unicode character value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public void CopyBytes(char value, byte[] buffer, int index)
{
this.CopyBytes(unchecked((short)value), buffer, index);
}
/// <summary>
/// Copies the specified double-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(double value, byte[] buffer, int index)
{
this.CopyBytes(*((long*)&value), buffer, index);
}
/// <summary>
/// Copies the specified single-precision floating point value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(float value, byte[] buffer, int index)
{
this.CopyBytes(*((int*)&value), buffer, index);
}
/// <summary>
/// Copies the specified decimal value into the specified byte array,
/// beginning at the specified index.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <param name="buffer">The byte array to copy the bytes into</param>
/// <param name="index">The first index into the array to copy the bytes into</param>
public unsafe void CopyBytes(decimal value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 16);
int* pvalue = (int*)&value;
this.CopyBytes(pvalue[0], buffer, index);
this.CopyBytes(pvalue[1], buffer, index + 4);
this.CopyBytes(pvalue[2], buffer, index + 8);
this.CopyBytes(pvalue[3], buffer, index + 12);
}
}
}

137
src/ImageSharp/IO/EndianBitConverter.GetBytes.cs

@ -1,137 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Returns the specified 16-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(short value)
{
byte[] result = new byte[2];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 32-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(int value)
{
byte[] result = new byte[4];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 64-bit signed integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(long value)
{
byte[] result = new byte[8];
this.CopyBytes(value, result, 0);
return result;
}
/// <summary>
/// Returns the specified 16-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
public byte[] GetBytes(ushort value)
{
return this.GetBytes(unchecked((short)value));
}
/// <summary>
/// Returns the specified 32-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public byte[] GetBytes(uint value)
{
return this.GetBytes(unchecked((int)value));
}
/// <summary>
/// Returns the specified 64-bit unsigned integer value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public byte[] GetBytes(ulong value)
{
return this.GetBytes(unchecked((long)value));
}
/// <summary>
/// Returns the specified Boolean value as an array of bytes.
/// </summary>
/// <param name="value">A Boolean value.</param>
/// <returns>An array of bytes with length 1.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(bool value)
{
return new byte[1] { value ? (byte)1 : (byte)0 };
}
/// <summary>
/// Returns the specified Unicode character value as an array of bytes.
/// </summary>
/// <param name="value">A character to convert.</param>
/// <returns>An array of bytes with length 2.</returns>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// </returns>
public byte[] GetBytes(char value)
{
return this.GetBytes((short)value);
}
/// <summary>
/// Returns the specified double-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 8.</returns>
public unsafe byte[] GetBytes(double value)
{
return this.GetBytes(*((long*)&value));
}
/// <summary>
/// Returns the specified single-precision floating point value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 4.</returns>
public unsafe byte[] GetBytes(float value)
{
return this.GetBytes(*((int*)&value));
}
/// <summary>
/// Returns the specified decimal value as an array of bytes.
/// </summary>
/// <param name="value">The number to convert.</param>
/// <returns>An array of bytes with length 16.</returns>
public byte[] GetBytes(decimal value)
{
byte[] result = new byte[16];
this.CopyBytes(value, result, 0);
return result;
}
}
}

139
src/ImageSharp/IO/EndianBitConverter.ToType.cs

@ -1,139 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit signed integer formed by two bytes beginning at startIndex.</returns>
public abstract short ToInt16(byte[] value, int startIndex);
/// <summary>
/// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit signed integer formed by four bytes beginning at startIndex.</returns>
public abstract int ToInt32(byte[] value, int startIndex);
/// <summary>
/// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit signed integer formed by eight bytes beginning at startIndex.</returns>
public abstract long ToInt64(byte[] value, int startIndex);
/// <summary>
/// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 16-bit unsigned integer formed by two bytes beginning at startIndex.</returns>
public ushort ToUInt16(byte[] value, int startIndex)
{
return unchecked((ushort)this.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 32-bit unsigned integer formed by four bytes beginning at startIndex.</returns>
public uint ToUInt32(byte[] value, int startIndex)
{
return unchecked((uint)this.ToInt32(value, startIndex));
}
/// <summary>
/// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A 64-bit unsigned integer formed by eight bytes beginning at startIndex.</returns>
public ulong ToUInt64(byte[] value, int startIndex)
{
return unchecked((ulong)this.ToInt64(value, startIndex));
}
/// <summary>
/// Returns a Boolean value converted from one byte at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>true if the byte at startIndex in value is nonzero; otherwise, false.</returns>
public bool ToBoolean(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 1);
return value[startIndex] != 0;
}
/// <summary>
/// Returns a Unicode character converted from two bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A character formed by two bytes beginning at startIndex.</returns>
public char ToChar(byte[] value, int startIndex)
{
return unchecked((char)this.ToInt16(value, startIndex));
}
/// <summary>
/// Returns a double-precision floating point number converted from eight bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A double precision floating point number formed by eight bytes beginning at startIndex.</returns>
public unsafe double ToDouble(byte[] value, int startIndex)
{
long intValue = this.ToInt64(value, startIndex);
return *((double*)&intValue);
}
/// <summary>
/// Returns a single-precision floating point number converted from four bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A single precision floating point number formed by four bytes beginning at startIndex.</returns>
public unsafe float ToSingle(byte[] value, int startIndex)
{
int intValue = this.ToInt32(value, startIndex);
return *((float*)&intValue);
}
/// <summary>
/// Returns a decimal value converted from sixteen bytes
/// at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <returns>A decimal formed by sixteen bytes beginning at startIndex.</returns>
public unsafe decimal ToDecimal(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 16);
decimal result = 0m;
int* presult = (int*)&result;
presult[0] = this.ToInt32(value, startIndex);
presult[1] = this.ToInt32(value, startIndex + 4);
presult[2] = this.ToInt32(value, startIndex + 8);
presult[3] = this.ToInt32(value, startIndex + 12);
return result;
}
}
}

127
src/ImageSharp/IO/EndianBitConverter.cs

@ -1,127 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Equivalent of <see cref="BitConverter"/>, but with either endianness.
/// </summary>
internal abstract partial class EndianBitConverter
{
/// <summary>
/// The little-endian bit converter.
/// </summary>
public static readonly LittleEndianBitConverter LittleEndianConverter = new LittleEndianBitConverter();
/// <summary>
/// The big-endian bit converter.
/// </summary>
public static readonly BigEndianBitConverter BigEndianConverter = new BigEndianBitConverter();
/// <summary>
/// Gets the byte order ("endianness") in which data is converted using this class.
/// </summary>
public abstract Endianness Endianness { get; }
/// <summary>
/// Gets a value indicating whether the byte order ("endianness") in which data is converted is little endian.
/// </summary>
/// <remarks>
/// Different computer architectures store data using different byte orders. "Big-endian"
/// means the most significant byte is on the left end of a word. "Little-endian" means the
/// most significant byte is on the right end of a word.
/// </remarks>
public abstract bool IsLittleEndian { get; }
/// <summary>
/// Gets the converter.
/// </summary>
/// <param name="endianness">The endianness.</param>
/// <returns>an <see cref="EndianBitConverter"/></returns>
/// <exception cref="ArgumentException">Not a valid form of Endianness - endianness</exception>
public static EndianBitConverter GetConverter(Endianness endianness)
{
switch (endianness)
{
case Endianness.LittleEndian:
return LittleEndianConverter;
case Endianness.BigEndian:
return BigEndianConverter;
default:
throw new ArgumentException("Not a valid form of Endianness", nameof(endianness));
}
}
/// <summary>
/// Returns a String converted from the elements of a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <remarks>All the elements of value are converted.</remarks>
/// <returns>
/// A String of hexadecimal pairs separated by hyphens, where each pair
/// represents the corresponding element in value; for example, "7F-2C-4A".
/// </returns>
public static string ToString(byte[] value)
{
return BitConverter.ToString(value);
}
/// <summary>
/// Returns a String converted from the elements of a byte array starting at a specified array position.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <remarks>The elements from array position startIndex to the end of the array are converted.</remarks>
/// <returns>
/// A String of hexadecimal pairs separated by hyphens, where each pair
/// represents the corresponding element in value; for example, "7F-2C-4A".
/// </returns>
public static string ToString(byte[] value, int startIndex)
{
return BitConverter.ToString(value, startIndex);
}
/// <summary>
/// Returns a String converted from a specified number of bytes at a specified position in a byte array.
/// </summary>
/// <param name="value">An array of bytes.</param>
/// <param name="startIndex">The starting position within value.</param>
/// <param name="length">The number of bytes to convert.</param>
/// <remarks>The length elements from array position startIndex are converted.</remarks>
/// <returns>
/// A String of hexadecimal pairs separated by hyphens, where each pair
/// represents the corresponding element in value; for example, "7F-2C-4A".
/// </returns>
public static string ToString(byte[] value, int startIndex, int length)
{
return BitConverter.ToString(value, startIndex, length);
}
/// <summary>
/// Checks the given argument for validity.
/// </summary>
/// <param name="value">The byte array passed in</param>
/// <param name="startIndex">The start index passed in</param>
/// <param name="bytesRequired">The number of bytes required</param>
/// <exception cref="ArgumentNullException">value is a null reference</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value minus bytesRequired.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static void CheckByteArgument(byte[] value, int startIndex, int bytesRequired)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (startIndex < 0 || startIndex > value.Length - bytesRequired)
{
throw new ArgumentOutOfRangeException(nameof(startIndex));
}
}
}
}

81
src/ImageSharp/IO/LittleEndianBitConverter.cs

@ -1,81 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.IO
{
/// <summary>
/// Implementation of EndianBitConverter which converts to/from little-endian byte arrays.
/// </summary>
internal sealed class LittleEndianBitConverter : EndianBitConverter
{
/// <inheritdoc/>
public override Endianness Endianness
{
get { return Endianness.LittleEndian; }
}
/// <inheritdoc/>
public override bool IsLittleEndian
{
get { return true; }
}
/// <inheritdoc/>
public override void CopyBytes(short value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 2);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(int value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 4);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
public override void CopyBytes(long value, byte[] buffer, int index)
{
CheckByteArgument(buffer, index, 8);
buffer[index + 7] = (byte)(value >> 56);
buffer[index + 6] = (byte)(value >> 48);
buffer[index + 5] = (byte)(value >> 40);
buffer[index + 4] = (byte)(value >> 32);
buffer[index + 3] = (byte)(value >> 24);
buffer[index + 2] = (byte)(value >> 16);
buffer[index + 1] = (byte)(value >> 8);
buffer[index] = (byte)value;
}
/// <inheritdoc/>
public unsafe override short ToInt16(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 2);
return (short)((value[startIndex + 1] << 8) | value[startIndex]);
}
/// <inheritdoc/>
public unsafe override int ToInt32(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 4);
return (value[startIndex + 3] << 24) | (value[startIndex + 2] << 16) | (value[startIndex + 1] << 8) | value[startIndex];
}
/// <inheritdoc/>
public unsafe override long ToInt64(byte[] value, int startIndex)
{
CheckByteArgument(value, startIndex, 8);
long p1 = (value[startIndex + 7] << 24) | (value[startIndex + 6] << 16) | (value[startIndex + 5] << 8) | value[startIndex + 4];
long p2 = (value[startIndex + 3] << 24) | (value[startIndex + 2] << 16) | (value[startIndex + 1] << 8) | value[startIndex];
return (p2 & 0xFFFFFFFF) | (p1 << 32);
}
}
}

8
src/ImageSharp/Image.FromStream.cs

@ -183,15 +183,15 @@ namespace SixLabors.ImageSharp
return data.img;
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
var sb = new StringBuilder();
sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
private static T WithSeekableStream<T>(Configuration config, Stream stream, Func<Stream, T> action)

16
src/ImageSharp/Image.LoadPixelData.cs

@ -2,12 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@ -103,13 +98,7 @@ namespace SixLabors.ImageSharp
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, TPixel[] data, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
int count = width * height;
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new Image<TPixel>(config, width, height);
SpanHelper.Copy(data, image.GetPixelSpan(), count);
return image;
return LoadPixelData(config, new Span<TPixel>(data), width, height);
}
/// <summary>
@ -128,7 +117,8 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new Image<TPixel>(config, width, height);
SpanHelper.Copy(data, image.Frames.RootFrame.GetPixelSpan(), count);
data.Slice(0, count).CopyTo(image.Frames.RootFrame.GetPixelSpan());
return image;
}

24
src/ImageSharp/ImageExtensions.cs

@ -35,28 +35,28 @@ namespace SixLabors.ImageSharp
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
var sb = new StringBuilder();
sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
var sb = new StringBuilder();
sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
source.Save(filePath, encoder);
@ -97,15 +97,15 @@ namespace SixLabors.ImageSharp
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
var sb = new StringBuilder();
sb.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
source.Save(stream, encoder);

7
src/ImageSharp/ImageFrame.LoadPixelData.cs

@ -2,11 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -46,7 +42,8 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
var image = new ImageFrame<TPixel>(memoryManager, width, height);
SpanHelper.Copy(data, image.GetPixelSpan(), count);
data.Slice(0, count).CopyTo(image.GetPixelSpan());
return image;
}

36
src/ImageSharp/ImageFrame{TPixel}.cs

@ -181,7 +181,7 @@ namespace SixLabors.ImageSharp
throw new ArgumentException("ImageFrame<TPixel>.CopyTo(): target must be of the same size!", nameof(target));
}
SpanHelper.Copy(this.GetPixelSpan(), target.Span);
this.GetPixelSpan().CopyTo(target.Span);
}
/// <summary>
@ -246,27 +246,23 @@ namespace SixLabors.ImageSharp
return this.Clone() as ImageFrame<TPixel2>;
}
Func<Vector4, Vector4> scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TPixel, TPixel2>();
var target = new ImageFrame<TPixel2>(this.MemoryManager, this.Width, this.Height, this.MetaData.Clone());
using (PixelAccessor<TPixel> pixels = this.Lock())
using (PixelAccessor<TPixel2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Configuration.Default.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
var color = default(TPixel2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
// TODO: ImageFrame has no visibility of the current configuration. It should have.
ParallelFor.WithTemporaryBuffer(
0,
this.Height,
Configuration.Default,
this.Width,
(int y, IBuffer<Vector4> tempRowBuffer) =>
{
Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y);
Span<Vector4> tempRowSpan = tempRowBuffer.Span;
PixelOperations<TPixel>.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
PixelOperations<TPixel2>.Instance.PackFromScaledVector4(tempRowSpan, targetRow, targetRow.Length);
});
return target;
}

4
src/ImageSharp/ImageSharp.csproj

@ -11,7 +11,7 @@
<AssemblyName>SixLabors.ImageSharp</AssemblyName>
<PackageId>SixLabors.ImageSharp</PackageId>
<PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Png Core</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png</PackageIconUrl>
<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>
@ -35,7 +35,7 @@
<Compile Include="..\Shared\*.cs" Exclude="bin\**;obj\**;**\*.xproj;packages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.Core" Version="1.0.0-ci0005" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0005" />
<AdditionalFiles Include="..\..\stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006">
<PrivateAssets>All</PrivateAssets>

2
src/ImageSharp/Memory/BasicArrayBuffer.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Memory
public int Length { get; }
public Span<T> Span => this.Array.AsSpan().Slice(0, this.Length);
public Span<T> Span => new Span<T>(this.Array, 0, this.Length);
/// <summary>
/// Returns a reference to specified element of the buffer.

52
src/ImageSharp/Memory/SpanHelper.cs

@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Memory
{
@ -13,52 +11,6 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
internal static class SpanHelper
{
/// <summary>
/// Fetches a <see cref="Vector{T}"/> from the beginning of the span.
/// </summary>
/// <typeparam name="T">The value type</typeparam>
/// <param name="span">The span to fetch the vector from</param>
/// <returns>A <see cref="Vector{T}"/> reference to the beginning of the span</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref Vector<T> FetchVector<T>(this Span<T> span)
where T : struct
{
return ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span));
}
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(Span<T> source, Span<T> destination, int count)
where T : struct
{
DebugGuard.MustBeLessThanOrEqualTo(count, source.Length, nameof(count));
DebugGuard.MustBeLessThanOrEqualTo(count, destination.Length, nameof(count));
ref byte srcRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source));
ref byte destRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(destination));
int byteCount = Unsafe.SizeOf<T>() * count;
// TODO: Use unfixed Unsafe.CopyBlock(ref T, ref T, int) for small blocks, when it gets available!
// This is now available. Check with Anton re intent. Do we replace both ifdefs?
fixed (byte* pSrc = &srcRef)
fixed (byte* pDest = &destRef)
{
#if NETSTANDARD1_1
Unsafe.CopyBlock(pDest, pSrc, (uint)byteCount);
#else
int destLength = destination.Length * Unsafe.SizeOf<T>();
Buffer.MemoryCopy(pSrc, pDest, destLength, byteCount);
#endif
}
}
/// <summary>
/// Copy all elements of 'source' into 'destination'.
/// </summary>
@ -66,10 +18,10 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(Span<T> source, Span<T> destination)
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct
{
Copy(source, destination, Math.Min(source.Length, destination.Length));
source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination);
}
/// <summary>

25
src/ImageSharp/MetaData/ImageProperty.cs

@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.MetaData
/// the copyright information, the date, where the image was created
/// or some other information.
/// </summary>
public class ImageProperty : IEquatable<ImageProperty>
public readonly struct ImageProperty : IEquatable<ImageProperty>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class.
/// Initializes a new instance of the <see cref="ImageProperty"/> struct.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="value">The value of the property.</param>
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class
/// Initializes a new instance of the <see cref="ImageProperty"/> struct
/// by making a copy from another property.
/// </summary>
/// <param name="other">
@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public static bool operator ==(ImageProperty left, ImageProperty right)
{
if (ReferenceEquals(left, right))
{
return true;
}
return left.Equals(right);
}
@ -110,9 +105,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public override bool Equals(object obj)
{
ImageProperty other = obj as ImageProperty;
return this.Equals(other);
return obj is ImageProperty other && this.Equals(other);
}
/// <summary>
@ -155,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData
/// <param name="other">An object to compare with this object.</param>
public bool Equals(ImageProperty other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return this.Name.Equals(other.Name) && Equals(this.Value, other.Value);
}
}

50
src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs

@ -11,66 +11,66 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Unknown
/// </summary>
Unknown,
Unknown = 0,
/// <summary>
/// Byte
/// An 8-bit unsigned integer.
/// </summary>
Byte,
Byte = 1,
/// <summary>
/// Ascii
/// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
/// </summary>
Ascii,
Ascii = 2,
/// <summary>
/// Short
/// A 16-bit (2-byte) unsigned integer.
/// </summary>
Short,
Short = 3,
/// <summary>
/// Long
/// A 32-bit (4-byte) unsigned integer.
/// </summary>
Long,
Long = 4,
/// <summary>
/// Rational
/// Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
/// </summary>
Rational,
Rational = 5,
/// <summary>
/// SignedByte
/// An 8-bit signed integer.
/// </summary>
SignedByte,
SignedByte = 6,
/// <summary>
/// Undefined
/// An 8-bit byte that can take any value depending on the field definition.
/// </summary>
Undefined,
Undefined = 7,
/// <summary>
/// SignedShort
/// A 16-bit (2-byte) signed integer.
/// </summary>
SignedShort,
SignedShort = 8,
/// <summary>
/// SignedLong
/// A 32-bit (4-byte) signed integer (2's complement notation).
/// </summary>
SignedLong,
SignedLong = 9,
/// <summary>
/// SignedRational
/// Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
/// </summary>
SignedRational,
SignedRational = 10,
/// <summary>
/// SingleFloat
/// A 32-bit floating point value.
/// </summary>
SingleFloat,
SingleFloat = 11,
/// <summary>
/// DoubleFloat
/// A 64-bit floating point value.
/// </summary>
DoubleFloat
DoubleFloat = 12
}
}

60
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// The collection of EXIF values
/// </summary>
private Collection<ExifValue> values;
private List<ExifValue> values;
/// <summary>
/// The list of invalid EXIF tags
/// </summary>
private List<ExifTag> invalidTags;
private IReadOnlyList<ExifTag> invalidTags;
/// <summary>
/// The thumbnail offset position in the byte stream
@ -75,8 +75,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags = new List<ExifTag>(other.invalidTags);
if (other.values != null)
{
this.values = new Collection<ExifValue>();
foreach (ExifValue value in other.values)
this.values = new List<ExifValue>(other.Values.Count);
foreach (ExifValue value in other.Values)
{
this.values.Add(new ExifValue(value));
}
@ -92,21 +93,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Gets or sets which parts will be written when the profile is added to an image.
/// </summary>
public ExifParts Parts
{
get;
set;
}
public ExifParts Parts { get; set; }
/// <summary>
/// Gets the tags that where found but contained an invalid value.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the values of this EXIF profile.
/// </summary>
public IEnumerable<ExifValue> Values
public IReadOnlyList<ExifValue> Values
{
get
{
@ -163,6 +160,31 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null;
}
/// <summary>
/// Conditionally returns the value of the tag if it exists.
/// </summary>
/// <param name="tag">The tag of the EXIF value.</param>
/// <param name="value">The value of the tag, if found.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public bool TryGetValue(ExifTag tag, out ExifValue value)
{
foreach (ExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
value = exifValue;
return true;
}
}
value = default;
return false;
}
/// <summary>
/// Removes the value with the specified tag.
/// </summary>
@ -193,16 +215,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
public void SetValue(ExifTag tag, object value)
{
foreach (ExifValue exifValue in this.Values)
for (int i = 0; i < this.Values.Count; i++)
{
if (exifValue.Tag == tag)
if (this.values[i].Tag == tag)
{
exifValue.Value = value;
this.values[i] = this.values[i].WithValue(value);
return;
}
}
var newExifValue = ExifValue.Create(tag, value);
this.values.Add(newExifValue);
}
@ -262,12 +286,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.data == null)
{
this.values = new Collection<ExifValue>();
this.values = new List<ExifValue>();
return;
}
var reader = new ExifReader();
this.values = reader.Read(this.data);
var reader = new ExifReader(this.data);
this.values = reader.ReadValues();
this.invalidTags = new List<ExifTag>(reader.InvalidTags);
this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength;

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

@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
@ -15,38 +18,37 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
internal sealed class ExifReader
{
private readonly Collection<ExifTag> invalidTags = new Collection<ExifTag>();
private byte[] exifData;
private uint currentIndex;
private bool isLittleEndian;
private readonly List<ExifTag> invalidTags = new List<ExifTag>();
private readonly byte[] exifData;
private int position;
private Endianness endianness = Endianness.BigEndian;
private uint exifOffset;
private uint gpsOffset;
private uint startIndex;
private int startIndex;
private delegate TDataType ConverterMethod<TDataType>(byte[] data);
public ExifReader(byte[] exifData)
{
DebugGuard.NotNull(exifData, nameof(exifData));
this.exifData = exifData;
}
private delegate TDataType ConverterMethod<TDataType>(ReadOnlySpan<byte> data);
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the thumbnail length in the byte stream
/// </summary>
public uint ThumbnailLength
{
get;
private set;
}
public uint ThumbnailLength { get; private set; }
/// <summary>
/// Gets the thumbnail offset position in the byte stream
/// </summary>
public uint ThumbnailOffset
{
get;
private set;
}
public uint ThumbnailOffset { get; private set; }
/// <summary>
/// Gets the remaining length.
@ -55,81 +57,78 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.currentIndex >= this.exifData.Length)
if (this.position >= this.exifData.Length)
{
return 0;
}
return this.exifData.Length - (int)this.currentIndex;
return this.exifData.Length - this.position;
}
}
/// <summary>
/// Reads and returns the collection of EXIF values.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>
/// The <see cref="Collection{ExifValue}"/>.
/// </returns>
public Collection<ExifValue> Read(byte[] data)
public List<ExifValue> ReadValues()
{
DebugGuard.NotNull(data, nameof(data));
var result = new Collection<ExifValue>();
var values = new List<ExifValue>();
this.exifData = data;
if (this.GetString(4) == "Exif")
if (this.ReadString(4) == "Exif")
{
if (this.GetShort() != 0)
if (this.ReadUInt16() != 0)
{
return result;
return values;
}
this.startIndex = 6;
}
else
{
this.currentIndex = 0;
this.position = 0;
}
this.isLittleEndian = this.GetString(2) == "II";
if (this.ReadString(2) == "II")
{
this.endianness = Endianness.LittleEndian;
}
if (this.GetShort() != 0x002A)
if (this.ReadUInt16() != 0x002A)
{
return result;
return values;
}
uint ifdOffset = this.GetLong();
this.AddValues(result, ifdOffset);
uint ifdOffset = this.ReadUInt32();
this.AddValues(values, (int)ifdOffset);
uint thumbnailOffset = this.GetLong();
this.GetThumbnail(thumbnailOffset);
uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail((int)thumbnailOffset);
if (this.exifOffset != 0)
{
this.AddValues(result, this.exifOffset);
this.AddValues(values, (int)this.exifOffset);
}
if (this.gpsOffset != 0)
{
this.AddValues(result, this.gpsOffset);
this.AddValues(values, (int)this.gpsOffset);
}
return result;
return values;
}
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, byte[] data, ConverterMethod<TDataType> converter)
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, ReadOnlySpan<byte> data, ConverterMethod<TDataType> converter)
{
int dataTypeSize = (int)ExifValue.GetSize(dataType);
int length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
var result = new TDataType[length];
for (int i = 0; i < length; i++)
{
Array.Copy(data, i * dataTypeSize, buffer, 0, dataTypeSize);
ReadOnlySpan<byte> buffer = data.Slice(i * dataTypeSize, dataTypeSize);
result.SetValue(converter(buffer), i);
}
@ -137,14 +136,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private static byte ToByte(byte[] data)
{
return data[0];
}
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private static string ToString(byte[] data)
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
{
string result = Encoding.UTF8.GetString(data, 0, data.Length);
#if NETSTANDARD1_1
byte[] bytes = buffer.ToArray();
string result = Encoding.UTF8.GetString(bytes, 0, buffer.Length);
#else
string result;
fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
{
result = Encoding.UTF8.GetString(pointer, buffer.Length);
}
#endif
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
@ -159,15 +167,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
/// <param name="values">The values.</param>
/// <param name="index">The index.</param>
private void AddValues(Collection<ExifValue> values, uint index)
private void AddValues(List<ExifValue> values, int index)
{
this.currentIndex = this.startIndex + index;
ushort count = this.GetShort();
this.position = this.startIndex + index;
int count = this.ReadUInt16();
for (ushort i = 0; i < count; i++)
for (int i = 0; i < count; i++)
{
ExifValue value = this.CreateValue();
if (value == null)
if (!this.TryReadValue(out ExifValue value))
{
continue;
}
@ -208,9 +215,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private object ConvertValue(ExifDataType dataType, byte[] data, uint numberOfComponents)
private object ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, uint numberOfComponents)
{
if (data == null || data.Length == 0)
if (buffer == null || buffer.Length == 0)
{
return null;
}
@ -220,106 +227,116 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifDataType.Unknown:
return null;
case ExifDataType.Ascii:
return ToString(data);
return this.ConvertToString(buffer);
case ExifDataType.Byte:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
case ExifDataType.DoubleFloat:
if (numberOfComponents == 1)
{
return this.ToDouble(data);
return this.ConvertToDouble(buffer);
}
return ToArray(dataType, data, this.ToDouble);
return ToArray(dataType, buffer, this.ConvertToDouble);
case ExifDataType.Long:
if (numberOfComponents == 1)
{
return this.ToLong(data);
return this.ConvertToUInt32(buffer);
}
return ToArray(dataType, data, this.ToLong);
return ToArray(dataType, buffer, this.ConvertToUInt32);
case ExifDataType.Rational:
if (numberOfComponents == 1)
{
return this.ToRational(data);
return this.ToRational(buffer);
}
return ToArray(dataType, data, this.ToRational);
return ToArray(dataType, buffer, this.ToRational);
case ExifDataType.Short:
if (numberOfComponents == 1)
{
return this.ToShort(data);
return this.ConvertToShort(buffer);
}
return ToArray(dataType, data, this.ToShort);
return ToArray(dataType, buffer, this.ConvertToShort);
case ExifDataType.SignedByte:
if (numberOfComponents == 1)
{
return this.ToSignedByte(data);
return this.ConvertToSignedByte(buffer);
}
return ToArray(dataType, data, this.ToSignedByte);
return ToArray(dataType, buffer, this.ConvertToSignedByte);
case ExifDataType.SignedLong:
if (numberOfComponents == 1)
{
return this.ToSignedLong(data);
return this.ConvertToInt32(buffer);
}
return ToArray(dataType, data, this.ToSignedLong);
return ToArray(dataType, buffer, this.ConvertToInt32);
case ExifDataType.SignedRational:
if (numberOfComponents == 1)
{
return this.ToSignedRational(data);
return this.ToSignedRational(buffer);
}
return ToArray(dataType, data, this.ToSignedRational);
return ToArray(dataType, buffer, this.ToSignedRational);
case ExifDataType.SignedShort:
if (numberOfComponents == 1)
{
return this.ToSignedShort(data);
return this.ConvertToSignedShort(buffer);
}
return ToArray(dataType, data, this.ToSignedShort);
return ToArray(dataType, buffer, this.ConvertToSignedShort);
case ExifDataType.SingleFloat:
if (numberOfComponents == 1)
{
return this.ToSingle(data);
return this.ConvertToSingle(buffer);
}
return ToArray(dataType, data, this.ToSingle);
return ToArray(dataType, buffer, this.ConvertToSingle);
case ExifDataType.Undefined:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
default:
throw new NotSupportedException();
}
}
private ExifValue CreateValue()
private bool TryReadValue(out ExifValue exifValue)
{
// 2 | 2 | 4 | 4
// tag | type | count | value offset
if (this.RemainingLength < 12)
{
return null;
exifValue = default;
return false;
}
ExifTag tag = this.ToEnum(this.GetShort(), ExifTag.Unknown);
ExifDataType dataType = this.ToEnum(this.GetShort(), ExifDataType.Unknown);
object value;
ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
uint type = this.ReadUInt16();
if (dataType == ExifDataType.Unknown)
// Ensure that the data type is valid
if (type == 0 || type > 12)
{
return new ExifValue(tag, dataType, null, false);
exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false);
return true;
}
uint numberOfComponents = this.GetLong();
var dataType = (ExifDataType)type;
object value;
uint numberOfComponents = this.ReadUInt32();
// Issue #132: ExifDataType == Undefined is treated like a byte array.
// If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes)
@ -329,29 +346,50 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
uint size = numberOfComponents * ExifValue.GetSize(dataType);
byte[] data = this.GetBytes(4);
this.TryReadSpan(4, out ReadOnlySpan<byte> offsetBuffer);
if (size > 4)
{
uint oldIndex = this.currentIndex;
this.currentIndex = this.ToLong(data) + this.startIndex;
int oldIndex = this.position;
uint newIndex = this.ConvertToUInt32(offsetBuffer) + (uint)this.startIndex;
// Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue)
{
this.invalidTags.Add(tag);
exifValue = default;
return false;
}
this.position = (int)newIndex;
if (this.RemainingLength < size)
{
this.invalidTags.Add(tag);
this.currentIndex = oldIndex;
return null;
this.position = oldIndex;
exifValue = default;
return false;
}
value = this.ConvertValue(dataType, this.GetBytes(size), numberOfComponents);
this.currentIndex = oldIndex;
this.TryReadSpan((int)size, out ReadOnlySpan<byte> dataBuffer);
value = this.ConvertValue(dataType, dataBuffer, numberOfComponents);
this.position = oldIndex;
}
else
{
value = this.ConvertValue(dataType, data, numberOfComponents);
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
}
bool isArray = value != null && numberOfComponents > 1;
return new ExifValue(tag, dataType, value, isArray);
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1);
return true;
}
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
@ -366,51 +404,57 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return defaultValue;
}
private byte[] GetBytes(uint length)
private bool TryReadSpan(int length, out ReadOnlySpan<byte> span)
{
if (this.currentIndex + length > (uint)this.exifData.Length)
if (this.RemainingLength < length)
{
return null;
span = default;
return false;
}
byte[] data = new byte[length];
Array.Copy(this.exifData, (int)this.currentIndex, data, 0, (int)length);
this.currentIndex += length;
span = new ReadOnlySpan<byte>(this.exifData, this.position, length);
return data;
this.position += length;
return true;
}
private uint GetLong()
private uint ReadUInt32()
{
return this.ToLong(this.GetBytes(4));
// Known as Long in Exif Specification
return this.TryReadSpan(4, out ReadOnlySpan<byte> span)
? this.ConvertToUInt32(span)
: default;
}
private ushort GetShort()
private ushort ReadUInt16()
{
return this.ToShort(this.GetBytes(2));
return this.TryReadSpan(2, out ReadOnlySpan<byte> span)
? this.ConvertToShort(span)
: default;
}
private string GetString(uint length)
private string ReadString(int length)
{
byte[] data = this.GetBytes(length);
if (data == null || data.Length == 0)
if (this.TryReadSpan(length, out ReadOnlySpan<byte> span) && span.Length != 0)
{
return null;
return this.ConvertToString(span);
}
return ToString(data);
return null;
}
private void GetThumbnail(uint offset)
private void GetThumbnail(int offset)
{
var values = new Collection<ExifValue>();
var values = new List<ExifValue>();
this.AddValues(values, offset);
foreach (ExifValue value in values)
{
if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
{
this.ThumbnailOffset = (uint)value.Value + this.startIndex;
this.ThumbnailOffset = (uint)value.Value + (uint)this.startIndex;
}
else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
{
@ -419,120 +463,112 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private double ToDouble(byte[] data)
private unsafe double ConvertToDouble(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8))
if (buffer.Length < 8)
{
return default(double);
return default;
}
return BitConverter.ToDouble(data, 0);
long intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt64BigEndian(buffer)
: BinaryPrimitives.ReadInt64LittleEndian(buffer);
return *((double*)&intValue);
}
private uint ToLong(byte[] data)
private uint ConvertToUInt32(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 4))
// Known as Long in Exif Specification
if (buffer.Length < 4)
{
return default(uint);
return default;
}
return BitConverter.ToUInt32(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt32BigEndian(buffer)
: BinaryPrimitives.ReadUInt32LittleEndian(buffer);
}
private ushort ToShort(byte[] data)
private ushort ConvertToShort(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 2))
if (buffer.Length < 2)
{
return default(ushort);
return default;
}
return BitConverter.ToUInt16(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt16BigEndian(buffer)
: BinaryPrimitives.ReadUInt16LittleEndian(buffer);
}
private float ToSingle(byte[] data)
private unsafe float ConvertToSingle(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 4))
if (buffer.Length < 4)
{
return default(float);
return default;
}
return BitConverter.ToSingle(data, 0);
int intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
return *((float*)&intValue);
}
private Rational ToRational(byte[] data)
private Rational ToRational(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8, 4))
if (buffer.Length < 8)
{
return default(Rational);
return default;
}
uint numerator = BitConverter.ToUInt32(data, 0);
uint denominator = BitConverter.ToUInt32(data, 4);
uint numerator = this.ConvertToUInt32(buffer.Slice(0, 4));
uint denominator = this.ConvertToUInt32(buffer.Slice(4, 4));
return new Rational(numerator, denominator, false);
}
private sbyte ToSignedByte(byte[] data)
private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer)
{
return unchecked((sbyte)data[0]);
return unchecked((sbyte)buffer[0]);
}
private int ToSignedLong(byte[] data)
private int ConvertToInt32(ReadOnlySpan<byte> buffer) // SignedLong in Exif Specification
{
if (!this.ValidateArray(data, 4))
if (buffer.Length < 4)
{
return default(int);
return default;
}
return BitConverter.ToInt32(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
}
private SignedRational ToSignedRational(byte[] data)
private SignedRational ToSignedRational(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8, 4))
if (buffer.Length < 8)
{
return default(SignedRational);
return default;
}
int numerator = BitConverter.ToInt32(data, 0);
int denominator = BitConverter.ToInt32(data, 4);
int numerator = this.ConvertToInt32(buffer.Slice(0, 4));
int denominator = this.ConvertToInt32(buffer.Slice(4, 4));
return new SignedRational(numerator, denominator, false);
}
private short ToSignedShort(byte[] data)
private short ConvertToSignedShort(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 2))
if (buffer.Length < 2)
{
return default(short);
return default;
}
return BitConverter.ToInt16(data, 0);
}
private bool ValidateArray(byte[] data, int size)
{
return this.ValidateArray(data, size, size);
}
private bool ValidateArray(byte[] data, int size, int stepSize)
{
if (data == null || data.Length < size)
{
return false;
}
if (this.isLittleEndian == BitConverter.IsLittleEndian)
{
return true;
}
for (int i = 0; i < data.Length; i += stepSize)
{
Array.Reverse(data, i, stepSize);
}
return true;
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
}
}

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

@ -0,0 +1,281 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using static SixLabors.ImageSharp.MetaData.Profiles.Exif.ExifTag;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifTags
{
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
public static readonly ExifTag[] Ifd =
{
SubfileType,
OldSubfileType,
ImageWidth,
ImageLength,
BitsPerSample,
Compression,
PhotometricInterpretation,
Thresholding,
CellWidth,
CellLength,
FillOrder,
DocumentName,
ImageDescription,
Make,
Model,
StripOffsets,
Orientation,
SamplesPerPixel,
RowsPerStrip,
StripByteCounts,
MinSampleValue,
MaxSampleValue,
XResolution,
YResolution,
PlanarConfiguration,
PageName,
XPosition,
YPosition,
FreeOffsets,
FreeByteCounts,
GrayResponseUnit,
GrayResponseCurve,
T4Options,
T6Options,
ResolutionUnit,
PageNumber,
ColorResponseUnit,
TransferFunction,
Software,
DateTime,
Artist,
HostComputer,
Predictor,
WhitePoint,
PrimaryChromaticities,
ColorMap,
HalftoneHints,
TileWidth,
TileLength,
TileOffsets,
TileByteCounts,
BadFaxLines,
CleanFaxData,
ConsecutiveBadFaxLines,
InkSet,
InkNames,
NumberOfInks,
DotRange,
TargetPrinter,
ExtraSamples,
SampleFormat,
SMinSampleValue,
SMaxSampleValue,
TransferRange,
ClipPath,
XClipPathUnits,
YClipPathUnits,
Indexed,
JPEGTables,
OPIProxy,
ProfileType,
FaxProfile,
CodingMethods,
VersionYear,
ModeNumber,
Decode,
DefaultImageColor,
T82ptions,
JPEGProc,
JPEGInterchangeFormat,
JPEGInterchangeFormatLength,
JPEGRestartInterval,
JPEGLosslessPredictors,
JPEGPointTransforms,
JPEGQTables,
JPEGDCTables,
JPEGACTables,
YCbCrCoefficients,
YCbCrSubsampling,
YCbCrSubsampling,
YCbCrPositioning,
ReferenceBlackWhite,
StripRowCounts,
XMP,
Rating,
RatingPercent,
ImageID,
CFARepeatPatternDim,
CFAPattern2,
BatteryLevel,
Copyright,
MDFileTag,
MDScalePixel,
MDLabName,
MDSampleInfo,
MDPrepDate,
MDPrepTime,
MDFileUnits,
PixelScale,
IntergraphPacketData,
IntergraphRegisters,
IntergraphMatrix,
ModelTiePoint,
SEMInfo,
ModelTransform,
ImageLayer,
FaxRecvParams,
FaxSubaddress,
FaxRecvTime,
ImageSourceData,
XPTitle,
XPComment,
XPAuthor,
XPKeywords,
XPSubject,
GDALMetadata,
GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
public static readonly ExifTag[] Exif =
{
ExposureTime,
FNumber,
ExposureProgram,
SpectralSensitivity,
ISOSpeedRatings,
OECF,
Interlace,
TimeZoneOffset,
SelfTimerMode,
SensitivityType,
StandardOutputSensitivity,
RecommendedExposureIndex,
ISOSpeed,
ISOSpeedLatitudeyyy,
ISOSpeedLatitudezzz,
ExifVersion,
DateTimeOriginal,
DateTimeDigitized,
OffsetTime,
OffsetTimeOriginal,
OffsetTimeDigitized,
ComponentsConfiguration,
CompressedBitsPerPixel,
ShutterSpeedValue,
ApertureValue,
BrightnessValue,
ExposureBiasValue,
MaxApertureValue,
SubjectDistance,
MeteringMode,
LightSource,
Flash,
FocalLength,
FlashEnergy2,
SpatialFrequencyResponse2,
Noise,
FocalPlaneXResolution2,
FocalPlaneYResolution2,
FocalPlaneResolutionUnit2,
ImageNumber,
SecurityClassification,
ImageHistory,
SubjectArea,
ExposureIndex2,
TIFFEPStandardID,
SensingMethod2,
MakerNote,
UserComment,
SubsecTime,
SubsecTimeOriginal,
SubsecTimeDigitized,
AmbientTemperature,
Humidity,
Pressure,
WaterDepth,
Acceleration,
CameraElevationAngle,
FlashpixVersion,
ColorSpace,
PixelXDimension,
PixelYDimension,
RelatedSoundFile,
FlashEnergy,
SpatialFrequencyResponse,
FocalPlaneXResolution,
FocalPlaneYResolution,
FocalPlaneResolutionUnit,
SubjectLocation,
ExposureIndex,
SensingMethod,
FileSource,
SceneType,
CFAPattern,
CustomRendered,
ExposureMode,
WhiteBalance,
DigitalZoomRatio,
FocalLengthIn35mmFilm,
SceneCaptureType,
GainControl,
Contrast,
Saturation,
Sharpness,
DeviceSettingDescription,
SubjectDistanceRange,
ImageUniqueID,
OwnerName,
SerialNumber,
LensInfo,
LensMake,
LensModel,
LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
public static readonly ExifTag[] Gps =
{
GPSVersionID,
GPSLatitudeRef,
GPSLatitude,
GPSLongitudeRef,
GPSLongitude,
GPSAltitudeRef,
GPSAltitude,
GPSTimestamp,
GPSSatellites,
GPSStatus,
GPSMeasureMode,
GPSDOP,
GPSSpeedRef,
GPSSpeed,
GPSTrackRef,
GPSTrack,
GPSImgDirectionRef,
GPSImgDirection,
GPSMapDatum,
GPSDestLatitudeRef,
GPSDestLatitude,
GPSDestLongitudeRef,
GPSDestLongitude,
GPSDestBearingRef,
GPSDestBearing,
GPSDestDistanceRef,
GPSDestDistance,
GPSProcessingMethod,
GPSAreaInformation,
GPSDateStamp,
GPSDifferential
};
}
}

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

@ -13,11 +13,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
public sealed class ExifValue : IEquatable<ExifValue>
{
/// <summary>
/// The exif value.
/// </summary>
private object exifValue;
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class
/// by making a copy from another exif value.
@ -34,30 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (!other.IsArray)
{
this.exifValue = other.exifValue;
this.Value = other.Value;
}
else
{
var array = (Array)other.exifValue;
this.exifValue = array.Clone();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray;
if (dataType == ExifDataType.Ascii)
{
this.IsArray = false;
var array = (Array)other.Value;
this.Value = array.Clone();
}
}
@ -69,51 +46,32 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
: this(tag, dataType, isArray)
{
this.exifValue = value;
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray && dataType != ExifDataType.Ascii;
this.Value = value;
}
/// <summary>
/// Gets the data type of the exif value.
/// </summary>
public ExifDataType DataType
{
get;
}
public ExifDataType DataType { get; }
/// <summary>
/// Gets a value indicating whether the value is an array.
/// </summary>
public bool IsArray
{
get;
}
public bool IsArray { get; }
/// <summary>
/// Gets the tag of the exif value.
/// </summary>
public ExifTag Tag
{
get;
}
public ExifTag Tag { get; }
/// <summary>
/// Gets or sets the value.
/// Gets the value.
/// </summary>
public object Value
{
get
{
return this.exifValue;
}
set
{
this.CheckValue(value);
this.exifValue = value;
}
}
public object Value { get; }
/// <summary>
/// Gets a value indicating whether the EXIF value has a value.
@ -122,14 +80,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return false;
}
if (this.DataType == ExifDataType.Ascii)
{
return ((string)this.exifValue).Length > 0;
return ((string)this.Value).Length > 0;
}
return true;
@ -143,7 +101,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return 4;
}
@ -163,12 +121,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
if (this.DataType == ExifDataType.Ascii)
{
return Encoding.UTF8.GetBytes((string)this.exifValue).Length;
return Encoding.UTF8.GetBytes((string)this.Value).Length;
}
if (this.IsArray)
{
return ((Array)this.exifValue).Length;
return ((Array)this.Value).Length;
}
return 1;
@ -217,12 +175,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
return this.Equals(obj as ExifValue);
return obj is ExifValue other && this.Equals(other);
}
/// <inheritdoc />
@ -241,7 +194,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return
this.Tag == other.Tag &&
this.DataType == other.DataType &&
object.Equals(this.exifValue, other.exifValue);
object.Equals(this.Value, other.Value);
}
/// <summary>
/// Clones the current value, overwriting the value.
/// </summary>
/// <param name="value">The value to overwrite.</param>
/// <returns><see cref="ExifValue"/></returns>
public ExifValue WithValue(object value)
{
this.CheckValue(value);
return new ExifValue(this.Tag, this.DataType, value, this.IsArray);
}
/// <inheritdoc/>
@ -253,26 +218,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc/>
public override string ToString()
{
if (this.exifValue == null)
if (this.Value == null)
{
return null;
}
if (this.DataType == ExifDataType.Ascii)
{
return (string)this.exifValue;
return (string)this.Value;
}
if (!this.IsArray)
{
return this.ToString(this.exifValue);
return this.ToString(this.Value);
}
var sb = new StringBuilder();
foreach (object value in (Array)this.exifValue)
foreach (object value in (Array)this.Value)
{
sb.Append(this.ToString(value));
sb.Append(" ");
sb.Append(' ');
}
return sb.ToString();
@ -293,13 +258,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
ExifValue exifValue;
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
switch (tag)
{
case ExifTag.ImageDescription:
@ -355,8 +313,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSDestBearingRef:
case ExifTag.GPSDestDistanceRef:
case ExifTag.GPSDateStamp:
exifValue = new ExifValue(tag, ExifDataType.Ascii, true);
break;
return new ExifValue(tag, ExifDataType.Ascii, value, true);
case ExifTag.ClipPath:
case ExifTag.VersionYear:
@ -369,13 +326,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.XPKeywords:
case ExifTag.XPSubject:
case ExifTag.GPSVersionID:
exifValue = new ExifValue(tag, ExifDataType.Byte, true);
break;
return new ExifValue(tag, ExifDataType.Byte, value, true);
case ExifTag.FaxProfile:
case ExifTag.ModeNumber:
case ExifTag.GPSAltitudeRef:
exifValue = new ExifValue(tag, ExifDataType.Byte, false);
break;
return new ExifValue(tag, ExifDataType.Byte, value, false);
case ExifTag.FreeOffsets:
case ExifTag.FreeByteCounts:
@ -389,8 +345,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.StripRowCounts:
case ExifTag.IntergraphRegisters:
case ExifTag.TimeZoneOffset:
exifValue = new ExifValue(tag, ExifDataType.Long, true);
break;
return new ExifValue(tag, ExifDataType.Long, value, true);
case ExifTag.SubfileType:
case ExifTag.SubIFDOffset:
case ExifTag.GPSIFDOffset:
@ -412,8 +367,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.FaxRecvParams:
case ExifTag.FaxRecvTime:
case ExifTag.ImageNumber:
exifValue = new ExifValue(tag, ExifDataType.Long, false);
break;
return new ExifValue(tag, ExifDataType.Long, value, false);
case ExifTag.WhitePoint:
case ExifTag.PrimaryChromaticities:
@ -428,8 +382,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSTimestamp:
case ExifTag.GPSDestLatitude:
case ExifTag.GPSDestLongitude:
exifValue = new ExifValue(tag, ExifDataType.Rational, true);
break;
return new ExifValue(tag, ExifDataType.Rational, value, true);
case ExifTag.XPosition:
case ExifTag.YPosition:
case ExifTag.XResolution:
@ -463,8 +417,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSImgDirection:
case ExifTag.GPSDestBearing:
case ExifTag.GPSDestDistance:
exifValue = new ExifValue(tag, ExifDataType.Rational, false);
break;
return new ExifValue(tag, ExifDataType.Rational, value, false);
case ExifTag.BitsPerSample:
case ExifTag.MinSampleValue:
@ -487,8 +440,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ISOSpeedRatings:
case ExifTag.SubjectArea:
case ExifTag.SubjectLocation:
exifValue = new ExifValue(tag, ExifDataType.Short, true);
break;
return new ExifValue(tag, ExifDataType.Short, value, true);
case ExifTag.OldSubfileType:
case ExifTag.Compression:
case ExifTag.PhotometricInterpretation:
@ -535,20 +488,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.Sharpness:
case ExifTag.SubjectDistanceRange:
case ExifTag.GPSDifferential:
exifValue = new ExifValue(tag, ExifDataType.Short, false);
break;
return new ExifValue(tag, ExifDataType.Short, value, false);
case ExifTag.Decode:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, true);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, true);
case ExifTag.ShutterSpeedValue:
case ExifTag.BrightnessValue:
case ExifTag.ExposureBiasValue:
case ExifTag.AmbientTemperature:
case ExifTag.WaterDepth:
case ExifTag.CameraElevationAngle:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, false);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, false);
case ExifTag.JPEGTables:
case ExifTag.OECF:
@ -565,18 +516,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ImageSourceData:
case ExifTag.GPSProcessingMethod:
case ExifTag.GPSAreaInformation:
exifValue = new ExifValue(tag, ExifDataType.Undefined, true);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, true);
case ExifTag.FileSource:
case ExifTag.SceneType:
exifValue = new ExifValue(tag, ExifDataType.Undefined, false);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, false);
case ExifTag.StripOffsets:
case ExifTag.TileByteCounts:
case ExifTag.ImageLayer:
exifValue = CreateNumber(tag, type, true);
break;
return CreateNumber(tag, value, true);
case ExifTag.ImageWidth:
case ExifTag.ImageLength:
case ExifTag.TileWidth:
@ -585,15 +535,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ConsecutiveBadFaxLines:
case ExifTag.PixelXDimension:
case ExifTag.PixelYDimension:
exifValue = CreateNumber(tag, type, false);
break;
return CreateNumber(tag, value, false);
default:
throw new NotSupportedException();
}
exifValue.Value = value;
return exifValue;
}
/// <summary>
@ -635,29 +581,35 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// Returns an EXIF value with a numeric type for the given tag.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="type">The numeric type.</param>
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </returns>
private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray)
private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
{
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
if (type == null || type == typeof(ushort))
{
return new ExifValue(tag, ExifDataType.Short, isArray);
return new ExifValue(tag, ExifDataType.Short, value, isArray);
}
if (type == typeof(short))
{
return new ExifValue(tag, ExifDataType.SignedShort, isArray);
return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
}
if (type == typeof(uint))
{
return new ExifValue(tag, ExifDataType.Long, isArray);
return new ExifValue(tag, ExifDataType.Long, value, isArray);
}
return new ExifValue(tag, ExifDataType.SignedLong, isArray);
return new ExifValue(tag, ExifDataType.SignedLong, value, isArray);
}
/// <summary>
@ -739,8 +691,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns>The <see cref="string"/></returns>
private string ToString(object value)
{
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value);
if (description != null)
if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
{
return description;
}
@ -788,7 +739,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private int GetHashCode(ExifValue exif)
{
int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode();
return hashCode ^ exif.exifValue?.GetHashCode() ?? hashCode;
return hashCode ^ exif.Value?.GetHashCode() ?? hashCode;
}
}
}

410
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SixLabors.ImageSharp.Primitives;
@ -19,299 +19,28 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
private const int StartIndex = 6;
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
private static readonly ExifTag[] IfdTags =
{
ExifTag.SubfileType,
ExifTag.OldSubfileType,
ExifTag.ImageWidth,
ExifTag.ImageLength,
ExifTag.BitsPerSample,
ExifTag.Compression,
ExifTag.PhotometricInterpretation,
ExifTag.Thresholding,
ExifTag.CellWidth,
ExifTag.CellLength,
ExifTag.FillOrder,
ExifTag.DocumentName,
ExifTag.ImageDescription,
ExifTag.Make,
ExifTag.Model,
ExifTag.StripOffsets,
ExifTag.Orientation,
ExifTag.SamplesPerPixel,
ExifTag.RowsPerStrip,
ExifTag.StripByteCounts,
ExifTag.MinSampleValue,
ExifTag.MaxSampleValue,
ExifTag.XResolution,
ExifTag.YResolution,
ExifTag.PlanarConfiguration,
ExifTag.PageName,
ExifTag.XPosition,
ExifTag.YPosition,
ExifTag.FreeOffsets,
ExifTag.FreeByteCounts,
ExifTag.GrayResponseUnit,
ExifTag.GrayResponseCurve,
ExifTag.T4Options,
ExifTag.T6Options,
ExifTag.ResolutionUnit,
ExifTag.PageNumber,
ExifTag.ColorResponseUnit,
ExifTag.TransferFunction,
ExifTag.Software,
ExifTag.DateTime,
ExifTag.Artist,
ExifTag.HostComputer,
ExifTag.Predictor,
ExifTag.WhitePoint,
ExifTag.PrimaryChromaticities,
ExifTag.ColorMap,
ExifTag.HalftoneHints,
ExifTag.TileWidth,
ExifTag.TileLength,
ExifTag.TileOffsets,
ExifTag.TileByteCounts,
ExifTag.BadFaxLines,
ExifTag.CleanFaxData,
ExifTag.ConsecutiveBadFaxLines,
ExifTag.InkSet,
ExifTag.InkNames,
ExifTag.NumberOfInks,
ExifTag.DotRange,
ExifTag.TargetPrinter,
ExifTag.ExtraSamples,
ExifTag.SampleFormat,
ExifTag.SMinSampleValue,
ExifTag.SMaxSampleValue,
ExifTag.TransferRange,
ExifTag.ClipPath,
ExifTag.XClipPathUnits,
ExifTag.YClipPathUnits,
ExifTag.Indexed,
ExifTag.JPEGTables,
ExifTag.OPIProxy,
ExifTag.ProfileType,
ExifTag.FaxProfile,
ExifTag.CodingMethods,
ExifTag.VersionYear,
ExifTag.ModeNumber,
ExifTag.Decode,
ExifTag.DefaultImageColor,
ExifTag.T82ptions,
ExifTag.JPEGProc,
ExifTag.JPEGInterchangeFormat,
ExifTag.JPEGInterchangeFormatLength,
ExifTag.JPEGRestartInterval,
ExifTag.JPEGLosslessPredictors,
ExifTag.JPEGPointTransforms,
ExifTag.JPEGQTables,
ExifTag.JPEGDCTables,
ExifTag.JPEGACTables,
ExifTag.YCbCrCoefficients,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrPositioning,
ExifTag.ReferenceBlackWhite,
ExifTag.StripRowCounts,
ExifTag.XMP,
ExifTag.Rating,
ExifTag.RatingPercent,
ExifTag.ImageID,
ExifTag.CFARepeatPatternDim,
ExifTag.CFAPattern2,
ExifTag.BatteryLevel,
ExifTag.Copyright,
ExifTag.MDFileTag,
ExifTag.MDScalePixel,
ExifTag.MDLabName,
ExifTag.MDSampleInfo,
ExifTag.MDPrepDate,
ExifTag.MDPrepTime,
ExifTag.MDFileUnits,
ExifTag.PixelScale,
ExifTag.IntergraphPacketData,
ExifTag.IntergraphRegisters,
ExifTag.IntergraphMatrix,
ExifTag.ModelTiePoint,
ExifTag.SEMInfo,
ExifTag.ModelTransform,
ExifTag.ImageLayer,
ExifTag.FaxRecvParams,
ExifTag.FaxSubaddress,
ExifTag.FaxRecvTime,
ExifTag.ImageSourceData,
ExifTag.XPTitle,
ExifTag.XPComment,
ExifTag.XPAuthor,
ExifTag.XPKeywords,
ExifTag.XPSubject,
ExifTag.GDALMetadata,
ExifTag.GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
private static readonly ExifTag[] ExifTags =
{
ExifTag.ExposureTime,
ExifTag.FNumber,
ExifTag.ExposureProgram,
ExifTag.SpectralSensitivity,
ExifTag.ISOSpeedRatings,
ExifTag.OECF,
ExifTag.Interlace,
ExifTag.TimeZoneOffset,
ExifTag.SelfTimerMode,
ExifTag.SensitivityType,
ExifTag.StandardOutputSensitivity,
ExifTag.RecommendedExposureIndex,
ExifTag.ISOSpeed,
ExifTag.ISOSpeedLatitudeyyy,
ExifTag.ISOSpeedLatitudezzz,
ExifTag.ExifVersion,
ExifTag.DateTimeOriginal,
ExifTag.DateTimeDigitized,
ExifTag.OffsetTime,
ExifTag.OffsetTimeOriginal,
ExifTag.OffsetTimeDigitized,
ExifTag.ComponentsConfiguration,
ExifTag.CompressedBitsPerPixel,
ExifTag.ShutterSpeedValue,
ExifTag.ApertureValue,
ExifTag.BrightnessValue,
ExifTag.ExposureBiasValue,
ExifTag.MaxApertureValue,
ExifTag.SubjectDistance,
ExifTag.MeteringMode,
ExifTag.LightSource,
ExifTag.Flash,
ExifTag.FocalLength,
ExifTag.FlashEnergy2,
ExifTag.SpatialFrequencyResponse2,
ExifTag.Noise,
ExifTag.FocalPlaneXResolution2,
ExifTag.FocalPlaneYResolution2,
ExifTag.FocalPlaneResolutionUnit2,
ExifTag.ImageNumber,
ExifTag.SecurityClassification,
ExifTag.ImageHistory,
ExifTag.SubjectArea,
ExifTag.ExposureIndex2,
ExifTag.TIFFEPStandardID,
ExifTag.SensingMethod2,
ExifTag.MakerNote,
ExifTag.UserComment,
ExifTag.SubsecTime,
ExifTag.SubsecTimeOriginal,
ExifTag.SubsecTimeDigitized,
ExifTag.AmbientTemperature,
ExifTag.Humidity,
ExifTag.Pressure,
ExifTag.WaterDepth,
ExifTag.Acceleration,
ExifTag.CameraElevationAngle,
ExifTag.FlashpixVersion,
ExifTag.ColorSpace,
ExifTag.PixelXDimension,
ExifTag.PixelYDimension,
ExifTag.RelatedSoundFile,
ExifTag.FlashEnergy,
ExifTag.SpatialFrequencyResponse,
ExifTag.FocalPlaneXResolution,
ExifTag.FocalPlaneYResolution,
ExifTag.FocalPlaneResolutionUnit,
ExifTag.SubjectLocation,
ExifTag.ExposureIndex,
ExifTag.SensingMethod,
ExifTag.FileSource,
ExifTag.SceneType,
ExifTag.CFAPattern,
ExifTag.CustomRendered,
ExifTag.ExposureMode,
ExifTag.WhiteBalance,
ExifTag.DigitalZoomRatio,
ExifTag.FocalLengthIn35mmFilm,
ExifTag.SceneCaptureType,
ExifTag.GainControl,
ExifTag.Contrast,
ExifTag.Saturation,
ExifTag.Sharpness,
ExifTag.DeviceSettingDescription,
ExifTag.SubjectDistanceRange,
ExifTag.ImageUniqueID,
ExifTag.OwnerName,
ExifTag.SerialNumber,
ExifTag.LensInfo,
ExifTag.LensMake,
ExifTag.LensModel,
ExifTag.LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
private static readonly ExifTag[] GPSTags =
{
ExifTag.GPSVersionID,
ExifTag.GPSLatitudeRef,
ExifTag.GPSLatitude,
ExifTag.GPSLongitudeRef,
ExifTag.GPSLongitude,
ExifTag.GPSAltitudeRef,
ExifTag.GPSAltitude,
ExifTag.GPSTimestamp,
ExifTag.GPSSatellites,
ExifTag.GPSStatus,
ExifTag.GPSMeasureMode,
ExifTag.GPSDOP,
ExifTag.GPSSpeedRef,
ExifTag.GPSSpeed,
ExifTag.GPSTrackRef,
ExifTag.GPSTrack,
ExifTag.GPSImgDirectionRef,
ExifTag.GPSImgDirection,
ExifTag.GPSMapDatum,
ExifTag.GPSDestLatitudeRef,
ExifTag.GPSDestLatitude,
ExifTag.GPSDestLongitudeRef,
ExifTag.GPSDestLongitude,
ExifTag.GPSDestBearingRef,
ExifTag.GPSDestBearing,
ExifTag.GPSDestDistanceRef,
ExifTag.GPSDestDistance,
ExifTag.GPSProcessingMethod,
ExifTag.GPSAreaInformation,
ExifTag.GPSDateStamp,
ExifTag.GPSDifferential
};
/// <summary>
/// Which parts will be written.
/// </summary>
private ExifParts allowedParts;
private Collection<ExifValue> values;
private Collection<int> dataOffsets;
private Collection<int> ifdIndexes;
private Collection<int> exifIndexes;
private Collection<int> gpsIndexes;
private IList<ExifValue> values;
private List<int> dataOffsets;
private List<int> ifdIndexes;
private List<int> exifIndexes;
private List<int> gpsIndexes;
/// <summary>
/// Initializes a new instance of the <see cref="ExifWriter"/> class.
/// </summary>
/// <param name="values">The values.</param>
/// <param name="allowedParts">The allowed parts.</param>
public ExifWriter(Collection<ExifValue> values, ExifParts allowedParts)
public ExifWriter(IList<ExifValue> values, ExifParts allowedParts)
{
this.values = values;
this.allowedParts = allowedParts;
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, IfdTags);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags);
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
}
/// <summary>
@ -360,6 +89,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
length += 10 + 4 + 2;
byte[] result = new byte[length];
result[0] = (byte)'E';
result[1] = (byte)'x';
result[2] = (byte)'i';
@ -377,17 +107,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (exifLength > 0)
{
this.values[exifIndex].Value = ifdOffset + ifdLength;
this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
}
if (gpsLength > 0)
{
this.values[gpsIndex].Value = ifdOffset + ifdLength + exifLength;
this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength);
}
i = Write(BitConverter.GetBytes(ifdOffset), result, i);
i = WriteUInt32(ifdOffset, result, i);
i = this.WriteHeaders(this.ifdIndexes, result, i);
i = Write(BitConverter.GetBytes(thumbnailOffset), result, i);
i = WriteUInt32(thumbnailOffset, result, i);
i = this.WriteData(this.ifdIndexes, result, i);
if (exifLength > 0)
@ -402,19 +132,61 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
i = this.WriteData(this.gpsIndexes, result, i);
}
Write(BitConverter.GetBytes((ushort)0), result, i);
WriteUInt16((ushort)0, result, i);
return result;
}
private static int Write(byte[] source, byte[] destination, int offset)
private static unsafe int WriteSingle(float value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), *((int*)&value));
return offset + 4;
}
private static unsafe int WriteDouble(double value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), *((long*)&value));
return offset + 8;
}
private static int Write(ReadOnlySpan<byte> source, Span<byte> destination, int offset)
{
Buffer.BlockCopy(source, 0, destination, offset, source.Length);
source.CopyTo(destination.Slice(offset, source.Length));
return offset + source.Length;
}
private int GetIndex(Collection<int> indexes, ExifTag tag)
private static int WriteInt16(short value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt16(ushort value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt32(uint value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private static int WriteInt32(int value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private int GetIndex(IList<int> indexes, ExifTag tag)
{
foreach (int index in indexes)
{
@ -430,14 +202,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newIndex;
}
private Collection<int> GetIndexes(ExifParts part, ExifTag[] tags)
private List<int> GetIndexes(ExifParts part, ExifTag[] tags)
{
if (((int)this.allowedParts & (int)part) == 0)
{
return new Collection<int>();
return new List<int>();
}
Collection<int> result = new Collection<int>();
var result = new List<int>();
for (int i = 0; i < this.values.Count; i++)
{
ExifValue value = this.values[i];
@ -457,7 +229,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private uint GetLength(IEnumerable<int> indexes)
private uint GetLength(IList<int> indexes)
{
uint length = 0;
@ -494,7 +266,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteData(Collection<int> indexes, byte[] destination, int offset)
private int WriteData(List<int> indexes, byte[] destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@ -509,7 +281,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
ExifValue value = this.values[index];
if (value.Length > 4)
{
Write(BitConverter.GetBytes(newOffset - StartIndex), destination, this.dataOffsets[i++]);
WriteUInt32((uint)(newOffset - StartIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset);
}
}
@ -517,11 +289,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteHeaders(Collection<int> indexes, byte[] destination, int offset)
private int WriteHeaders(List<int> indexes, byte[] destination, int offset)
{
this.dataOffsets = new Collection<int>();
this.dataOffsets = new List<int>();
int newOffset = Write(BitConverter.GetBytes((ushort)indexes.Count), destination, offset);
int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset);
if (indexes.Count == 0)
{
@ -531,9 +303,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
foreach (int index in indexes)
{
ExifValue value = this.values[index];
newOffset = Write(BitConverter.GetBytes((ushort)value.Tag), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((ushort)value.DataType), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((uint)value.NumberOfComponents), destination, newOffset);
newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
if (value.Length > 4)
{
@ -550,23 +322,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteRational(Rational value, byte[] destination, int offset)
private static void WriteRational(Span<byte> destination, in Rational value)
{
Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
private int WriteSignedRational(SignedRational value, byte[] destination, int offset)
private static void WriteSignedRational(Span<byte> destination, in SignedRational value)
{
Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
private int WriteValue(ExifDataType dataType, object value, byte[] destination, int offset)
private int WriteValue(ExifDataType dataType, object value, Span<byte> destination, int offset)
{
switch (dataType)
{
@ -577,24 +345,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
destination[offset] = (byte)value;
return offset + 1;
case ExifDataType.DoubleFloat:
return Write(BitConverter.GetBytes((double)value), destination, offset);
return WriteDouble((double)value, destination, offset);
case ExifDataType.Short:
return Write(BitConverter.GetBytes((ushort)value), destination, offset);
return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long:
return Write(BitConverter.GetBytes((uint)value), destination, offset);
return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational:
return this.WriteRational((Rational)value, destination, offset);
WriteRational(destination.Slice(offset, 8), (Rational)value);
return offset + 8;
case ExifDataType.SignedByte:
destination[offset] = unchecked((byte)((sbyte)value));
return offset + 1;
case ExifDataType.SignedLong:
return Write(BitConverter.GetBytes((int)value), destination, offset);
return WriteInt32((int)value, destination, offset);
case ExifDataType.SignedShort:
return Write(BitConverter.GetBytes((short)value), destination, offset);
return WriteInt16((short)value, destination, offset);
case ExifDataType.SignedRational:
return this.WriteSignedRational((SignedRational)value, destination, offset);
WriteSignedRational(destination.Slice(offset, 8), (SignedRational)value);
return offset + 8;
case ExifDataType.SingleFloat:
return Write(BitConverter.GetBytes((float)value), destination, offset);
return WriteSingle((float)value, destination, offset);
default:
throw new NotImplementedException();
}

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

@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true;
}
return obj is IccParametricCurve && this.Equals((IccParametricCurve)obj);
return obj is IccParametricCurve other && this.Equals(other);
}
/// <inheritdoc/>

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

@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true;
}
return obj is IccResponseCurve && this.Equals((IccResponseCurve)obj);
return obj is IccResponseCurve other && this.Equals(other);
}
/// <inheritdoc />

25
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Text;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public ushort ReadUInt16()
{
return this.converter.ToUInt16(this.data, this.AddIndex(2));
return BinaryPrimitives.ReadUInt16BigEndian(new Span<byte>(this.data, this.AddIndex(2), 2));
}
/// <summary>
@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public short ReadInt16()
{
return this.converter.ToInt16(this.data, this.AddIndex(2));
return BinaryPrimitives.ReadInt16BigEndian(new Span<byte>(this.data, this.AddIndex(2), 2));
}
/// <summary>
@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public uint ReadUInt32()
{
return this.converter.ToUInt32(this.data, this.AddIndex(4));
return BinaryPrimitives.ReadUInt32BigEndian(new Span<byte>(this.data, this.AddIndex(4), 4));
}
/// <summary>
@ -44,7 +45,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public int ReadInt32()
{
return this.converter.ToInt32(this.data, this.AddIndex(4));
return BinaryPrimitives.ReadInt32BigEndian(new Span<byte>(this.data, this.AddIndex(4), 4));
}
/// <summary>
@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public ulong ReadUInt64()
{
return this.converter.ToUInt64(this.data, this.AddIndex(8));
return BinaryPrimitives.ReadUInt64BigEndian(new Span<byte>(this.data, this.AddIndex(8), 8));
}
/// <summary>
@ -62,25 +63,29 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <returns>the value</returns>
public long ReadInt64()
{
return this.converter.ToInt64(this.data, this.AddIndex(8));
return BinaryPrimitives.ReadInt64BigEndian(new Span<byte>(this.data, this.AddIndex(8), 8));
}
/// <summary>
/// Reads a float
/// </summary>
/// <returns>the value</returns>
public float ReadSingle()
public unsafe float ReadSingle()
{
return this.converter.ToSingle(this.data, this.AddIndex(4));
int intValue = this.ReadInt32();
return *((float*)&intValue);
}
/// <summary>
/// Reads a double
/// </summary>
/// <returns>the value</returns>
public double ReadDouble()
public unsafe double ReadDouble()
{
return this.converter.ToDouble(this.data, this.AddIndex(8));
long intValue = this.ReadInt64();
return *((double*)&intValue);
}
/// <summary>

6
src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs

@ -3,7 +3,6 @@
using System;
using System.Text;
using SixLabors.ImageSharp.IO;
namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
{
@ -20,11 +19,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// </summary>
private readonly byte[] data;
/// <summary>
/// The bit converter
/// </summary>
private readonly EndianBitConverter converter = new BigEndianBitConverter();
/// <summary>
/// The current reading position
/// </summary>

2
src/ImageSharp/PixelAccessor{TPixel}.cs

@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp
/// <param name="target">The target pixel buffer accessor.</param>
internal void CopyTo(PixelAccessor<TPixel> target)
{
SpanHelper.Copy(this.PixelBuffer.Span, target.PixelBuffer.Span);
this.PixelBuffer.Span.CopyTo(target.PixelBuffer.Span);
}
/// <summary>

18
src/ImageSharp/PixelFormats/Alpha8.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing a single 8 bit normalized W values.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;0, 0, 0, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form.
/// </para>
/// </summary>
public struct Alpha8 : IPixel<Alpha8>, IPackedVector<byte>
@ -62,6 +62,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Alpha8> CreatePixelOperations() => new PixelOperations<Alpha8>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -124,7 +138,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Alpha8) && this.Equals((Alpha8)obj);
return obj is Alpha8 other && this.Equals(other);
}
/// <summary>

20
src/ImageSharp/PixelFormats/Argb32.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in alpha, red, green, and blue order.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
/// <remarks>
@ -237,6 +237,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Argb32> CreatePixelOperations() => new PixelOperations<Argb32>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
@ -321,7 +335,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(float x, float y, float z, float w)
{
Vector4 value = new Vector4(x, y, z, w);
var value = new Vector4(x, y, z, w);
return Pack(ref value);
}
@ -347,7 +361,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(ref Vector3 vector)
{
Vector4 value = new Vector4(vector, 1);
var value = new Vector4(vector, 1);
return Pack(ref value);
}

20
src/ImageSharp/PixelFormats/Bgr24.cs

@ -1,7 +1,6 @@
// 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;
@ -11,6 +10,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red order.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgr24 : IPixel<Bgr24>
@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj);
return obj is Bgr24 other && this.Equals(other);
}
/// <inheritdoc/>
@ -80,6 +82,20 @@ namespace SixLabors.ImageSharp.PixelFormats
this = source.Bgr;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

16
src/ImageSharp/PixelFormats/Bgr565.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits.
/// <para>
/// Ranges from &lt;0, 0, 0, 1&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Bgr565 : IPixel<Bgr565>, IPackedVector<ushort>
@ -85,6 +85,20 @@ namespace SixLabors.ImageSharp.PixelFormats
(this.PackedValue & 0x1F) * (1F / 31F));
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

19
src/ImageSharp/PixelFormats/Bgra32.cs

@ -11,6 +11,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red, and alpha order.
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
@ -101,7 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj?.GetType() == typeof(Bgra32) && this.Equals((Bgra32)obj);
public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/>
public override int GetHashCode()
@ -116,6 +119,20 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

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

Loading…
Cancel
Save